Files
2022-11-26 17:02:01 +08:00

657 lines
20 KiB
C++

/**
******************************************************************************
*
* @file uavitem.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @brief A graphicsItem representing a UAV
* @see The GNU Public License (GPL) Version 3
* @defgroup OPMapWidget
* @{
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pureprojection.h"
#include "uavitem.h"
#include <math.h>
namespace mapcontrol {
double UAVItem::groundspeed_mps_filt = 0;
UAVItem::UAVItem(MapGraphicItem *map, OPMapWidget *parent, QString uavPic ,uint8_t sysid,uint8_t compid) : map(map), mapwidget(parent), altitude(0), showtrail(true), showtrailline(true),
trailtime(100), traildistance(5), autosetreached(true), autosetdistance(100), showUAVInfo(false),sysid(sysid),compid(compid)
{
this->setFlag(QGraphicsItem::ItemIsMovable, false);
this->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
this->setFlag(QGraphicsItem::ItemIsSelectable, true);
pic.load(":/uavs/images/aircraft_jet_norm.svg");
//this->setFlag(QGraphicsItem::ItemIsMovable, false);
//this->setFlag(QGraphicsItem::ItemIsSelectable, false);
//this->setFlag(QGraphicsItem::ItemIsMovable, false);
//this->setFlag(QGraphicsItem::ItemIsSelectable, true);
localposition = map->FromLatLngToLocal(mapwidget->CurrentPosition());
this->setPos(localposition.X(), localposition.Y());
this->setZValue(7);
trail = new QGraphicsItemGroup(this);
trail->setZValue(6);
trail->setParentItem(map);
trailLine = new QGraphicsItemGroup(this);
trailLine->setZValue(6);
trailLine->setParentItem(map);
//this->setFlag(QGraphicsItem::ItemIgnoresTransformations, false);
//this->setFlag(QGraphicsItem::ItemIsMovable, false);
//this->setFlag(QGraphicsItem::ItemIsSelectable, true);
setCacheMode(QGraphicsItem::ItemCoordinateCache);
mapfollowtype = UAVMapFollowType::None;
trailtype = UAVTrailType::ByDistance;
timer.start();
generateArrowhead();
double pixels2meters = map->Projection()->GetGroundResolution(map->ZoomTotal(), coord.Lat());
meters2pixels = 1.0 / pixels2meters;
setCacheMode(QGraphicsItem::DeviceCoordinateCache);
connect(map, SIGNAL(childRefreshPosition()), this, SLOT(RefreshPos()));
connect(map, SIGNAL(childSetOpacity(qreal)), this, SLOT(setOpacitySlot(qreal)));
connect(map, SIGNAL(zoomChanged(double, double, double)), this, SLOT(zoomChangedSlot()));
}
UAVItem::~UAVItem()
{
}
void UAVItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
QPen pen;
QFont font;
painter->save();
//设置图层
if(isEdit)
{
this->setZValue(1);
}
else
{
if (isSelected)
{
this->setZValue(7);
}
else
{
this->setZValue(8);
}
}
if (isSelected)
{
pic.load(":/uavs/images/aircraft_jet_selete.svg");
}
else
{
pic.load(":/uavs/images/aircraft_jet_norm.svg");
}
// Draw plane
painter->drawPixmap(-pic.width() / 2, -pic.height() / 2, pic);
// Turn on anti-aliasing so the fonts don't look terrible
painter->setRenderHint(QPainter::Antialiasing, true);
painter->restore();
painter->save();
// Rotate the text back to vertical
qreal rot = this->rotation();
painter->rotate(-1 * rot);
//设置每个飞机的ID号
pen.setWidth(10);
pen.setColor(Qt::red);
painter->setPen(pen);
font.setPointSize(10);
font.setWeight(QFont::Bold);
font.setFamily("Arial");//非衬线
painter->setFont(font);
painter->drawText(-50,-50,100,100,Qt::AlignCenter,QString::number(sysid));
painter->restore();
if(isShowTip)
{
painter->save();
qreal rot = this->rotation();
painter->rotate(-1 * rot);
QRectF rect = QRectF(-50,-80,100,50);
//画一块底色#58ACFA
painter->setBrush(QColor("#BEF781"));
//painter->setBrush(Qt::NoBrush);
painter->setOpacity(TipOpacity);
pen.setColor(QColor("#58ACFA"));
pen.setWidth(2);
painter->setPen(pen);
painter->drawRoundedRect(rect,5,5);
//画字
QFont font;
font.setWeight(QFont::ExtraLight);
font.setFamily("Arial");//非衬线
font.setPixelSize(12);
painter->setFont(font);
pen.setColor(QColor("#000000"));
painter->setPen(pen);
QString _h;
QString _v;
if(Vc < 1)
{
_v = tr("Speed: %1m/s\nMa:%2").arg(QString::number(Vc,'f',3)).arg(QString::number(Ma,'f',3));
}
else if(Vc < 10)
{
_v = tr("Speed: %1m/s\nMa:%2").arg(QString::number(Vc,'f',1)).arg(QString::number(Ma,'f',3));
}
else
{
_v = tr("Speed: %1m/s\nMa:%2").arg(QString::number(Vc,'f',0)).arg(QString::number(Ma,'f',3));
}
_h.append(tr("Height: %1m\n%2").arg(QString::number(altitude,'f',0)).arg(_v));
painter->drawText(QRectF(rect.x() + 2,rect.y()+2,rect.width() - 4,rect.height() - 4),Qt::AlignLeft | Qt::AlignVCenter,_h);
painter->restore();
}
/*
painter->save();
painter->drawRect(boundingRect());
painter->restore();
*/
}
void UAVItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
}
}
void UAVItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
qDebug() << "UAV mousePressEvent";
//this->setSelected(true);
update();
}
//QGraphicsItem::mousePressEvent(event);
}
void UAVItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
//查找所有
foreach(QGraphicsItem * i, map->childItems()) {
UAVItem *uav = qgraphicsitem_cast<UAVItem *>(i);
if (uav) {
if(uav != this)
{
uav->setSelect(false);
}
}
}
qDebug() << "emit select" << sysid << compid;
emit selected(sysid,compid);
isShowTip = (isShowTip)?(false):(true);
if(isSelected == false)
{
emit vehicleChanged(sysid,compid);
}
isSelected = true;
update();
}
//QGraphicsItem::mouseReleaseEvent(event);
}
void UAVItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
}
}
void UAVItem::setID(int sys,int comp)
{
sysid = sys;
compid = comp;
}
void UAVItem::setSelect(bool select)
{
isSelected = select;
qDebug() << "emit select" << sysid << compid;
emit selected(sysid,compid);
update();
}
void UAVItem::setEdit(bool value)
{
isEdit = value;
if(isEdit)
{
this->setZValue(1);
}
else
{
this->setZValue(8);
}
}
void UAVItem::updateTextOverlay()
{
QPainterPath temp;
if (!showUAVInfo) {
temp.swap(textPath);
return;
}
QFont borderfont("Arial", 14, QFont::Normal, false);
// Top left corner of text
int textAnchorX = 20;
int textAnchorY = 20;
QString uavoInfoStrLine1, uavoInfoStrLine2;
QString uavoInfoStrLine3, uavoInfoStrLine4;
QString uavoInfoStrLine5;
uavoInfoStrLine1.append(QString("CAS: %1 kph").arg(CAS_mps * 3.6));
uavoInfoStrLine2.append(QString("Groundspeed: %1 kph").arg(groundspeed_kph, 0, 'f', 1));
uavoInfoStrLine3.append(QString("Lat-Lon: %1, %2").arg(coord.Lat(), 0, 'f', 7).arg(coord.Lng(), 0, 'f', 7));
uavoInfoStrLine4.append(QString("North-East: %1 m, %2 m").arg(NED[0], 0, 'f', 1).arg(NED[1], 0, 'f', 1));
uavoInfoStrLine5.append(QString("Altitude: %1 m").arg(-NED[2], 0, 'f', 1));
temp.addText(textAnchorX, textAnchorY + 16 * 0, borderfont, uavoInfoStrLine1);
temp.addText(textAnchorX, textAnchorY + 16 * 1, borderfont, uavoInfoStrLine2);
temp.addText(textAnchorX, textAnchorY + 16 * 2, borderfont, uavoInfoStrLine3);
temp.addText(textAnchorX, textAnchorY + 16 * 3, borderfont, uavoInfoStrLine4);
temp.addText(textAnchorX, textAnchorY + 16 * 4, borderfont, uavoInfoStrLine5);
// Add text for time rings.
if (groundspeed_mps > 0) {
// Always add the left side...
temp.addText(-(groundspeed_mps_filt * ringTime * 1 * meters2pixels + 10), 0, borderfont, QString("%1 s").arg(ringTime, 0, 'f', 0));
temp.addText(-(groundspeed_mps_filt * ringTime * 2 * meters2pixels + 10), 0, borderfont, QString("%1 s").arg(ringTime * 2, 0, 'f', 0));
temp.addText(-(groundspeed_mps_filt * ringTime * 4 * meters2pixels + 10), 0, borderfont, QString("%1 s").arg(ringTime * 4, 0, 'f', 0));
// ... and add the right side, only if it doesn't interfere with the uav info text
if (groundspeed_mps_filt * ringTime * 4 * meters2pixels > 200) {
if (groundspeed_mps_filt * ringTime * 2 * meters2pixels > 200) {
if (groundspeed_mps_filt * ringTime * 1 * meters2pixels > 200) {
temp.addText(groundspeed_mps_filt * ringTime * 1 * meters2pixels - 8, 0, borderfont, QString("%1 s").arg(ringTime, 0, 'f', 0));
}
temp.addText(groundspeed_mps_filt * ringTime * 2 * meters2pixels - 8, 0, borderfont, QString("%1 s").arg(ringTime * 2, 0, 'f', 0));
}
temp.addText(groundspeed_mps_filt * ringTime * 4 * meters2pixels - 8, 0, borderfont, QString("%1 s").arg(ringTime * 4, 0, 'f', 0));
}
}
temp.swap(textPath);
}
QRectF UAVItem::boundingRect() const
{
if (showUAVInfo) {
if (boundingRectSize < 220) {
// In case the bounding rectangle isn't big enough to get the whole of the UAV Info graphic
return QRectF(-boundingRectSize, -80, boundingRectSize + 220, 180);
} else {
return QRectF(-boundingRectSize, -boundingRectSize, 2 * boundingRectSize, 2 * boundingRectSize);
}
} else {
if(isShowTip)
{
return QRectF(-pic.width() * 1.3, -pic.height() * 1.3, pic.width() * 2.6, pic.height() * 2.6);
}
else
{
return QRectF(-pic.width() * 0.5, -pic.height() * 0.5, pic.width() * 1, pic.height() * 1);
}
}
}
void UAVItem::SetNED(double NED[3])
{
this->NED[0] = NED[0];
this->NED[1] = NED[1];
this->NED[2] = NED[2];
}
void UAVItem::SetYawRate(double yawRate_dps)
{
this->yawRate_dps = yawRate_dps;
if (fabs(this->yawRate_dps) < 5e-1) { // This number is really the smallest we can go. Any smaller, and it might have problems if we forecast a shorter distance into the future
this->yawRate_dps = 5e-1;
}
// *********** Create trend arc
trendSpanAngle = this->yawRate_dps * 5; // Forecast 5 seconds into the future
// Calculate radius in [m], and then convert to pixels in local frame (not the same frame as is displayed on the map widget)
trendRadius = fabs(groundspeed_mps / (this->yawRate_dps * M_PI / 180)) * meters2pixels;
}
void UAVItem::SetCAS(double CAS_mps)
{
this->CAS_mps = CAS_mps;
}
void UAVItem::SetGroundspeed(double vNED[3], int m_maxUpdateRate_ms)//这个应该没有任何意义
{
this->vNED[0] = vNED[0];
this->vNED[1] = vNED[1];
this->vNED[2] = vNED[2];
groundspeed_kph = sqrt(vNED[0] * vNED[0] + vNED[1] * vNED[1] + vNED[2] * vNED[2]) * 3.6;
groundspeed_mps = groundspeed_kph / 3.6;
// On the first pass, set the filtered speed to the reported speed.
static bool firstGroundspeed = true;
if (firstGroundspeed) {
groundspeed_mps_filt = groundspeed_kph / 3.6;
firstGroundspeed = false;
} else {
int riseTime_ms = 1000;
double alpha = m_maxUpdateRate_ms / (double)(m_maxUpdateRate_ms + riseTime_ms);
groundspeed_mps_filt = alpha * groundspeed_mps_filt + (1 - alpha) * (groundspeed_kph / 3.6);
}
ringTime = 10 * pow(2, 17 - map->ZoomTotal()); // Basic ring is 10 seconds wide at zoom level 17
precalcRings = groundspeed_mps_filt * ringTime * meters2pixels;
boundingRectSize = groundspeed_mps_filt * ringTime * 4 * meters2pixels + 20;
prepareGeometryChange();
}
void UAVItem::SetUAVPos(const internals::PointLatLng &position, const int &altitude)
{
if (coord.IsEmpty()) {
lastcoord = coord;
}
if (coord != position) {
if (trailtype == UAVTrailType::ByTimeElapsed) {
if (timer.elapsed() > trailtime * 1000) {
TrailItem *ob = new TrailItem(position, altitude, Qt::green, map);
trail->addToGroup(ob);
connect(this, SIGNAL(setChildPosition()), ob, SLOT(setPosSLOT()));
if (!lasttrailline.IsEmpty()) {
TrailLineItem *obj = new TrailLineItem(lasttrailline, position, Qt::red, map);
trailLine->addToGroup(obj);
connect(this, SIGNAL(setChildLine()), obj, SLOT(setLineSlot()));
}
lasttrailline = position;
timer.restart();
}
} else if (trailtype == UAVTrailType::ByDistance) {
if (qAbs(internals::PureProjection::DistanceBetweenLatLng(lastcoord, position) * 10000000) > traildistance) {
TrailItem *ob = new TrailItem(position, altitude, Qt::green, map);
trail->addToGroup(ob);
connect(this, SIGNAL(setChildPosition()), ob, SLOT(setPosSLOT()));
if (!lasttrailline.IsEmpty()) {
TrailLineItem *obj = new TrailLineItem(lasttrailline, position, Qt::red, map);
trailLine->addToGroup(obj);
connect(this, SIGNAL(setChildLine()), obj, SLOT(setLineSlot()));
}
lasttrailline = position;
lastcoord = position;
}
}
//只允许1000个航迹
if(trailLine->childItems().size() >= 3000)//50分钟
{
//qDebug() << "trail over number" << trailLine->childItems().size();
QGraphicsItem *i = trailLine->childItems().at(0);
trailLine->removeFromGroup(i);
delete i;
}
if(trail->childItems().size() >= 3001)
{
//qDebug() << "trail over number" << trail->childItems().size();
QGraphicsItem *i = trail->childItems().at(0);
trail->removeFromGroup(i);
delete i;
}
coord = position;
this->altitude = altitude;
RefreshPos();
if (mapfollowtype == UAVMapFollowType::CenterAndRotateMap || mapfollowtype == UAVMapFollowType::CenterMap) {
mapwidget->SetCurrentPosition(coord);
}
if (autosetreached) {
foreach(QGraphicsItem * i, map->childItems()) {
WayPointItem *wp = qgraphicsitem_cast<WayPointItem *>(i);
if (wp) {
if (Distance3D(wp->Coord(), wp->Altitude()) < autosetdistance) {
wp->SetReached(true);
emit UAVReachedWayPoint(wp->Number(), wp);
}
}
}
}
if (mapwidget->Home != 0) {
// verify if the UAV is inside the safety bouble
if (Distance3D(mapwidget->Home->Coord(), mapwidget->Home->Altitude()) > mapwidget->Home->SafeArea()) {
if (mapwidget->Home->safe != false) {
mapwidget->Home->safe = false;
mapwidget->Home->update();
emit UAVLeftSafetyBouble(this->coord);
}
} else {
if (mapwidget->Home->safe != true) {
mapwidget->Home->safe = true;
mapwidget->Home->update();
}
}
}
}
}
/**
* Rotate the UAV Icon on the map, or rotate the map
* depending on the display mode
*/
void UAVItem::SetUAVHeading(const qreal &value)
{
if (mapfollowtype == UAVMapFollowType::CenterAndRotateMap) {
mapwidget->SetRotate(-value);
} else {
if (this->rotation() != value) {
this->setRotation(value);
}
}
}
int UAVItem::type() const
{
return Type;
}
void UAVItem::RefreshPos()
{
localposition = map->FromLatLngToLocal(coord);
this->setPos(localposition.X(), localposition.Y());
emit setChildPosition();
emit setChildLine();
updateTextOverlay();
}
void UAVItem::setOpacitySlot(qreal opacity)
{
this->setOpacity(opacity);
}
void UAVItem::zoomChangedSlot()
{
double pixels2meters = map->Projection()->GetGroundResolution(map->ZoomTotal(), coord.Lat());
meters2pixels = 1.0 / pixels2meters;
boundingRectSize = groundspeed_mps_filt * ringTime * 4 * meters2pixels + 20;
prepareGeometryChange();
updateTextOverlay();
update();
}
void UAVItem::SetTrailType(const UAVTrailType::Types &value)
{
trailtype = value;
if (trailtype == UAVTrailType::ByTimeElapsed) {
timer.restart();
}
}
void UAVItem::SetShowTrail(const bool &value)
{
showtrail = value;
trail->setVisible(value);
}
void UAVItem::SetShowTrailLine(const bool &value)
{
showtrailline = value;
trailLine->setVisible(value);
}
void UAVItem::DeleteTrail() const
{
foreach(QGraphicsItem * i, trail->childItems())
delete i;
foreach(QGraphicsItem * i, trailLine->childItems())
delete i;
}
double UAVItem::Distance3D(const internals::PointLatLng &coord, const int &altitude)
{
return sqrt(pow(internals::PureProjection::DistanceBetweenLatLng(this->coord, coord) * 1000, 2) +
pow(this->altitude - altitude, 2));
}
void UAVItem::SetUavPic(QString UAVPic)
{
pic.load(":/uavs/images/" + UAVPic);
}
void UAVItem::SetShowUAVInfo(bool const & value)
{
showUAVInfo = value;
showJustChanged = true;
update();
}
void UAVItem::generateArrowhead()//不需要
{
qreal arrowSize = 10;
// Create line from (0,0), to (1,1). Later, we'll scale and rotate it
arrowShaft = QLineF(0, 0, 1.0, 1.0);
// Set the starting point to (0,0)
arrowShaft.setP1(QPointF(0, 0));
// Set angle and length
arrowShaft.setLength(60.0);
arrowShaft.setAngle(90.0);
// Form arrowhead
double angle = ::acos(arrowShaft.dx() / arrowShaft.length());
if (arrowShaft.dy() <= 0) {
angle = (M_PI * 2) - angle;
}
QPointF arrowP1 = arrowShaft.pointAt(1) + QPointF(sin(angle + M_PI / 3) * arrowSize,
cos(angle + M_PI / 3) * arrowSize);
QPointF arrowP2 = arrowShaft.pointAt(1) + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
cos(angle + M_PI - M_PI / 3) * arrowSize);
// Assemble arrowhead
arrowHead.clear();
arrowHead << arrowShaft.pointAt(1) << arrowP1 << arrowP2;
}
}