518 lines
14 KiB
C++
Executable File
518 lines
14 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Interface-Implementation of ObjectIndicator
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
// Own
|
|
#include "object_indicator_impl.h"
|
|
#include <MapImpl/map_impl.h>
|
|
#include <Map/bounding_box.h>
|
|
#include <Map/object_factory.h>
|
|
#include <MapImpl/abstract_map_object_impl.h>
|
|
#include <ElevationMap/elevation_map_manager.h>
|
|
#include <MathUtils/simple_helper.h>
|
|
|
|
|
|
// ESRI
|
|
#include <arcgis/MapGraphicsView.h>
|
|
#include <arcgis/SceneGraphicsView.h>
|
|
|
|
|
|
// Qt
|
|
#include <QGraphicsView>
|
|
|
|
|
|
#include <cassert>
|
|
|
|
namespace SituationMap
|
|
{
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// class ObjectIndicatorImpl
|
|
//-----------------------------------------------------------------------------
|
|
ObjectIndicatorImpl::ObjectIndicatorImpl()
|
|
: BaseClass()
|
|
, m_logger(getLogger("ObjectIndicatorImpl"))
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
m_markerSize = 15; // size of filled circle at the origin (indicating the center of the map)
|
|
m_lineWidth = 3; // lineWidth of (dashed) line from origin to target
|
|
|
|
// updateTimer:
|
|
m_updateTimer = new QTimer(this);
|
|
m_updateTimer->setSingleShot(true);
|
|
m_updateTimer->setInterval(100);
|
|
connect(m_updateTimer, &QTimer::timeout, this, &ObjectIndicatorImpl::updateImpl);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
bool ObjectIndicatorImpl::init(MapImpl *mapImpl)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
m_mapImpl = mapImpl;
|
|
|
|
if (!m_mapImpl)
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " m_map_impl is nullptr, init failed";
|
|
return false;
|
|
}
|
|
|
|
// to ensure graceful shutdown: (remove QGraphicsItems BEFORE qGraphicsScene/qGraphicsView is destroyed:
|
|
connect(m_mapImpl, &Map::beginDestroy, this, &ObjectIndicatorImpl::onMapDestroyed, Qt::UniqueConnection);
|
|
|
|
m_qGraphicsView = static_cast<QGraphicsView *>(m_mapImpl->getEsriQGraphicsView());
|
|
if (m_qGraphicsView)
|
|
m_qGraphicsScene = m_qGraphicsView->scene();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
ObjectIndicatorImpl::~ObjectIndicatorImpl()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
remove();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
UniqueMapId ObjectIndicatorImpl::mapObjectToPointTo()
|
|
{
|
|
return m_uniqueIdToPointTo;
|
|
}
|
|
|
|
void ObjectIndicatorImpl::setMapObjectToPointTo(const UniqueMapId &newUniqueId, bool setSelected, int lifetimeMsec)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, newUniqueId = " << newUniqueId
|
|
<< ", (old m_uniqueIdToPointTo = " << m_uniqueIdToPointTo
|
|
<< ", lifetimeMsec=" << lifetimeMsec;
|
|
|
|
m_targetGeo2D = QPointF();
|
|
|
|
connect(m_mapImpl, &MapImpl::extentChangedDelayed,
|
|
this, &ObjectIndicatorImpl::update, Qt::UniqueConnection);
|
|
|
|
if (m_uniqueIdToPointTo != newUniqueId)
|
|
{
|
|
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove);
|
|
m_uniqueIdToPointTo = newUniqueId;
|
|
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Add);
|
|
}
|
|
|
|
update();
|
|
|
|
if (m_lifetimeTimer)
|
|
m_lifetimeTimer->stop();
|
|
|
|
if (lifetimeMsec > 1)
|
|
{
|
|
if (!m_lifetimeTimer)
|
|
{
|
|
m_lifetimeTimer = new QTimer(this);
|
|
m_lifetimeTimer->setSingleShot(true);
|
|
connect(m_lifetimeTimer.data(), &QTimer::timeout, this, &ObjectIndicatorImpl::remove); // lifetime expired
|
|
}
|
|
m_lifetimeTimer->setInterval(lifetimeMsec);
|
|
m_lifetimeTimer->start();
|
|
}
|
|
|
|
m_setSelected = setSelected;
|
|
if (m_setSelected)
|
|
setMapObjectSelected(m_setSelected);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, newUniqueId = " << newUniqueId;
|
|
}
|
|
|
|
void ObjectIndicatorImpl::setPointToPointTo(const QPointF &targetGeo, int lifetimeMsec)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, targetGeo = " << targetGeo
|
|
<< ", lifetimeMsec=" << lifetimeMsec;
|
|
|
|
connect(m_mapImpl, &MapImpl::extentChangedDelayed,
|
|
this, &ObjectIndicatorImpl::update, Qt::UniqueConnection);
|
|
|
|
clearCurrentSelection();
|
|
|
|
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove);
|
|
m_uniqueIdToPointTo = INVALID_MAP_ID;
|
|
m_targetGeo2D = targetGeo;
|
|
|
|
update();
|
|
|
|
if (m_lifetimeTimer)
|
|
m_lifetimeTimer->stop();
|
|
|
|
if (lifetimeMsec > 1)
|
|
{
|
|
if (!m_lifetimeTimer)
|
|
{
|
|
m_lifetimeTimer = new QTimer(this);
|
|
m_lifetimeTimer->setSingleShot(true);
|
|
connect(m_lifetimeTimer.data(), &QTimer::timeout, this, &ObjectIndicatorImpl::remove); // lifetime expired
|
|
}
|
|
m_lifetimeTimer->setInterval(lifetimeMsec);
|
|
m_lifetimeTimer->start();
|
|
}
|
|
}
|
|
|
|
void ObjectIndicatorImpl::clearCurrentSelection()
|
|
{
|
|
ObjectContainer &objCont = m_mapImpl->objectContainer();
|
|
const QList<ObjectContainer::Object*> &allObj = objCont.getAllObjects();
|
|
|
|
for (ObjectContainer::Object* mapObj : allObj)
|
|
{
|
|
if (!mapObj)
|
|
continue;
|
|
if (!mapObj->selected())
|
|
continue;
|
|
mapObj->setSelected(false);
|
|
mapObj->updateView();
|
|
}
|
|
}
|
|
|
|
bool ObjectIndicatorImpl::update()
|
|
{
|
|
if (!m_updateTimer)
|
|
return false;
|
|
m_updateTimer->start(); // start is restart
|
|
return true;
|
|
}
|
|
|
|
bool ObjectIndicatorImpl::updateImpl()
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< ", m_uniqueIdToPointTo = " << m_uniqueIdToPointTo;
|
|
|
|
if (!m_qGraphicsView)
|
|
return false;
|
|
if (!m_qGraphicsScene)
|
|
return false;
|
|
|
|
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo) && m_targetGeo2D.isNull())
|
|
return false;
|
|
|
|
|
|
//
|
|
// Line:
|
|
//
|
|
Point3D targetGeo;
|
|
QColor color = m_defaultColor;
|
|
|
|
|
|
// center:
|
|
bool targetIsValid = false;
|
|
if (!m_targetGeo2D.isNull())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " target: m_targetGeo2D= " << m_targetGeo2D;
|
|
targetGeo = Point3D::fromQPointF(m_targetGeo2D);
|
|
targetIsValid = true;
|
|
} else {
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " target: m_uniqueIdToPointTo= " << m_uniqueIdToPointTo;
|
|
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo))
|
|
return false;
|
|
targetIsValid = centerOfTarget(targetGeo, color);
|
|
}
|
|
|
|
|
|
QRectF rect = m_qGraphicsScene->sceneRect();
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " rect: " << rect;
|
|
|
|
QBrush brush(color, Qt::SolidPattern);
|
|
QPen pen(brush, m_lineWidth, Qt::DashLine);
|
|
|
|
QPointF targetSceneCoordinate;
|
|
if (targetIsValid)
|
|
{
|
|
if (m_mapImpl->getElevationMapManager())
|
|
{
|
|
const double ele = m_mapImpl->getElevationMapManager()->elevation(targetGeo.x(), targetGeo.y());
|
|
targetGeo.rz() += ele; // targetGeo should contain an absolute z-value
|
|
}
|
|
QPointF targetWindowCoord = m_mapImpl->geoCoordinateToViewPortWindowPoint3D(targetGeo);
|
|
targetSceneCoordinate = m_qGraphicsView->mapToScene(targetWindowCoord.toPoint());
|
|
if (targetSceneCoordinate.isNull())
|
|
{
|
|
INFO_LOG(m_logger) << LOGFUNCTION << " targetSceneCoordinate invisible!!!?";
|
|
targetSceneCoordinate = rect.center();
|
|
}
|
|
|
|
QLineF lineData(rect.center(), targetSceneCoordinate);
|
|
if (!m_qGraphicsLine)
|
|
m_qGraphicsLine = m_qGraphicsScene->addLine(lineData, pen);
|
|
m_qGraphicsLine->setLine(lineData);
|
|
m_qGraphicsLine->setPen(pen);
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION
|
|
<< ", rect.center() " << rect.center()
|
|
<< ", bCenterOfTarget " << targetIsValid
|
|
<< ", targetGeo:" << targetGeo
|
|
<< ", targetSceneCoordinate " << targetSceneCoordinate;
|
|
}
|
|
else
|
|
{
|
|
delete m_qGraphicsLine;
|
|
m_qGraphicsLine = nullptr;
|
|
targetSceneCoordinate = rect.center();
|
|
}
|
|
|
|
//
|
|
// ArrowTip:
|
|
//
|
|
m_arrowTip = QPixmap::fromImage(renderArrowHeadSymbol(color, 30, 10));
|
|
|
|
if (!m_qGraphicsArrowTip)
|
|
{
|
|
m_qGraphicsArrowTip = m_qGraphicsScene->addPixmap(m_arrowTip);
|
|
m_qGraphicsArrowTip->setOffset(m_arrowTip.width() / -2.0, 0);
|
|
}
|
|
m_qGraphicsArrowTip->setPixmap(m_arrowTip);
|
|
m_qGraphicsArrowTip->setPos(targetSceneCoordinate);
|
|
|
|
const double lineAngle = MathUtils::getAngleDegreeNorth(targetSceneCoordinate, rect.center());
|
|
|
|
const double angle = 360.0 - (lineAngle);
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << ", lineAngle " << lineAngle
|
|
<< ", angle " << angle;
|
|
|
|
// Sets the clockwise rotation angle, in degrees, around the Z axis.
|
|
m_qGraphicsArrowTip->setRotation(angle);
|
|
|
|
//
|
|
// centerMarker:
|
|
//
|
|
rect = QRect(rect.center().x() - m_markerSize * 0.5, rect.center().y() - m_markerSize * 0.5,
|
|
m_markerSize, m_markerSize);
|
|
|
|
if (!m_qGraphicsCenterMarker)
|
|
m_qGraphicsCenterMarker = m_qGraphicsScene->addEllipse(rect, pen, brush);
|
|
m_qGraphicsCenterMarker->setRect(rect);
|
|
m_qGraphicsCenterMarker->setPen(QPen(color, Qt::DashLine));
|
|
m_qGraphicsCenterMarker->setBrush(brush);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
void ObjectIndicatorImpl::onMapDestroyed()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
remove();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
AbstractMapObjectImpl *ObjectIndicatorImpl::getObject(const UniqueMapId &uniqueId)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId = " << uniqueId;
|
|
|
|
if (!SituationMap::isUniqueMapIdValid(uniqueId))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId invalid: " << uniqueId;
|
|
return nullptr;
|
|
}
|
|
|
|
if (!m_mapImpl->objectFactory().contains(uniqueId))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo not found: " << m_uniqueIdToPointTo;
|
|
return nullptr;
|
|
}
|
|
|
|
AbstractMapObjectImpl *impl = dynamic_cast<AbstractMapObjectImpl *>(m_mapImpl->objectFactory().idToMapObject(uniqueId));
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId = " << uniqueId;
|
|
return impl;
|
|
}
|
|
|
|
bool ObjectIndicatorImpl::addRemoveCallback(const UniqueMapId &uniqueId, AddRemove addRemove)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId = " << uniqueId
|
|
<< " addRemove = " << (int)addRemove;
|
|
|
|
if (!SituationMap::isUniqueMapIdValid(uniqueId))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId invalid: " << uniqueId;
|
|
return false;
|
|
}
|
|
|
|
AbstractMapObjectImpl *impl = getObject(uniqueId);
|
|
|
|
if (!impl)
|
|
return false;
|
|
|
|
const QString &objToPointToDebugInfo = impl->debugInfo();
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId is valid, uniqueId=" << uniqueId
|
|
<< ", (objToPointToDebugInfo=" << objToPointToDebugInfo << ")";
|
|
|
|
if (addRemove == AddRemove::Add)
|
|
impl->addChangedCallback(this);
|
|
else
|
|
impl->removeChangedCallback(this);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId = " << uniqueId
|
|
<< ", addRemove = " << (int)addRemove;
|
|
return true;
|
|
}
|
|
|
|
void ObjectIndicatorImpl::remove()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, m_uniqueIdToPointTo=" << m_uniqueIdToPointTo;
|
|
disconnect(this);
|
|
|
|
if (m_updateTimer)
|
|
m_updateTimer->stop();
|
|
|
|
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove);
|
|
|
|
// if we set the object selected, remove selection
|
|
if (m_setSelected)
|
|
setMapObjectSelected(false);
|
|
|
|
m_uniqueIdToPointTo = INVALID_MAP_ID;
|
|
m_targetGeo2D = QPointF();
|
|
delete m_qGraphicsCenterMarker;
|
|
delete m_qGraphicsLine;
|
|
delete m_qGraphicsArrowTip;
|
|
|
|
m_qGraphicsCenterMarker = nullptr;
|
|
m_qGraphicsLine = nullptr;
|
|
m_qGraphicsArrowTip = nullptr;
|
|
|
|
updateImpl();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_uniqueIdToPointTo=" << m_uniqueIdToPointTo;
|
|
}
|
|
|
|
void ObjectIndicatorImpl::onOriginPositionChanged()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " update...";
|
|
update();
|
|
}
|
|
|
|
bool ObjectIndicatorImpl::centerOfTarget(Point3D &objCenterPoint, QColor &objColor)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
//
|
|
// point to desired obj:
|
|
//
|
|
RefPointer<AbstractMapObject> objToPointTo;
|
|
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo invalid: " << m_uniqueIdToPointTo;
|
|
return false;
|
|
}
|
|
|
|
if (!m_mapImpl->objectFactory().contains(m_uniqueIdToPointTo))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo not found: " << m_uniqueIdToPointTo;
|
|
return false;
|
|
}
|
|
|
|
objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo);
|
|
|
|
// retrive values from objToPointTo:
|
|
|
|
if (!objToPointTo.valid())
|
|
{
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found";
|
|
return false;
|
|
}
|
|
|
|
AbstractMapObjectImpl *impl = dynamic_cast<AbstractMapObjectImpl *>(objToPointTo.get());
|
|
if (!impl)
|
|
{
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " dynamic_cast failed";
|
|
return false;
|
|
}
|
|
|
|
// get target position:
|
|
BoundingBox bbox;
|
|
objToPointTo->boundingBox(bbox);
|
|
if (!bbox.isValid())
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox = " << bbox;
|
|
|
|
const QString &objToPointToDebugInfo = objToPointTo->debugInfo();
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION
|
|
<< ", objToPointTo is valid, uniqueIdToPointTo = " << m_uniqueIdToPointTo
|
|
<< ", (objToPointToDebugInfo = " << objToPointToDebugInfo << ")"
|
|
<< ", bbox= " << bbox;
|
|
|
|
|
|
objCenterPoint = bbox.center();
|
|
|
|
// objColor:
|
|
objColor = impl->majorColor();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
QImage ObjectIndicatorImpl::renderArrowHeadSymbol(const QColor &color, int length, int halfWidth)
|
|
{
|
|
int width = halfWidth * 2;
|
|
QImage image(width, length, QImage::Format_ARGB32);
|
|
image.fill(Qt::transparent);
|
|
|
|
QPainter painter(&image);
|
|
painter.setRenderHints(QPainter::Antialiasing);
|
|
|
|
QPen pen(color);
|
|
pen.setWidth(2);
|
|
painter.setPen(pen);
|
|
|
|
painter.setBrush(QBrush(color, Qt::BrushStyle::SolidPattern));
|
|
|
|
// 2 ||
|
|
// | |
|
|
// 1 | | 3
|
|
|
|
QPolygon poly;
|
|
poly.reserve(3);
|
|
poly.append(QPoint(0, length));
|
|
poly.append(QPoint(halfWidth, 0));
|
|
poly.append(QPoint(width, length));
|
|
|
|
painter.drawPolygon(poly);
|
|
|
|
painter.end();
|
|
|
|
// image.save(QLatin1String("/tmp/Arrow.png"));
|
|
return image;
|
|
}
|
|
|
|
bool ObjectIndicatorImpl::setMapObjectSelected(bool selected)
|
|
{
|
|
// 'clear' current selection:
|
|
clearCurrentSelection();
|
|
|
|
if (!selected)
|
|
return true; // already done by clearCurrentSelection :-)
|
|
|
|
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo))
|
|
return false;
|
|
|
|
RefPointer<AbstractMapObject> objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo);
|
|
// retrive values from objToPointTo:
|
|
if (!objToPointTo.valid())
|
|
{
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found";
|
|
return false;
|
|
}
|
|
AbstractMapObjectImpl *impl = dynamic_cast<AbstractMapObjectImpl *>(objToPointTo.get());
|
|
if (!impl)
|
|
{
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " dynamic_cast failed";
|
|
return false;
|
|
}
|
|
|
|
if (impl->selected() == selected)
|
|
return true;
|
|
impl->setSelected(selected);
|
|
return impl->updateView();
|
|
}
|
|
|
|
} // namespace SituationMap
|