Files
gcs-nf/opmap/mapwidget/mapgraphicitem.cpp
T
2020-09-23 18:41:12 +08:00

502 lines
19 KiB
C++

/**
******************************************************************************
*
* @file mapgraphicitem.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @brief The main graphicsItem used on the widget, contains the map and map logic
* @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 "uavitem.h"
#include "gpsitem.h"
#include "homeitem.h"
#include "mapgraphicitem.h"
#include <QGraphicsSceneMouseEvent>
namespace mapcontrol {
MapGraphicItem::MapGraphicItem(internals::Core *core, Configuration *configuration) : core(core), config(configuration), MapRenderTransform(1),
maxZoom(17), minZoom(2), zoomReal(0), zoomDigi(0), isSelected(false), rotation(0)
{
dragons.load(QString::fromUtf8(":/markers/images/dragons2.jpg"));
showTileGridLines = false;
isMouseOverMarker = false;
maprect = QRectF(0, 0, 1022, 680);
core->SetCurrentRegion(internals::Rectangle(0, 0, maprect.width(), maprect.height()));
core->SetMapType(MapType::GoogleHybrid);
this->SetZoom(2);
this->setFlag(ItemIsFocusable);
connect(core, SIGNAL(OnNeedInvalidation()), this, SLOT(Core_OnNeedInvalidation()));
connect(core, SIGNAL(OnMapDrag()), this, SLOT(childPosRefresh()));
connect(core, SIGNAL(OnMapZoomChanged()), this, SLOT(childPosRefresh()));
setCacheMode(QGraphicsItem::ItemCoordinateCache);
}
void MapGraphicItem::start()
{
core->StartSystem();
}
void MapGraphicItem::resize(const QRectF &rect)
{
Q_UNUSED(rect);
{
this->prepareGeometryChange();
maprect = boundingBox(scene()->sceneRect(), rotation);
this->setTransform(QTransform().translate(-(maprect.width() - scene()->width()) / 2, -(maprect.height() - scene()->height()) / 2));
this->setTransformOriginPoint(maprect.center().x(), maprect.center().y());
this->setRotation(rotation);
}
core->OnMapSizeChanged(maprect.width(), maprect.height());
core->SetCurrentRegion(internals::Rectangle(0, 0, maprect.width(), maprect.height()));
if (isVisible()) {
core->GoToCurrentPosition();
}
}
QRectF MapGraphicItem::boundingRect() const
{
const int Margin = 1;
return maprect.adjusted(-Margin, -Margin, +Margin, +Margin);
}
void MapGraphicItem::Core_OnNeedInvalidation()
{
this->update();
emit childRefreshPosition();
}
void MapGraphicItem::childPosRefresh()
{
emit childRefreshPosition();
}
void MapGraphicItem::setOverlayOpacity(qreal value)
{
emit childSetOpacity(value);
}
void MapGraphicItem::ConstructLastImage(int const & zoomdiff)
{
QImage temp;
QSize size = boundingRect().size().toSize();
size.setWidth(size.width() * 2 * zoomdiff);
size.setHeight(size.height() * 2 * zoomdiff);
temp = QImage(size, QImage::Format_ARGB32_Premultiplied);
temp.fill(0);
QPainter imagePainter(&temp);
imagePainter.translate(-boundingRect().topLeft());
imagePainter.scale(2 * zoomdiff, 2 * zoomdiff);
paintImage(&imagePainter);
imagePainter.end();
lastimagepoint = Point(core->GetrenderOffset().X() * 2 * zoomdiff, core->GetrenderOffset().Y() * 2 * zoomdiff);
lastimage = temp;
}
void MapGraphicItem::paintImage(QPainter *painter)
{
if (MapRenderTransform != 1) {
QTransform transform;
transform.translate(-((boundingRect().width() * MapRenderTransform) - (boundingRect().width())) / 2, -((boundingRect().height() * MapRenderTransform) - (boundingRect().height())) / 2);
transform.scale(MapRenderTransform, MapRenderTransform);
painter->setWorldTransform(transform);
{
DrawMap2D(painter);
}
painter->resetTransform();
} else {
DrawMap2D(painter);
}
// painter->drawRect(maprect);
}
void MapGraphicItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
if (MapRenderTransform != 1) {
QTransform transform;
transform.translate(-((boundingRect().width() * MapRenderTransform) - (boundingRect().width())) / 2, -((boundingRect().height() * MapRenderTransform) - (boundingRect().height())) / 2);
transform.scale(MapRenderTransform, MapRenderTransform);
painter->setWorldTransform(transform);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
painter->setRenderHint(QPainter::HighQualityAntialiasing, true);
{
DrawMap2D(painter);
}
painter->resetTransform();
} else {
DrawMap2D(painter);
}
}
void MapGraphicItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (core->IsDragging()) {
if (MapRenderTransform != 1) {
qreal dx = (event->pos().x() - core->mouseDown.X()) / (MapRenderTransform);
qreal dy = (event->pos().y() - core->mouseDown.Y()) / (MapRenderTransform);
qreal nx = core->mouseDown.X() + dx;
qreal ny = core->mouseDown.Y() + dy;
core->mouseCurrent.SetX(nx);
core->mouseCurrent.SetY(ny);
} else {
core->mouseCurrent.SetX(event->pos().x());
core->mouseCurrent.SetY(event->pos().y());
}
{
core->Drag(core->mouseCurrent);
}
} else if (isSelected && !selectionStart.IsEmpty() && (event->modifiers() == Qt::ControlModifier || event->modifiers() == Qt::ShiftModifier)) {
selectionEnd = FromLocalToLatLng(event->pos().x(), event->pos().y());
{
internals::PointLatLng p1 = selectionStart;
internals::PointLatLng p2 = selectionEnd;
double x1 = qMin(p1.Lng(), p2.Lng());
double y1 = qMax(p1.Lat(), p2.Lat());
double x2 = qMax(p1.Lng(), p2.Lng());
double y2 = qMin(p1.Lat(), p2.Lat());
SetSelectedArea(internals::RectLatLng(y1, x1, x2 - x1, y1 - y2));
}
}
QGraphicsItem::mouseMoveEvent(event);
}
void MapGraphicItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "MapGraphicItem" << "press";
if (!IsMouseOverMarker()) {
if (event->button() == config->DragButton && CanDragMap() && !((event->modifiers() == Qt::ShiftModifier) || (event->modifiers() == Qt::ControlModifier))) {
core->mouseDown.SetX(event->pos().x());
core->mouseDown.SetY(event->pos().y());
this->setCursor(Qt::ClosedHandCursor);
core->BeginDrag(core->mouseDown);
this->update();
} else if (!isSelected && ((event->modifiers() == Qt::ControlModifier) || (event->modifiers() == Qt::ShiftModifier))) {
isSelected = true;
SetSelectedArea(internals::RectLatLng::Empty);
selectionEnd = internals::PointLatLng::Empty;
selectionStart = FromLocalToLatLng(event->pos().x(), event->pos().y());
}
}
}
void MapGraphicItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (isSelected) {
isSelected = false;
}
if (core->IsDragging()) {
core->EndDrag();
this->setCursor(Qt::ArrowCursor);
if (!BoundsOfMap.IsEmpty() && !BoundsOfMap.Contains(core->CurrentPosition())) {
if (!core->LastLocationInBounds.IsEmpty()) {
core->SetCurrentPosition(core->LastLocationInBounds);
}
}
} else {
if (!selectionEnd.IsEmpty() && !selectionStart.IsEmpty()) {
if (!selectedArea.IsEmpty() && event->modifiers() == Qt::ShiftModifier) {
SetZoomToFitRect(SelectedArea());
selectedArea = internals::RectLatLng::Empty;
}
}
}
}
void MapGraphicItem::keyPressEvent(QKeyEvent *event)
{
if (event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) {
this->setCursor(Qt::CrossCursor);
}
if (event->key() == Qt::Key_Escape) {
selectedArea = internals::RectLatLng::Empty;
}
QGraphicsItem::keyPressEvent(event);
}
void MapGraphicItem::keyReleaseEvent(QKeyEvent *event)
{
if ((event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) {
this->setCursor(Qt::ArrowCursor);
}
}
bool MapGraphicItem::SetZoomToFitRect(internals::RectLatLng const & rect)
{
int maxZoom = core->GetMaxZoomToFitRect(rect);
if (maxZoom > 0) {
internals::PointLatLng center = internals::PointLatLng(rect.Lat() - (rect.HeightLat() / 2), rect.Lng() + (rect.WidthLng() / 2));
core->SetCurrentPosition(center);
if (maxZoom > MaxZoom()) {
maxZoom = MaxZoom();
}
if ((int)Zoom() != maxZoom) {
SetZoom(maxZoom);
}
return true;
}
return false;
}
void MapGraphicItem::wheelEvent(QGraphicsSceneWheelEvent *event)
{
if (!IsMouseOverMarker() && !IsDragging()) {
if (core->GetmouseLastZoom().X() != event->pos().x() && core->mouseLastZoom.Y() != event->pos().y()) {
if (GetMouseWheelZoomType() == internals::MouseWheelZoomType::MousePositionAndCenter) {
core->SetCurrentPosition(FromLocalToLatLng(event->pos().x(), event->pos().y()));
} else if (GetMouseWheelZoomType() == internals::MouseWheelZoomType::ViewCenter) {
core->SetCurrentPosition(FromLocalToLatLng((int)maprect.width() / 2, (int)maprect.height() / 2));
} else if (GetMouseWheelZoomType() == internals::MouseWheelZoomType::MousePositionWithoutCenter) {
core->SetCurrentPosition(FromLocalToLatLng(event->pos().x(), event->pos().y()));
}
core->mouseLastZoom.SetX((event->pos().x()));
core->mouseLastZoom.SetY((event->pos().y()));
}
// set mouse position to map center
if (GetMouseWheelZoomType() != internals::MouseWheelZoomType::MousePositionWithoutCenter) {
{
// System.Drawing.Point p = PointToScreen(new System.Drawing.Point(Width/2, Height/2));
// Stuff.SetCursorPos((int) p.X, (int) p.Y);
}
}
core->MouseWheelZooming = true;
if (event->delta() > 0) {
SetZoom(ZoomTotal() + 1);
} else if (event->delta() < 0) {
SetZoom(ZoomTotal() - 1);
}
core->MouseWheelZooming = false;
}
}
void MapGraphicItem::DrawMap2D(QPainter *painter)
{
painter->drawImage(this->boundingRect(), dragons.toImage());
if (!lastimage.isNull()) {
painter->drawImage(core->GetrenderOffset().X() - lastimagepoint.X(), core->GetrenderOffset().Y() - lastimagepoint.Y(), lastimage);
}
for (int i = -core->GetsizeOfMapArea().Width(); i <= core->GetsizeOfMapArea().Width(); i++) {
for (int j = -core->GetsizeOfMapArea().Height(); j <= core->GetsizeOfMapArea().Height(); j++) {
core->SettilePoint(core->GetcenterTileXYLocation());
core->SettilePoint(Point(core->GettilePoint().X() + i, core->GettilePoint().Y() + j));
{
internals::Tile *t = core->Matrix.TileAt(core->GettilePoint());
if (true) {
core->tileRect.SetX(core->GettilePoint().X() * core->tileRect.Width());
core->tileRect.SetY(core->GettilePoint().Y() * core->tileRect.Height());
core->tileRect.Offset(core->GetrenderOffset());
if (core->GetCurrentRegion().IntersectsWith(core->tileRect)) {
bool found = false;
// render tile
// lock(t.Overlays)
if (t != 0) {
foreach(QByteArray img, t->Overlays) {
if (img.count() != 0) {
if (!found) {
found = true;
}
{
painter->drawPixmap(core->tileRect.X(), core->tileRect.Y(), core->tileRect.Width(), core->tileRect.Height(), PureImageProxy::FromStream(img));
}
}
}
}
if (showTileGridLines) {
painter->setPen(config->EmptyTileBorders);
painter->drawRect(core->tileRect.X(), core->tileRect.Y(), core->tileRect.Width(), core->tileRect.Height());
{
painter->setFont(config->MissingDataFont);
painter->setPen(Qt::red);
painter->drawText(QRectF(core->tileRect.X(), core->tileRect.Y(), core->tileRect.Width(), core->tileRect.Height()), Qt::AlignCenter, (core->GettilePoint() == core->GetcenterTileXYLocation() ? "CENTER: " : "TILE: ") + core->GettilePoint().ToString());
}
}
// add text if tile is missing
if (false) {
painter->fillRect(QRectF(core->tileRect.X(), core->tileRect.Y(), core->tileRect.Width(), core->tileRect.Height()), config->EmptytileBrush);
painter->setFont(config->MissingDataFont);
painter->drawText(QRectF(core->tileRect.X(), core->tileRect.Y(), core->tileRect.Width(), core->tileRect.Height()), config->EmptyTileText);
painter->setPen(config->EmptyTileBorders);
painter->drawRect(core->tileRect.X(), core->tileRect.Y(), core->tileRect.Width(), core->tileRect.Height());
// raise error
}
if (!SelectedArea().IsEmpty()) {
core::Point p1 = FromLatLngToLocal(SelectedArea().LocationTopLeft());
core::Point p2 = FromLatLngToLocal(SelectedArea().LocationRightBottom());
int x1 = p1.X();
int y1 = p1.Y();
int x2 = p2.X();
int y2 = p2.Y();
painter->setPen(Qt::black);
painter->setBrush(QBrush(QColor(50, 50, 100, 20)));
painter->drawRect(x1, y1, x2 - x1, y2 - y1);
}
}
}
}
}
}
}
core::Point MapGraphicItem::FromLatLngToLocal(internals::PointLatLng const & point)
{
core::Point ret = core->FromLatLngToLocal(point);
if (MapRenderTransform != 1) {
ret.SetX((int)(ret.X() * MapRenderTransform));
ret.SetY((int)(ret.Y() * MapRenderTransform));
ret.SetX(ret.X() - ((boundingRect().width() * MapRenderTransform) - (boundingRect().width())) / 2);
ret.SetY(ret.Y() - ((boundingRect().height() * MapRenderTransform) - (boundingRect().height())) / 2);
}
return ret;
}
internals::PointLatLng MapGraphicItem::FromLocalToLatLng(int x, int y)
{
if (MapRenderTransform != 1) {
x = x + ((boundingRect().width() * MapRenderTransform) - (boundingRect().width())) / 2;
y = y + ((boundingRect().height() * MapRenderTransform) - (boundingRect().height())) / 2;
x = (int)(x / MapRenderTransform);
y = (int)(y / MapRenderTransform);
}
return core->FromLocalToLatLng(x, y);
}
double MapGraphicItem::Zoom()
{
return zoomReal;
}
double MapGraphicItem::ZoomDigi()
{
return zoomDigi;
}
double MapGraphicItem::ZoomTotal()
{
return zoomDigi + zoomReal;
}
void MapGraphicItem::SetZoom(double const & value)
{
if (ZoomTotal() != value) {
if (value > MaxZoom()) {
zoomReal = MaxZoom();
zoomDigi = value - MaxZoom();
} else if (value < MinZoom()) {
zoomDigi = 0;
zoomReal = MinZoom();
} else {
zoomDigi = 0;
zoomReal = value;
}
double integer;
double remainder = modf(value, &integer);
if (zoomDigi != 0 || remainder != 0) {
float scaleValue = zoomDigi + remainder + 1;
{
MapRenderTransform = scaleValue;
}
if (integer > MaxZoom()) {
integer = MaxZoom();
}
SetZoomStep((qint32)(integer));
// core->GoToCurrentPositionOnZoom();
this->update();
} else {
MapRenderTransform = 1;
SetZoomStep((qint32)(value));
zoomReal = ZoomStep();
this->update();
}
}
}
int MapGraphicItem::ZoomStep() const
{
return core->Zoom();
}
void MapGraphicItem::SetZoomStep(int const & value)
{
if (value - core->Zoom() > 0 && value <= MaxZoom()) {
ConstructLastImage(value - core->Zoom());
} else if (value != MaxZoom()) {
lastimage = QImage();
}
if (value > MaxZoom()) {
core->SetZoom(MaxZoom());
emit zoomChanged(MaxZoom() + ZoomDigi(), Zoom(), ZoomDigi());
} else if (value < MinZoom()) {
core->SetZoom(MinZoom());
emit zoomChanged(MinZoom() + ZoomDigi(), Zoom(), ZoomDigi());
} else {
core->SetZoom(value);
emit zoomChanged(value + ZoomDigi(), Zoom(), ZoomDigi());;
}
}
void MapGraphicItem::Offset(int const & x, int const & y)
{
core->DragOffset(Point(x, y));
}
void MapGraphicItem::mapRotate(qreal angle)
{
if (rotation != angle) {
rotation = angle;
resize(scene()->sceneRect());
}
}
QRectF MapGraphicItem::boundingBox(const QRectF &rect, const qreal &angle)
{
QRectF ret(rect);
float c = cos(angle * 2 * M_PI / 360);
float s = sin(angle * 2 * M_PI / 360);
ret.setHeight(rect.height() * fabs(c) + rect.width() * fabs(s));
ret.setWidth(rect.width() * fabs(c) + rect.height() * fabs(s));
return ret;
}
QSize MapGraphicItem::sizeHint() const
{
core::Size size = core->projection->GetTileMatrixMaxXY(MinZoom());
core::Size tilesize = core->projection->TileSize();
QSize rsize((size.Width() + 1) * tilesize.Width(), (size.Height() + 1) * tilesize.Height());
return rsize;
}
}