1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-06 23:03:22 +02:00
GPXSee/src/trackview.cpp

501 lines
12 KiB
C++
Raw Normal View History

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPainterPath>
#include <QWheelEvent>
2016-02-12 10:23:14 +01:00
#include "poi.h"
#include "gpx.h"
2016-02-12 10:09:17 +01:00
#include "map.h"
2016-02-19 21:34:55 +01:00
#include "waypointitem.h"
#include "markeritem.h"
#include "scaleitem.h"
#include "ll.h"
#include "trackview.h"
#define MARGIN 10.0
#define TRACK_WIDTH 3
#define SCALE_OFFSET 7
TrackView::TrackView(QWidget *parent)
: QGraphicsView(parent)
{
_scene = new QGraphicsScene(this);
setScene(_scene);
setCacheMode(QGraphicsView::CacheBackground);
setDragMode(QGraphicsView::ScrollHandDrag);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2016-02-28 10:58:25 +01:00
setRenderHints(QPainter::Antialiasing);
_mapScale = new ScaleItem();
_mapScale->setZValue(2.0);
2016-03-15 01:20:24 +01:00
_zoom = ZOOM_MAX;
_scale = mapScale(_zoom);
_map = 0;
_maxPath = 0;
_maxDistance = 0;
}
TrackView::~TrackView()
{
2016-05-12 09:03:05 +02:00
if (_mapScale->scene() != _scene)
delete _mapScale;
}
2016-03-03 09:15:56 +01:00
void TrackView::addTrack(const QVector<QPointF> &track)
{
2016-03-03 09:15:56 +01:00
QPainterPath path;
QGraphicsPathItem *pi;
MarkerItem *mi;
2016-03-23 20:49:40 +01:00
if (track.size() < 2) {
_palette.color();
2016-03-03 09:15:56 +01:00
return;
2016-03-23 20:49:40 +01:00
}
2016-03-03 09:15:56 +01:00
_tracks.append(track);
2016-03-19 09:06:43 +01:00
const QPointF &p = track.at(0);
path.moveTo(ll2mercator(QPointF(p.x(), -p.y())));
for (int i = 1; i < track.size(); i++) {
const QPointF &p = track.at(i);
path.lineTo(ll2mercator(QPointF(p.x(), -p.y())));
}
_maxPath = qMax(path.length(), _maxPath);
2016-03-03 09:15:56 +01:00
pi = new QGraphicsPathItem(path);
_paths.append(pi);
2016-03-15 01:20:24 +01:00
_zoom = qMin(_zoom, scale2zoom(trackScale()));
_scale = mapScale(_zoom);
2016-03-03 19:45:04 +01:00
QBrush brush(_palette.color(), Qt::SolidPattern);
2016-03-03 09:15:56 +01:00
QPen pen(brush, TRACK_WIDTH * _scale);
pi->setPen(pen);
pi->setScale(1.0/_scale);
_scene->addItem(pi);
2016-03-03 09:15:56 +01:00
mi = new MarkerItem(pi);
_markers.append(mi);
mi->setPos(pi->path().pointAtPercent(0));
mi->setScale(_scale);
}
2016-03-15 01:20:24 +01:00
void TrackView::addWaypoints(const QList<Waypoint> &waypoints)
{
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(
2016-03-19 09:06:43 +01:00
Waypoint(ll2mercator(QPointF(w.coordinates().x(),
-w.coordinates().y())), w.description()));
2016-03-15 01:20:24 +01:00
wi->setPos(wi->entry().coordinates() * 1.0/_scale);
wi->setZValue(1);
_scene->addItem(wi);
_locations.append(wi);
2016-03-19 09:06:43 +01:00
_waypoints.append(w.coordinates());
2016-03-15 01:20:24 +01:00
}
_zoom = qMin(_zoom, scale2zoom(waypointScale()));
_scale = mapScale(_zoom);
}
2016-03-03 09:15:56 +01:00
void TrackView::loadGPX(const GPX &gpx)
{
2016-03-15 01:20:24 +01:00
int zoom = _zoom;
2016-03-03 09:15:56 +01:00
for (int i = 0; i < gpx.trackCount(); i++) {
QVector<QPointF> track;
gpx.track(i).track(track);
addTrack(track);
_maxDistance = qMax(gpx.track(i).distance(), _maxDistance);
2016-03-03 09:15:56 +01:00
}
2016-03-15 01:20:24 +01:00
addWaypoints(gpx.waypoints());
if (_paths.empty() && _locations.empty())
2016-03-03 09:15:56 +01:00
return;
2016-03-15 01:20:24 +01:00
if ((_paths.size() > 1 && _zoom < zoom)
|| (_locations.size() && _zoom < zoom))
2016-03-03 09:15:56 +01:00
rescale(_scale);
2016-03-15 01:20:24 +01:00
QRectF br = trackBoundingRect() | waypointBoundingRect();
2016-03-03 09:15:56 +01:00
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba);
centerOn(ba.center());
2016-03-03 09:15:56 +01:00
_mapScale->setLatitude(-(br.center().ry() * _scale));
_mapScale->setZoom(_zoom);
if (_mapScale->scene() != _scene)
_scene->addItem(_mapScale);
}
QRectF TrackView::trackBoundingRect() const
{
if (_paths.empty())
2016-03-03 09:15:56 +01:00
return QRectF();
QRectF br = _paths.at(0)->sceneBoundingRect();
for (int i = 1; i < _paths.size(); i++)
br |= _paths.at(i)->sceneBoundingRect();
return br;
}
2016-03-15 01:20:24 +01:00
QRectF TrackView::waypointBoundingRect() const
{
qreal bottom, top, left, right;
if (_locations.empty())
2016-03-15 01:20:24 +01:00
return QRectF();
const QPointF &p = _locations.at(0)->pos();
bottom = p.y();
top = p.y();
left = p.x();
right = p.x();
for (int i = 1; i < _locations.size(); i++) {
const QPointF &p = _locations.at(i)->pos();
bottom = qMax(bottom, p.y());
top = qMin(top, p.y());
right = qMax(right, p.x());
left = qMin(left, p.x());
2016-03-15 01:20:24 +01:00
}
return QRectF(QPointF(left, top), QPointF(right, bottom));
}
qreal TrackView::trackScale() const
{
if (_paths.empty())
2016-03-15 01:20:24 +01:00
return mapScale(ZOOM_MAX);
QRectF br = _paths.at(0)->path().boundingRect();
for (int i = 1; i < _paths.size(); i++)
br |= _paths.at(i)->path().boundingRect();
QPointF sc(br.width() / (viewport()->width() - MARGIN/2),
br.height() / (viewport()->height() - MARGIN/2));
return qMax(sc.x(), sc.y());
}
2016-03-15 01:20:24 +01:00
qreal TrackView::waypointScale() const
{
2016-03-15 01:20:24 +01:00
qreal bottom, top, left, right;
if (_locations.size() < 2)
2016-03-15 01:20:24 +01:00
return mapScale(ZOOM_MAX);
const QPointF &p = _locations.at(0)->entry().coordinates();
bottom = p.y();
top = p.y();
left = p.x();
right = p.x();
for (int i = 1; i < _locations.size(); i++) {
const QPointF &p = _locations.at(i)->entry().coordinates();
bottom = qMax(bottom, p.y());
top = qMin(top, p.y());
right = qMax(right, p.x());
left = qMin(left, p.x());
2016-03-15 01:20:24 +01:00
}
QRectF br(QPointF(left, top), QPointF(right, bottom));
QPointF sc(br.width() / (viewport()->width() - MARGIN/2),
br.height() / (viewport()->height() - MARGIN/2));
return qMax(sc.x(), sc.y());
}
qreal TrackView::mapScale(int zoom) const
{
return ((360.0/(qreal)(1<<zoom))/(qreal)TILE_SIZE);
}
void TrackView::rescale(qreal scale)
{
for (int i = 0; i < _paths.size(); i++) {
_markers.at(i)->setScale(scale);
_paths.at(i)->setScale(1.0/scale);
QPen pen(_paths.at(i)->pen());
pen.setWidthF(TRACK_WIDTH * scale);
_paths.at(i)->setPen(pen);
}
for (int i = 0; i < _locations.size(); i++)
_locations.at(i)->setPos(_locations.at(i)->entry().coordinates()
2016-03-15 01:20:24 +01:00
* 1.0/scale);
2016-02-19 21:42:54 +01:00
QHash<Waypoint, WaypointItem*>::const_iterator it, jt;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
it.value()->setPos(it.value()->entry().coordinates() * 1.0/scale);
it.value()->show();
}
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
for (jt = _pois.constBegin(); jt != _pois.constEnd(); jt++) {
if (it != jt && it.value()->isVisible() && jt.value()->isVisible()
&& it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
_scale = scale;
}
void TrackView::addPOI(const QVector<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(w))
continue;
WaypointItem *pi = new WaypointItem(
2016-03-19 09:06:43 +01:00
Waypoint(ll2mercator(QPointF(w.coordinates().x(),
-w.coordinates().y())), w.description()));
pi->setPos(pi->entry().coordinates() * 1.0/_scale);
pi->setZValue(1);
_scene->addItem(pi);
2016-03-19 09:06:43 +01:00
_pois.insert(w, pi);
}
}
void TrackView::loadPOI(const POI &poi)
{
QHash<Waypoint, WaypointItem*>::const_iterator it,jt;
if (!_tracks.size() && !_waypoints.size())
return;
for (int i = 0; i < _tracks.size(); i++)
addPOI(poi.points(_tracks.at(i)));
addPOI(poi.points(_waypoints));
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
for (jt = _pois.constBegin(); jt != _pois.constEnd(); jt++) {
if (it != jt && it.value()->isVisible() && jt.value()->isVisible()
&& it.value()->collidesWithItem(jt.value()))
jt.value()->hide();
}
}
}
void TrackView::setMap(Map *map)
{
_map = map;
if (_map)
connect(_map, SIGNAL(loaded()), this, SLOT(redraw()));
resetCachedContent();
}
void TrackView::setUnits(enum Units units)
{
_mapScale->setUnits(units);
}
void TrackView::redraw()
{
resetCachedContent();
}
2016-04-05 09:10:19 +02:00
void TrackView::rescale()
{
_zoom = qMin(scale2zoom(trackScale()), scale2zoom(waypointScale()));
rescale(mapScale(_zoom));
_mapScale->setZoom(_zoom);
}
void TrackView::wheelEvent(QWheelEvent *event)
{
if (_paths.isEmpty() && _locations.isEmpty())
return;
QPointF pos = mapToScene(event->pos());
qreal scale = _scale;
_zoom = (event->delta() > 0) ?
qMin(_zoom + 1, ZOOM_MAX) : qMax(_zoom - 1, ZOOM_MIN);
2016-03-15 01:20:24 +01:00
rescale(mapScale(_zoom));
QRectF br = trackBoundingRect() | waypointBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba);
if (br.width() < viewport()->size().width()
&& br.height() < viewport()->size().height())
centerOn(br.center());
else
centerOn(pos * scale/_scale);
_mapScale->setZoom(_zoom);
resetCachedContent();
}
void TrackView::setTrackLineWidth(qreal width)
{
for (int i = 0; i < _paths.size(); i++) {
QPen pen(_paths.at(i)->pen());
pen.setWidthF(width);
_paths.at(i)->setPen(pen);
}
}
void TrackView::plot(QPainter *painter, const QRectF &target)
{
QRectF orig, adj;
qreal ratio, diff;
2016-05-15 13:19:07 +02:00
orig = viewport()->rect();
if (target.width()/target.height() > orig.width()/orig.height()) {
ratio = target.width()/target.height();
diff = qAbs((orig.height() * ratio) - orig.width());
adj = orig.adjusted(-diff/2, 0, diff/2, 0);
} else {
ratio = target.height()/target.width();
2016-05-15 13:19:07 +02:00
diff = qAbs((orig.width() * ratio) - orig.height());
adj = orig.adjusted(0, -diff/2, 0, diff/2);
}
2016-05-15 13:19:07 +02:00
_mapScale->setPos(mapToScene(QPointF(adj.bottomRight()
+ QPoint(-_mapScale->boundingRect().width(),
2016-05-15 13:19:07 +02:00
-_mapScale->boundingRect().height())).toPoint()));
2016-05-15 13:19:07 +02:00
render(painter, target, adj.toRect());
}
enum QPrinter::Orientation TrackView::orientation() const
{
return (sceneRect().width() > sceneRect().height())
? QPrinter::Landscape : QPrinter::Portrait;
}
void TrackView::clearPOI()
{
2016-02-19 21:42:54 +01:00
QHash<Waypoint, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
_scene->removeItem(it.value());
delete it.value();
}
_pois.clear();
}
void TrackView::clear()
{
if (_mapScale->scene() == _scene)
_scene->removeItem(_mapScale);
_pois.clear();
_paths.clear();
_locations.clear();
_markers.clear();
_scene->clear();
2016-03-02 09:34:39 +01:00
_palette.reset();
_tracks.clear();
_waypoints.clear();
_maxPath = 0;
_maxDistance = 0;
2016-03-15 01:20:24 +01:00
_zoom = ZOOM_MAX;
_scale = mapScale(_zoom);
2016-03-03 09:15:56 +01:00
_scene->setSceneRect(QRectF());
}
void TrackView::movePositionMarker(qreal val)
{
qreal mp = val / _maxDistance;
for (int i = 0; i < _paths.size(); i++) {
qreal f = _maxPath / _paths.at(i)->path().length();
2016-03-30 20:50:51 +02:00
if (mp * f < 0 || mp * f > 1.0)
_markers.at(i)->setVisible(false);
else {
QPointF pos = _paths.at(i)->path().pointAtPercent(mp * f);
_markers.at(i)->setPos(pos);
_markers.at(i)->setVisible(true);
}
}
}
void TrackView::drawBackground(QPainter *painter, const QRectF &rect)
{
if ((_paths.isEmpty() && _locations.isEmpty())|| !_map) {
painter->fillRect(rect, Qt::white);
return;
}
QRectF rr(rect.topLeft() * _scale, rect.size());
QPoint tile = mercator2tile(QPointF(rr.topLeft().x(), -rr.topLeft().y()),
_zoom);
QPointF tm = tile2mercator(tile, _zoom);
2016-05-15 13:19:07 +02:00
QPoint tl = mapToScene(mapFromScene(QPointF(tm.x() / _scale,
-tm.y() / _scale))).toPoint();
QList<Tile> tiles;
for (int i = 0; i <= rr.size().width() / TILE_SIZE + 1; i++) {
for (int j = 0; j <= rr.size().height() / TILE_SIZE + 1; j++) {
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom));
}
}
_map->loadTiles(tiles);
for (int i = 0; i < tiles.count(); i++) {
Tile &t = tiles[i];
QPoint tp(tl.x() + (t.xy().rx() - tile.rx()) * TILE_SIZE,
tl.y() + (t.xy().ry() - tile.ry()) * TILE_SIZE);
painter->drawPixmap(tp, t.pixmap());
}
}
void TrackView::resizeEvent(QResizeEvent *e)
{
if (_paths.isEmpty() && _locations.isEmpty())
return;
2016-04-05 09:10:19 +02:00
rescale();
2016-03-15 01:20:24 +01:00
QRectF br = trackBoundingRect() | waypointBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
if (ba.width() < e->size().width()) {
qreal diff = e->size().width() - ba.width();
ba.adjust(-diff/2, 0, diff/2, 0);
}
if (ba.height() < e->size().height()) {
qreal diff = e->size().height() - ba.height();
ba.adjust(0, -diff/2, 0, diff/2);
}
_scene->setSceneRect(ba);
centerOn(br.center());
resetCachedContent();
}
void TrackView::paintEvent(QPaintEvent *e)
{
QPointF scenePos = mapToScene(rect().bottomLeft() + QPoint(SCALE_OFFSET,
-(SCALE_OFFSET + _mapScale->boundingRect().height())));
if (_mapScale->pos() != scenePos)
_mapScale->setPos(scenePos);
QGraphicsView::paintEvent(e);
}