1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 07:13:21 +02:00
GPXSee/src/GUI/mapview.cpp

1209 lines
27 KiB
C++
Raw Normal View History

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QWheelEvent>
2017-04-05 22:53:25 +02:00
#include <QApplication>
#include <QScrollBar>
#include <QClipboard>
#include <QOpenGLWidget>
2017-11-26 18:54:03 +01:00
#include "data/poi.h"
#include "data/data.h"
#include "map/map.h"
#include "map/pcs.h"
#include "trackitem.h"
2016-08-09 01:16:19 +02:00
#include "routeitem.h"
2016-02-19 21:34:55 +01:00
#include "waypointitem.h"
2019-01-31 01:46:53 +01:00
#include "areaitem.h"
#include "scaleitem.h"
#include "coordinatesitem.h"
2020-12-02 23:58:11 +01:00
#include "mapitem.h"
2017-04-05 22:53:25 +02:00
#include "keys.h"
#include "graphicsscene.h"
#include "mapaction.h"
#include "markerinfoitem.h"
#include "mapview.h"
2017-06-26 00:20:42 +02:00
#define MAX_DIGITAL_ZOOM 2
2017-06-26 00:20:42 +02:00
#define MIN_DIGITAL_ZOOM -3
2018-10-05 07:10:49 +02:00
#define MARGIN 10
2017-06-26 00:20:42 +02:00
#define SCALE_OFFSET 7
#define COORDINATES_OFFSET SCALE_OFFSET
2018-10-15 01:15:00 +02:00
2020-12-02 23:58:11 +01:00
template<typename T>
static void updateZValues(T &items)
{
for (int i = 0; i < items.size(); i++) {
const QGraphicsItem *ai = items.at(i);
for (int j = 0; j < items.size(); j++) {
QGraphicsItem *aj = items[j];
if (aj->boundingRect().contains(ai->boundingRect()))
aj->setZValue(qMin(ai->zValue() - 1, aj->zValue()));
}
}
}
MapView::MapView(Map *map, POI *poi, QWidget *parent)
2017-07-27 19:47:46 +02:00
: QGraphicsView(parent)
{
Q_ASSERT(map != 0);
Q_ASSERT(poi != 0);
_scene = new GraphicsScene(this);
setScene(_scene);
setDragMode(QGraphicsView::ScrollHandDrag);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2020-12-06 00:11:47 +01:00
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
setResizeAnchor(QGraphicsView::AnchorViewCenter);
2016-10-04 10:16:46 +02:00
setAcceptDrops(false);
_mapScale = new ScaleItem();
_mapScale->setZValue(2.0);
_scene->addItem(_mapScale);
_coordinates = new CoordinatesItem();
_coordinates->setZValue(2.0);
_coordinates->setVisible(false);
_scene->addItem(_coordinates);
_outputProjection = PCS::pcs(3857);
_inputProjection = GCS::gcs(4326);
_map = map;
2018-08-23 20:26:10 +02:00
_map->load();
_map->setOutputProjection(_outputProjection);
_map->setInputProjection(_inputProjection);
2021-04-28 00:01:07 +02:00
connect(_map, &Map::tilesLoaded, this, &MapView::reloadMap);
2017-10-11 22:39:42 +02:00
_poi = poi;
2021-04-28 00:01:07 +02:00
connect(_poi, &POI::pointsChanged, this, &MapView::updatePOI);
2019-01-31 01:46:53 +01:00
_mapOpacity = 1.0;
2017-09-15 00:07:09 +02:00
_backgroundColor = Qt::white;
2017-12-03 00:36:52 +01:00
_markerColor = Qt::red;
2016-08-09 01:16:19 +02:00
_showMap = true;
2016-08-09 01:16:19 +02:00
_showTracks = true;
_showRoutes = true;
2019-01-31 01:46:53 +01:00
_showAreas = true;
2016-08-09 01:16:19 +02:00
_showWaypoints = true;
_showWaypointLabels = true;
2021-10-10 08:38:38 +02:00
_showWaypointIcons = false;
2016-10-08 14:53:10 +02:00
_showPOI = true;
2016-08-09 01:16:19 +02:00
_showPOILabels = true;
2021-10-10 08:38:38 +02:00
_showPOIIcons = false;
2016-08-09 01:16:19 +02:00
_overlapPOIs = true;
2016-08-09 10:47:49 +02:00
_showRouteWaypoints = true;
2019-02-16 12:39:23 +01:00
_showMarkers = true;
_markerInfoType = MarkerInfoItem::None;
_showPathTicks = false;
2016-12-06 01:48:26 +01:00
_trackWidth = 3;
_routeWidth = 3;
_trackStyle = Qt::SolidLine;
_routeStyle = Qt::DashLine;
_waypointSize = 8;
_waypointColor = Qt::black;
_poiSize = 8;
_poiColor = Qt::black;
2016-08-09 01:16:19 +02:00
_deviceRatio = 1.0;
_mapRatio = 1.0;
2018-08-23 20:26:10 +02:00
_opengl = false;
2016-08-09 01:16:19 +02:00
_plot = false;
2017-04-05 22:53:25 +02:00
_digitalZoom = 0;
2017-01-20 01:17:22 +01:00
_res = _map->resolution(_map->bounds());
_scene->setSceneRect(_map->bounds());
centerOn(_scene->sceneRect().center());
}
void MapView::centerOn(const QPointF &pos)
{
QGraphicsView::centerOn(pos);
QRectF vr(mapToScene(viewport()->rect()).boundingRect());
_res = _map->resolution(vr);
_mapScale->setResolution(_res);
_coordinates->setCoordinates(Coordinates());
}
PathItem *MapView::addTrack(const Track &track)
{
2019-01-31 01:46:53 +01:00
if (!track.isValid()) {
skipColor();
2016-09-19 00:56:10 +02:00
return 0;
2016-03-23 20:49:40 +01:00
}
TrackItem *ti = new TrackItem(track, _map);
2016-08-09 01:16:19 +02:00
_tracks.append(ti);
2016-11-11 17:58:18 +01:00
_tr |= ti->path().boundingRect();
2016-12-06 01:48:26 +01:00
ti->setColor(_palette.nextColor());
ti->setWidth(_trackWidth);
2016-12-06 01:48:26 +01:00
ti->setStyle(_trackStyle);
2016-08-09 01:16:19 +02:00
ti->setVisible(_showTracks);
ti->setDigitalZoom(_digitalZoom);
2017-12-03 00:36:52 +01:00
ti->setMarkerColor(_markerColor);
2019-02-16 12:39:23 +01:00
ti->showMarker(_showMarkers);
ti->showMarkerInfo(_markerInfoType);
ti->showTicks(_showPathTicks);
2016-08-09 01:16:19 +02:00
_scene->addItem(ti);
2016-09-19 00:56:10 +02:00
2017-11-26 18:54:03 +01:00
if (_showTracks)
addPOI(_poi->points(ti->path()));
2016-10-08 14:53:10 +02:00
2016-09-19 00:56:10 +02:00
return ti;
2016-03-03 09:15:56 +01:00
}
PathItem *MapView::addRoute(const Route &route)
2016-08-09 01:16:19 +02:00
{
2019-01-31 01:46:53 +01:00
if (!route.isValid()) {
skipColor();
2016-09-19 00:56:10 +02:00
return 0;
2016-08-09 01:16:19 +02:00
}
RouteItem *ri = new RouteItem(route, _map);
2016-08-09 01:16:19 +02:00
_routes.append(ri);
2016-11-11 17:58:18 +01:00
_rr |= ri->path().boundingRect();
2016-12-06 01:48:26 +01:00
ri->setColor(_palette.nextColor());
ri->setWidth(_routeWidth);
2016-12-06 01:48:26 +01:00
ri->setStyle(_routeStyle);
2016-08-09 01:16:19 +02:00
ri->setVisible(_showRoutes);
2016-08-09 10:47:49 +02:00
ri->showWaypoints(_showRouteWaypoints);
ri->showWaypointLabels(_showWaypointLabels);
2021-10-10 08:38:38 +02:00
ri->showWaypointIcons(_showWaypointLabels);
ri->setDigitalZoom(_digitalZoom);
2017-12-03 00:36:52 +01:00
ri->setMarkerColor(_markerColor);
2019-02-16 12:39:23 +01:00
ri->showMarker(_showMarkers);
ri->showMarkerInfo(_markerInfoType);
ri->showTicks(_showPathTicks);
2016-08-09 01:16:19 +02:00
_scene->addItem(ri);
2016-09-19 00:56:10 +02:00
2017-11-26 18:54:03 +01:00
if (_showRoutes)
addPOI(_poi->points(ri->path()));
2016-10-08 14:53:10 +02:00
2016-09-19 00:56:10 +02:00
return ri;
2016-08-09 01:16:19 +02:00
}
2019-01-31 01:46:53 +01:00
void MapView::addArea(const Area &area)
{
if (!area.isValid()) {
skipColor();
return;
}
AreaItem *ai = new AreaItem(area, _map);
ai->setColor(_palette.nextColor());
ai->setWidth(_areaWidth);
ai->setStyle(_areaStyle);
ai->setOpacity(_areaOpacity);
ai->setDigitalZoom(_digitalZoom);
ai->setVisible(_showAreas);
2020-12-02 23:58:11 +01:00
2019-01-31 01:46:53 +01:00
_scene->addItem(ai);
2020-12-02 23:58:11 +01:00
_ar |= ai->bounds();
_areas.append(ai);
2019-01-31 01:46:53 +01:00
if (_showAreas)
2020-12-02 23:58:11 +01:00
addPOI(_poi->points(ai->bounds()));
2019-01-31 01:46:53 +01:00
}
2019-01-18 00:17:28 +01:00
void MapView::addWaypoints(const QVector<Waypoint> &waypoints)
2016-03-15 01:20:24 +01:00
{
for (int i = 0; i < waypoints.count(); i++) {
2016-03-19 09:06:43 +01:00
const Waypoint &w = waypoints.at(i);
2016-03-15 01:20:24 +01:00
WaypointItem *wi = new WaypointItem(w, _map);
_waypoints.append(wi);
_wr = _wr.united(wi->waypoint().coordinates());
2016-03-15 01:20:24 +01:00
wi->setZValue(1);
wi->setSize(_waypointSize);
wi->setColor(_waypointColor);
2016-08-09 01:16:19 +02:00
wi->showLabel(_showWaypointLabels);
2021-10-10 08:38:38 +02:00
wi->showIcon(_showWaypointIcons);
2016-08-09 01:16:19 +02:00
wi->setVisible(_showWaypoints);
wi->setDigitalZoom(_digitalZoom);
2016-03-15 01:20:24 +01:00
_scene->addItem(wi);
2017-11-26 18:54:03 +01:00
if (_showWaypoints)
addPOI(_poi->points(w));
}
2016-03-15 01:20:24 +01:00
}
2020-12-10 22:02:09 +01:00
MapItem *MapView::addMap(MapAction *map)
2020-12-02 23:58:11 +01:00
{
MapItem *mi = new MapItem(map, _map);
mi->setColor(_palette.nextColor());
mi->setWidth(_areaWidth);
mi->setStyle(_areaStyle);
mi->setOpacity(_areaOpacity);
mi->setDigitalZoom(_digitalZoom);
mi->setVisible(_showAreas);
_scene->addItem(mi);
_ar |= mi->bounds();
_areas.append(mi);
if (_showAreas)
addPOI(_poi->points(mi->bounds()));
return mi;
}
QList<PathItem *> MapView::loadData(const Data &data)
2016-03-03 09:15:56 +01:00
{
2016-09-19 00:56:10 +02:00
QList<PathItem *> paths;
2018-03-10 08:40:55 +01:00
int zoom = _map->zoom();
2019-10-14 20:07:05 +02:00
for (int i = 0; i < data.areas().count(); i++)
addArea(data.areas().at(i));
2016-10-23 11:09:20 +02:00
for (int i = 0; i < data.tracks().count(); i++)
2019-01-31 01:46:53 +01:00
paths.append(addTrack(data.tracks().at(i)));
2016-10-23 11:09:20 +02:00
for (int i = 0; i < data.routes().count(); i++)
2019-01-31 01:46:53 +01:00
paths.append(addRoute(data.routes().at(i)));
2016-10-23 11:09:20 +02:00
addWaypoints(data.waypoints());
2016-03-15 01:20:24 +01:00
2019-01-31 01:46:53 +01:00
if (_tracks.empty() && _routes.empty() && _waypoints.empty()
&& _areas.empty())
2016-09-19 00:56:10 +02:00
return paths;
2016-03-15 01:20:24 +01:00
if (fitMapZoom() != zoom)
rescale();
2017-01-20 01:17:22 +01:00
else
2016-10-08 14:53:10 +02:00
updatePOIVisibility();
2020-12-02 23:58:11 +01:00
if (!data.areas().isEmpty())
updateZValues(_areas);
centerOn(contentCenter());
2016-09-19 00:56:10 +02:00
return paths;
}
void MapView::loadMaps(const QList<MapAction *> &maps)
2020-12-02 23:58:11 +01:00
{
int zoom = _map->zoom();
2020-12-10 22:02:09 +01:00
for (int i = 0; i < maps.size(); i++)
addMap(maps.at(i));
2020-12-02 23:58:11 +01:00
if (fitMapZoom() != zoom)
rescale();
else
updatePOIVisibility();
updateZValues(_areas);
centerOn(contentCenter());
}
2021-09-23 22:44:21 +02:00
void MapView::loadDEMs(const QList<Area> &dems)
{
int zoom = _map->zoom();
for (int i = 0; i < dems.size(); i++)
addArea(dems.at(i));
if (fitMapZoom() != zoom)
rescale();
else
updatePOIVisibility();
updateZValues(_areas);
centerOn(contentCenter());
}
int MapView::fitMapZoom() const
{
2019-01-31 01:46:53 +01:00
RectC br = _tr | _rr | _wr | _ar;
2016-03-15 01:20:24 +01:00
2017-12-01 20:52:34 +01:00
return _map->zoomFit(viewport()->size() - QSize(2*MARGIN, 2*MARGIN),
br.isNull() ? _map->llBounds() : br);
2016-03-15 01:20:24 +01:00
}
QPointF MapView::contentCenter() const
{
2019-01-31 01:46:53 +01:00
RectC br = _tr | _rr | _wr | _ar;
2017-12-01 20:52:34 +01:00
return br.isNull() ? sceneRect().center() : _map->ll2xy(br.center());
}
void MapView::updatePOIVisibility()
{
2016-10-08 14:53:10 +02:00
if (!_showPOI)
return;
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
2016-10-08 14:53:10 +02:00
it.value()->show();
if (!_overlapPOIs) {
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++) {
for (POIHash::const_iterator jt = _pois.constBegin();
jt != _pois.constEnd(); jt++) {
2016-10-08 14:53:10 +02:00
if (it.value()->isVisible() && jt.value()->isVisible()
&& it != jt && it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
}
}
void MapView::rescale()
{
_scene->setSceneRect(_map->bounds());
2018-08-18 21:06:36 +02:00
reloadMap();
2017-01-16 09:54:12 +01:00
2016-08-09 01:16:19 +02:00
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setMap(_map);
2016-08-09 01:16:19 +02:00
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMap(_map);
2019-01-31 01:46:53 +01:00
for (int i = 0; i < _areas.size(); i++)
_areas.at(i)->setMap(_map);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(_map);
2016-03-15 01:20:24 +01:00
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setMap(_map);
2016-10-08 14:53:10 +02:00
updatePOIVisibility();
}
void MapView::setPalette(const Palette &palette)
2016-12-06 01:48:26 +01:00
{
_palette = palette;
_palette.reset();
for (int i = 0; i < _tracks.count(); i++)
_tracks.at(i)->setColor(_palette.nextColor());
for (int i = 0; i < _routes.count(); i++)
_routes.at(i)->setColor(_palette.nextColor());
2019-01-31 01:46:53 +01:00
for (int i = 0; i < _areas.count(); i++)
_areas.at(i)->setColor(_palette.nextColor());
2016-12-06 01:48:26 +01:00
}
void MapView::setMap(Map *map)
{
QRectF vr(mapToScene(viewport()->rect()).boundingRect()
.intersected(_map->bounds()));
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
2017-06-26 00:20:42 +02:00
_map->unload();
2021-04-28 00:01:07 +02:00
disconnect(_map, &Map::tilesLoaded, this, &MapView::reloadMap);
_map = map;
2018-08-23 20:26:10 +02:00
_map->load();
_map->setOutputProjection(_outputProjection);
_map->setInputProjection(_inputProjection);
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
2021-04-28 00:01:07 +02:00
connect(_map, &Map::tilesLoaded, this, &MapView::reloadMap);
2017-12-01 21:27:12 +01:00
digitalZoom(0);
2017-04-05 22:53:25 +02:00
_map->zoomFit(viewport()->rect().size(), cr);
_scene->setSceneRect(_map->bounds());
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setMap(map);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMap(map);
2019-01-31 01:46:53 +01:00
for (int i = 0; i < _areas.size(); i++)
_areas.at(i)->setMap(map);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(map);
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
2017-04-05 22:53:25 +02:00
it.value()->setMap(_map);
updatePOIVisibility();
QPointF nc = QRectF(_map->ll2xy(cr.topLeft()),
_map->ll2xy(cr.bottomRight())).center();
centerOn(nc);
2018-08-18 21:06:36 +02:00
reloadMap();
}
void MapView::setPOI(POI *poi)
2016-10-08 14:53:10 +02:00
{
2021-04-28 00:01:07 +02:00
disconnect(_poi, &POI::pointsChanged, this, &MapView::updatePOI);
connect(poi, &POI::pointsChanged, this, &MapView::updatePOI);
2016-10-09 23:46:30 +02:00
2016-10-08 14:53:10 +02:00
_poi = poi;
updatePOI();
2016-10-08 14:53:10 +02:00
}
void MapView::setGraph(int index)
{
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setGraph(index);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setGraph(index);
}
void MapView::updatePOI()
2016-10-08 14:53:10 +02:00
{
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
2016-10-09 23:46:30 +02:00
_scene->removeItem(it.value());
qDeleteAll(_pois);
2016-10-09 23:46:30 +02:00
_pois.clear();
2017-11-26 18:54:03 +01:00
if (_showTracks)
for (int i = 0; i < _tracks.size(); i++)
addPOI(_poi->points(_tracks.at(i)->path()));
if (_showRoutes)
for (int i = 0; i < _routes.size(); i++)
addPOI(_poi->points(_routes.at(i)->path()));
2019-01-31 01:46:53 +01:00
if (_showAreas)
for (int i = 0; i < _areas.size(); i++)
2020-12-02 23:58:11 +01:00
addPOI(_poi->points(_areas.at(i)->bounds()));
2017-11-26 18:54:03 +01:00
if (_showWaypoints)
for (int i = 0; i< _waypoints.size(); i++)
addPOI(_poi->points(_waypoints.at(i)->waypoint()));
2016-10-08 14:53:10 +02:00
updatePOIVisibility();
}
void MapView::addPOI(const QList<Waypoint> &waypoints)
{
for (int i = 0; i < waypoints.size(); i++) {
2016-03-19 09:06:43 +01:00
const Waypoint &w = waypoints.at(i);
if (_pois.contains(SearchPointer<Waypoint>(&w)))
continue;
WaypointItem *pi = new WaypointItem(w, _map);
pi->setZValue(1);
pi->setSize(_poiSize);
pi->setColor(_poiColor);
2016-08-09 01:16:19 +02:00
pi->showLabel(_showPOILabels);
2021-10-10 08:38:38 +02:00
pi->showIcon(_showPOIIcons);
2016-10-08 14:53:10 +02:00
pi->setVisible(_showPOI);
pi->setDigitalZoom(_digitalZoom);
_scene->addItem(pi);
_pois.insert(SearchPointer<Waypoint>(&(pi->waypoint())), pi);
}
}
void MapView::setUnits(Units units)
{
2020-05-20 21:00:36 +02:00
WaypointItem::setUnits(units);
PathItem::setUnits(units);
2016-07-25 19:32:36 +02:00
2016-08-09 01:16:19 +02:00
for (int i = 0; i < _tracks.count(); i++)
2021-10-10 08:38:38 +02:00
_tracks.at(i)->updateTicks();
for (int i = 0; i < _routes.count(); i++)
2021-10-10 08:38:38 +02:00
_routes.at(i)->updateTicks();
2020-05-20 21:00:36 +02:00
_mapScale->setUnits(units);
}
void MapView::setCoordinatesFormat(CoordinatesFormat format)
{
2020-05-20 21:00:36 +02:00
WaypointItem::setCoordinatesFormat(format);
PathItem::setCoordinatesFormat(format);
for (int i = 0; i < _tracks.count(); i++)
2021-10-10 08:38:38 +02:00
_tracks.at(i)->updateMarkerInfo();
for (int i = 0; i < _routes.count(); i++)
2021-10-10 08:38:38 +02:00
_routes.at(i)->updateMarkerInfo();
2020-05-20 21:00:36 +02:00
_coordinates->setFormat(format);
}
2016-08-02 00:28:56 +02:00
2020-05-20 21:00:36 +02:00
void MapView::setTimeZone(const QTimeZone &zone)
{
WaypointItem::setTimeZone(zone);
PathItem::setTimeZone(zone);
for (int i = 0; i < _tracks.count(); i++)
2021-10-10 08:38:38 +02:00
_tracks.at(i)->updateMarkerInfo();
for (int i = 0; i < _routes.count(); i++)
2021-10-10 08:38:38 +02:00
_routes.at(i)->updateMarkerInfo();
}
void MapView::clearMapCache()
{
2017-10-04 23:15:39 +02:00
_map->clearCache();
reloadMap();
}
void MapView::digitalZoom(int zoom)
2017-04-05 22:53:25 +02:00
{
2017-12-01 21:27:12 +01:00
if (zoom) {
_digitalZoom += zoom;
scale(pow(2, zoom), pow(2, zoom));
} else {
_digitalZoom = 0;
resetTransform();
}
2017-04-05 22:53:25 +02:00
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setDigitalZoom(_digitalZoom);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setDigitalZoom(_digitalZoom);
2019-01-31 01:46:53 +01:00
for (int i = 0; i < _areas.size(); i++)
_areas.at(i)->setDigitalZoom(_digitalZoom);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setDigitalZoom(_digitalZoom);
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setDigitalZoom(_digitalZoom);
2017-04-05 22:53:25 +02:00
_mapScale->setDigitalZoom(_digitalZoom);
_coordinates->setDigitalZoom(_digitalZoom);
2017-04-05 22:53:25 +02:00
}
2020-12-29 18:36:23 +01:00
void MapView::zoom(int zoom, const QPoint &pos, bool shift)
2017-04-05 22:53:25 +02:00
{
if (_digitalZoom) {
if (((_digitalZoom > 0 && zoom > 0) && (!shift || _digitalZoom
2017-06-26 00:20:42 +02:00
>= MAX_DIGITAL_ZOOM)) || ((_digitalZoom < 0 && zoom < 0) && (!shift
|| _digitalZoom <= MIN_DIGITAL_ZOOM)))
2017-04-05 22:53:25 +02:00
return;
digitalZoom(zoom);
} else {
2018-05-03 19:11:55 +02:00
Coordinates c = _map->xy2ll(mapToScene(pos));
2018-10-15 01:15:00 +02:00
int oz = _map->zoom();
int nz = (zoom > 0) ? _map->zoomIn() : _map->zoomOut();
2017-04-05 22:53:25 +02:00
2018-10-15 01:15:00 +02:00
if (nz != oz) {
2017-04-05 22:53:25 +02:00
rescale();
centerOn(_map->ll2xy(c) - (pos - viewport()->rect().center()));
2017-04-05 22:53:25 +02:00
} else {
if (shift)
digitalZoom(zoom);
}
}
}
void MapView::wheelEvent(QWheelEvent *event)
{
2017-04-01 06:56:50 +02:00
static int deg = 0;
2020-12-29 18:36:23 +01:00
bool shift = (event->modifiers() & MODIFIER) ? true : false;
2020-12-30 09:26:26 +01:00
// Shift inverts the wheel axis on OS X, so use scrolling in both axes for
2020-12-29 18:36:23 +01:00
// the zoom.
int delta = event->angleDelta().y()
? event->angleDelta().y() : event->angleDelta().x();
2017-04-01 06:56:50 +02:00
2020-12-29 18:36:23 +01:00
deg += delta / 8;
2017-04-01 06:56:50 +02:00
if (qAbs(deg) < 15)
return;
deg = 0;
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
2020-12-29 18:36:23 +01:00
zoom((delta > 0) ? 1 : -1, event->pos(), shift);
#else // QT 5.15
2020-12-29 18:36:23 +01:00
zoom((delta > 0) ? 1 : -1, event->position().toPoint(), shift);
#endif // QT 5.15
}
void MapView::mouseDoubleClickEvent(QMouseEvent *event)
{
2020-12-29 18:36:23 +01:00
bool shift = (event->modifiers() & MODIFIER) ? true : false;
2020-12-02 23:58:11 +01:00
QGraphicsView::mouseDoubleClickEvent(event);
if (event->isAccepted())
return;
if (event->button() != Qt::LeftButton && event->button() != Qt::RightButton)
return;
2020-12-29 18:36:23 +01:00
zoom((event->button() == Qt::LeftButton) ? 1 : -1, event->pos(), shift);
}
void MapView::keyPressEvent(QKeyEvent *event)
{
2017-04-05 22:53:25 +02:00
int z;
2020-12-29 18:36:23 +01:00
bool shift = (event->modifiers() & MODIFIER) ? true : false;
2017-06-27 22:42:59 +02:00
QPoint pos = viewport()->rect().center();
if (event->key() == ZOOM_IN)
2017-04-05 22:53:25 +02:00
z = 1;
else if (event->key() == ZOOM_OUT)
2017-04-05 22:53:25 +02:00
z = -1;
else if (_digitalZoom && event->key() == Qt::Key_Escape) {
2017-12-01 21:27:12 +01:00
digitalZoom(0);
return;
} else {
if (event->key() == MODIFIER_KEY) {
_cursor = viewport()->cursor();
viewport()->setCursor(Qt::CrossCursor);
}
2017-04-05 22:53:25 +02:00
QGraphicsView::keyPressEvent(event);
return;
}
2020-12-29 18:36:23 +01:00
zoom(z, pos, shift);
}
void MapView::keyReleaseEvent(QKeyEvent *event)
{
2020-11-13 23:43:52 +01:00
if (event->key() == MODIFIER_KEY
&& viewport()->cursor().shape() == Qt::CrossCursor)
viewport()->setCursor(_cursor);
QGraphicsView::keyReleaseEvent(event);
}
void MapView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && event->modifiers() & MODIFIER)
2020-11-13 23:43:52 +01:00
QApplication::clipboard()->setText(Format::coordinates(
_map->xy2ll(mapToScene(event->pos())), _coordinates->format()));
else
QGraphicsView::mousePressEvent(event);
}
void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
2020-09-27 00:34:38 +02:00
PlotFlags flags)
{
QRect orig, adj;
qreal ratio, diff, q;
2017-08-31 16:28:37 +02:00
QPointF origScene, origPos;
2018-04-28 22:18:11 +02:00
int zoom;
2016-05-20 22:44:03 +02:00
// Enter plot mode
setUpdatesEnabled(false);
2017-08-31 16:28:37 +02:00
_plot = true;
_map->setDevicePixelRatio(_deviceRatio, 1.0);
// Compute sizes & ratios
2016-05-15 13:19:07 +02:00
orig = viewport()->rect();
2017-08-31 16:28:37 +02:00
origPos = _mapScale->pos();
2016-05-27 22:45:58 +02:00
if (orig.height() * (target.width() / target.height()) - orig.width() < 0) {
ratio = target.height() / target.width();
2016-05-27 22:45:58 +02:00
diff = (orig.width() * ratio) - orig.height();
adj = orig.adjusted(0, -diff/2, 0, diff/2);
2016-05-27 22:45:58 +02:00
} else {
ratio = target.width() / target.height();
diff = (orig.height() * ratio) - orig.width();
adj = orig.adjusted(-diff/2, 0, diff/2, 0);
}
2020-09-27 00:34:38 +02:00
// Expand the view if plotting into a bitmap
if (flags & Expand) {
qreal xdiff = (target.width() - adj.width()) / 2.0;
qreal ydiff = (target.height() - adj.height()) / 2.0;
adj.adjust(-xdiff, -ydiff, xdiff, ydiff);
q = 1.0;
} else
q = (target.width() / scale) / adj.width();
2017-08-31 16:28:37 +02:00
// Adjust the view for printing
2020-09-27 00:34:38 +02:00
if (flags & HiRes) {
2018-04-28 22:18:11 +02:00
zoom = _map->zoom();
QRectF vr(mapToScene(orig).boundingRect());
origScene = vr.center();
2017-08-31 16:28:37 +02:00
QPointF s(painter->device()->logicalDpiX()
/ (qreal)metric(QPaintDevice::PdmDpiX),
painter->device()->logicalDpiY()
/ (qreal)metric(QPaintDevice::PdmDpiY));
2017-08-31 16:28:37 +02:00
adj = QRect(0, 0, adj.width() * s.x(), adj.height() * s.y());
2019-01-31 01:46:53 +01:00
_map->zoomFit(adj.size(), _tr | _rr | _wr | _ar);
rescale();
QPointF center = contentCenter();
centerOn(center);
adj.moveCenter(mapFromScene(center));
_mapScale->setDigitalZoom(_digitalZoom - log2(s.x() / q));
2017-08-31 16:28:37 +02:00
_mapScale->setPos(mapToScene(QPoint(adj.bottomRight() + QPoint(
-(SCALE_OFFSET + _mapScale->boundingRect().width()) * (s.x() / q),
-(SCALE_OFFSET + _mapScale->boundingRect().height()) * (s.x() / q)))));
2017-08-31 16:28:37 +02:00
} else {
_mapScale->setDigitalZoom(_digitalZoom - log2(1.0 / q));
2017-08-31 16:28:37 +02:00
_mapScale->setPos(mapToScene(QPoint(adj.bottomRight() + QPoint(
-(SCALE_OFFSET + _mapScale->boundingRect().width()) / q ,
-(SCALE_OFFSET + _mapScale->boundingRect().height()) / q))));
2017-08-31 16:28:37 +02:00
}
2016-05-20 22:44:03 +02:00
2017-08-31 16:28:37 +02:00
// Print the view
render(painter, target, adj);
2016-05-20 22:44:03 +02:00
2017-08-31 16:28:37 +02:00
// Revert view changes to display mode
2020-09-27 22:52:04 +02:00
if (flags & HiRes) {
2018-04-28 22:18:11 +02:00
_map->setZoom(zoom);
rescale();
centerOn(origScene);
}
2018-04-28 22:18:11 +02:00
_mapScale->setDigitalZoom(_digitalZoom);
2017-08-31 16:28:37 +02:00
_mapScale->setPos(origPos);
// Exit plot mode
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
2017-08-31 16:28:37 +02:00
_plot = false;
2016-05-20 22:44:03 +02:00
setUpdatesEnabled(true);
}
void MapView::clear()
{
_pois.clear();
2016-08-09 01:16:19 +02:00
_tracks.clear();
_routes.clear();
2019-01-31 01:46:53 +01:00
_areas.clear();
_waypoints.clear();
_scene->removeItem(_mapScale);
_scene->removeItem(_coordinates);
_scene->clear();
_scene->addItem(_mapScale);
_scene->addItem(_coordinates);
2016-03-02 09:34:39 +01:00
_palette.reset();
2017-06-30 18:15:22 +02:00
_tr = RectC();
_rr = RectC();
_wr = RectC();
2019-01-31 01:46:53 +01:00
_ar = RectC();
2017-04-05 22:53:25 +02:00
2017-12-01 21:27:12 +01:00
digitalZoom(0);
// If not reset, causes huge redraw areas (and system memory exhaustion)
resetCachedContent();
}
void MapView::showTracks(bool show)
2016-08-09 01:16:19 +02:00
{
_showTracks = show;
for (int i = 0; i < _tracks.count(); i++)
_tracks.at(i)->setVisible(show);
2017-11-26 18:54:03 +01:00
updatePOI();
2016-08-09 01:16:19 +02:00
}
void MapView::showRoutes(bool show)
2016-08-09 01:16:19 +02:00
{
_showRoutes = show;
for (int i = 0; i < _routes.count(); i++)
_routes.at(i)->setVisible(show);
2017-11-26 18:54:03 +01:00
updatePOI();
2016-08-09 01:16:19 +02:00
}
void MapView::showWaypoints(bool show)
2016-08-09 01:16:19 +02:00
{
_showWaypoints = show;
for (int i = 0; i < _waypoints.count(); i++)
_waypoints.at(i)->setVisible(show);
2017-11-26 18:54:03 +01:00
updatePOI();
2016-08-09 01:16:19 +02:00
}
2019-01-31 01:46:53 +01:00
void MapView::showAreas(bool show)
{
_showAreas = show;
for (int i = 0; i < _areas.count(); i++)
_areas.at(i)->setVisible(show);
updatePOI();
}
void MapView::showWaypointLabels(bool show)
2016-08-09 01:16:19 +02:00
{
_showWaypointLabels = show;
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->showLabel(show);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->showWaypointLabels(show);
2016-08-09 01:16:19 +02:00
}
2021-10-10 08:38:38 +02:00
void MapView::showWaypointIcons(bool show)
{
_showWaypointIcons = show;
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->showIcon(show);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->showWaypointIcons(show);
}
void MapView::showRouteWaypoints(bool show)
2016-08-09 10:47:49 +02:00
{
_showRouteWaypoints = show;
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->showWaypoints(show);
}
2019-02-16 12:39:23 +01:00
void MapView::showMarkers(bool show)
{
_showMarkers = show;
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->showMarker(show);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->showMarker(show);
}
void MapView::showMarkerInfo(MarkerInfoItem::Type type)
{
_markerInfoType = type;
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->showMarkerInfo(type);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->showMarkerInfo(type);
}
void MapView::showTicks(bool show)
{
_showPathTicks = show;
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->showTicks(show);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->showTicks(show);
}
void MapView::showMap(bool show)
{
_showMap = show;
2018-08-18 21:06:36 +02:00
reloadMap();
}
void MapView::showPOI(bool show)
2016-10-08 14:53:10 +02:00
{
_showPOI = show;
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
2016-10-08 14:53:10 +02:00
it.value()->setVisible(show);
updatePOIVisibility();
}
void MapView::showPOILabels(bool show)
2016-08-09 01:16:19 +02:00
{
_showPOILabels = show;
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
2016-08-09 01:16:19 +02:00
it.value()->showLabel(show);
2016-10-08 14:53:10 +02:00
updatePOIVisibility();
2016-08-09 01:16:19 +02:00
}
2021-10-10 08:38:38 +02:00
void MapView::showPOIIcons(bool show)
{
_showPOIIcons = show;
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->showIcon(show);
updatePOIVisibility();
}
void MapView::showCoordinates(bool show)
{
_coordinates->setVisible(show);
setMouseTracking(show);
}
void MapView::showOverlappedPOIs(bool show)
{
_overlapPOIs = show;
2016-10-08 14:53:10 +02:00
updatePOIVisibility();
}
void MapView::setTrackWidth(int width)
2016-12-06 01:48:26 +01:00
{
_trackWidth = width;
for (int i = 0; i < _tracks.count(); i++)
_tracks.at(i)->setWidth(width);
2016-12-06 01:48:26 +01:00
}
void MapView::setRouteWidth(int width)
2016-12-06 01:48:26 +01:00
{
_routeWidth = width;
for (int i = 0; i < _routes.count(); i++)
_routes.at(i)->setWidth(width);
2016-12-06 01:48:26 +01:00
}
2019-01-31 01:46:53 +01:00
void MapView::setAreaWidth(int width)
{
_areaWidth = width;
for (int i = 0; i < _areas.count(); i++)
_areas.at(i)->setWidth(width);
}
void MapView::setTrackStyle(Qt::PenStyle style)
2016-12-06 01:48:26 +01:00
{
_trackStyle = style;
for (int i = 0; i < _tracks.count(); i++)
_tracks.at(i)->setStyle(style);
}
void MapView::setRouteStyle(Qt::PenStyle style)
2016-12-06 01:48:26 +01:00
{
_routeStyle = style;
for (int i = 0; i < _routes.count(); i++)
_routes.at(i)->setStyle(style);
}
2019-01-31 01:46:53 +01:00
void MapView::setAreaStyle(Qt::PenStyle style)
{
_areaStyle = style;
for (int i = 0; i < _areas.count(); i++)
_areas.at(i)->setStyle(style);
}
void MapView::setAreaOpacity(int opacity)
{
_areaOpacity = opacity / 100.0;
for (int i = 0; i < _areas.count(); i++)
_areas.at(i)->setOpacity(_areaOpacity);
}
void MapView::setWaypointSize(int size)
{
_waypointSize = size;
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setSize(size);
}
void MapView::setWaypointColor(const QColor &color)
{
_waypointColor = color;
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setColor(color);
}
void MapView::setPOISize(int size)
{
_poiSize = size;
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setSize(size);
}
void MapView::setPOIColor(const QColor &color)
{
_poiColor = color;
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setColor(color);
}
void MapView::setMapOpacity(int opacity)
{
2019-01-31 01:46:53 +01:00
_mapOpacity = opacity / 100.0;
2018-08-18 21:06:36 +02:00
reloadMap();
}
void MapView::setBackgroundColor(const QColor &color)
{
2017-09-15 00:07:09 +02:00
_backgroundColor = color;
2018-08-18 21:06:36 +02:00
reloadMap();
}
void MapView::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->fillRect(rect, _backgroundColor);
if (_showMap) {
QRectF ir = rect.intersected(_map->bounds());
2018-08-23 20:26:10 +02:00
Map::Flags flags = Map::NoFlags;
2019-01-31 01:46:53 +01:00
if (_mapOpacity < 1.0)
painter->setOpacity(_mapOpacity);
2018-08-23 20:26:10 +02:00
if (_plot)
flags = Map::Block;
else if (_opengl)
flags = Map::OpenGL;
_map->draw(painter, ir, flags);
}
}
void MapView::paintEvent(QPaintEvent *event)
{
QPointF scaleScenePos = mapToScene(rect().bottomRight() + QPoint(
2016-05-20 22:44:03 +02:00
-(SCALE_OFFSET + _mapScale->boundingRect().width()),
-(SCALE_OFFSET + _mapScale->boundingRect().height())));
if (_mapScale->pos() != scaleScenePos && !_plot)
_mapScale->setPos(scaleScenePos);
if (_coordinates->isVisible()) {
QPointF coordinatesScenePos = mapToScene(rect().bottomLeft()
+ QPoint(COORDINATES_OFFSET, -COORDINATES_OFFSET));
if (_coordinates->pos() != coordinatesScenePos && !_plot)
_coordinates->setPos(coordinatesScenePos);
}
QGraphicsView::paintEvent(event);
}
2016-12-06 01:48:26 +01:00
void MapView::scrollContentsBy(int dx, int dy)
2017-01-16 09:54:12 +01:00
{
QGraphicsView::scrollContentsBy(dx, dy);
QRectF sr(mapToScene(viewport()->rect()).boundingRect());
qreal res = _map->resolution(sr);
2017-01-16 09:54:12 +01:00
if (qMax(res, _res) / qMin(res, _res) > 1.1) {
_mapScale->setResolution(res);
_res = res;
}
}
void MapView::mouseMoveEvent(QMouseEvent *event)
{
if (_coordinates->isVisible())
_coordinates->setCoordinates(_map->xy2ll(mapToScene(event->pos())));
QGraphicsView::mouseMoveEvent(event);
}
void MapView::leaveEvent(QEvent *event)
{
_coordinates->setCoordinates(Coordinates());
QGraphicsView::leaveEvent(event);
}
void MapView::useOpenGL(bool use)
2016-12-06 01:48:26 +01:00
{
_opengl = use;
2017-02-12 17:38:20 +01:00
if (use)
setViewport(new QOpenGLWidget);
2017-02-12 17:38:20 +01:00
else
2016-12-06 01:48:26 +01:00
setViewport(new QWidget);
}
2017-09-15 00:07:09 +02:00
void MapView::useAntiAliasing(bool use)
2017-09-15 00:07:09 +02:00
{
setRenderHint(QPainter::Antialiasing, use);
}
2017-10-04 23:15:39 +02:00
2017-12-03 00:36:52 +01:00
void MapView::setMarkerColor(const QColor &color)
{
_markerColor = color;
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setMarkerColor(color);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMarkerColor(color);
}
void MapView::setMarkerPosition(qreal pos)
{
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setMarkerPosition(pos);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMarkerPosition(pos);
}
void MapView::reloadMap()
2017-10-04 23:15:39 +02:00
{
2018-08-18 21:06:36 +02:00
_scene->invalidate();
}
void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
2018-08-18 21:06:36 +02:00
{
if (_deviceRatio == deviceRatio && _mapRatio == mapRatio)
2018-08-18 21:06:36 +02:00
return;
_deviceRatio = deviceRatio;
_mapRatio = mapRatio;
2018-08-18 21:06:36 +02:00
QRectF vr(mapToScene(viewport()->rect()).boundingRect()
.intersected(_map->bounds()));
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
2018-08-18 21:06:36 +02:00
_scene->setSceneRect(_map->bounds());
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setMap(_map);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMap(_map);
2019-01-31 01:46:53 +01:00
for (int i = 0; i < _areas.size(); i++)
_areas.at(i)->setMap(_map);
2018-08-18 21:06:36 +02:00
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(_map);
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
2018-08-18 21:06:36 +02:00
it.value()->setMap(_map);
updatePOIVisibility();
QPointF nc = QRectF(_map->ll2xy(cr.topLeft()),
_map->ll2xy(cr.bottomRight())).center();
centerOn(nc);
reloadMap();
2017-10-04 23:15:39 +02:00
}
2021-06-17 21:58:25 +02:00
void MapView::setOutputProjection(const Projection &proj)
{
2021-06-17 21:58:25 +02:00
_outputProjection = proj;
Coordinates center = _map->xy2ll(mapToScene(viewport()->rect().center()));
_map->setOutputProjection(_outputProjection);
rescale();
centerOn(_map->ll2xy(center));
}
2021-06-17 21:58:25 +02:00
void MapView::setInputProjection(const Projection &proj)
{
2021-06-17 21:58:25 +02:00
_inputProjection = proj;
Coordinates center = _map->xy2ll(mapToScene(viewport()->rect().center()));
_map->setInputProjection(_inputProjection);
rescale();
centerOn(_map->ll2xy(center));
}
void MapView::fitContentToSize()
{
int zoom = _map->zoom();
if (fitMapZoom() != zoom)
rescale();
centerOn(contentCenter());
}
2021-09-01 13:08:34 +02:00
RectC MapView::boundingRect() const
{
RectC rect;
if (_showTracks)
rect |= _tr;
if (_showRoutes)
rect |= _rr;
if (_showWaypoints)
rect |= _wr;
if (_showAreas)
rect |= _ar;
return rect;
}