Предварительно.
Сделал очень-очень простой вьювер на тайловой основе. Пока только без карты. Умеет изменять масштаб, показывать координаты, переходить или центрировать вид по координатам. В общих чертах я понял, как примерно строятся "Вьюверы"...
Теперь нужно отработать технологию добавления самой карты, тех же самых тайлов того же самого OSM...
---------
Ссылки, которые мне помогли:
---------
http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
---------
http://doc.crossplatform.ru/qt/4.5.0/qgraphicsview.html
http://doc.crossplatform.ru/qt/4.5.0/qg ... scene.html
http://doc.crossplatform.ru/qt/4.5.0/qgraphicsitem.html
---------
http://www.medieninf.de/qmapcontrol/
---------
https://bitbucket.org/maproom/qmapshack/wiki/Home
http://qlandkarte.sourceforge.net/#
----------------------------------Код--[Qt5.4 + MSVS C++ 2013]--------------------------------
tilebase.h
Код: Выделить всё
#ifndef TILEBASE_H
#define TILEBASE_H
#include <QObject>
// Класс для управления тайлами
class TileBase : public QObject
{
Q_OBJECT
public:
TileBase(QObject *parent = 0);
~TileBase();
// Количество тайлов по оси в зависимости от масштаба
quint64 tilesOnZoomLevel(quint8 zoomLevel) const;
quint8 getMinZoomLevel();
quint8 getMaxZoomLevel();
quint16 getTileSize() const;
// Конвертация географических координат в пиксельные
QPointF lonlat2pix(const QPointF &lonlat, quint8 zoomLevel) const;
// Конвертация пиксельных координат в географические
QPointF pix2lonlat(const QPointF &pix, quint8 zoomLevel) const;
protected:
const qreal pi;
const qreal deg2rad;
const qreal rad2deg;
const quint8 minZoomLevel;
const quint8 maxZoomLevel;
const quint16 tileSize;
};
#endif // TILEBASE_H
tilebase.cpp
Код: Выделить всё
#include "stdafx.h"
#include "tilebase.h"
TileBase::TileBase(QObject *parent)
: QObject(parent),
pi(3.14159265358979323846), deg2rad(pi / 180.0), rad2deg(180.0 / pi),
minZoomLevel(0), maxZoomLevel(18), tileSize(256)
{
qDebug() << "TileBase::TileBase() ctor";
}
TileBase::~TileBase()
{
qDebug() << "TileBase::TileBase() dtor";
}
quint64 TileBase::tilesOnZoomLevel(quint8 zoomLevel) const
{
qDebug() << "TileBase::tilesOnZoomLevel()";
return pow(2.0, zoomLevel);
}
QPointF TileBase::lonlat2pix(const QPointF &lonlat, quint8 zoomLevel) const
{
qDebug() << "TileBase::lonlat2pix()";
const qreal edge = tilesOnZoomLevel(zoomLevel);
qreal x = (lonlat.x() + 180) * (edge * tileSize) / 360;
qreal y = (1 - (log(tan(pi / 4 + (lonlat.y() * deg2rad) / 2)) / pi)) / 2 * (edge * tileSize);
return QPoint(int(x), int(y));
}
QPointF TileBase::pix2lonlat(const QPointF &pix, quint8 zoomLevel) const
{
qDebug() << "TileBase::pix2lonlat()";
const qreal edge = tilesOnZoomLevel(zoomLevel);
qreal longitude = (pix.x() * (360 / (edge * tileSize))) - 180;
qreal latitude = rad2deg * (atan(sinh((1 - pix.y() * (2 / (edge * tileSize))) * pi)));
return QPointF(longitude, latitude);
}
quint8 TileBase::getMinZoomLevel()
{
qDebug() << "TileBase::getMinZoomLevel()";
return minZoomLevel;
}
quint8 TileBase::getMaxZoomLevel()
{
qDebug() << "TileBase::getMaxZoomLevel()";
return maxZoomLevel;
}
quint16 TileBase::getTileSize() const
{
qDebug() << "TileBase::getTileSize()";
return tileSize;
}
qgscene.h
Код: Выделить всё
#ifndef QGSCENE_H
#define QGSCENE_H
class QGraphicsScene;
// Просто сцена
class QGScene : public QGraphicsScene
{
Q_OBJECT
public:
QGScene(QWidget *parent = 0);
~QGScene();
};
#endif // QGSCENE_H
qgscene.cpp
Код: Выделить всё
#include "stdafx.h"
#include "qgscene.h"
#include <QGraphicsScene>
QGScene::QGScene(QWidget *parent)
: QGraphicsScene(parent)
{
qDebug() << "QGScene::QGScene() ctor";
}
QGScene::~QGScene()
{
qDebug() << "QGScene::QGScene() dtor";
}
qgviewer.h
Код: Выделить всё
#ifndef QGVIEWER_H
#define QGVIEWER_H
class QGraphicsView;
class QGScene;
class QGObject;
class TileBase;
// Класс вида - управляет картой
class QGViewer : public QGraphicsView
{
Q_OBJECT
public:
// Режим масштабирования
enum ZoomMode
{
CenterZoom, // Масштаб по центру
MouseZoom // Масштаб управляется мышью
};
QGViewer(QGScene *scene = 0, QWidget *parent = 0);
~QGViewer();
// Установка тайловой соновы
void setTileBase(TileBase *base);
// Установка масштаба
void setZoomLevel(quint8 zoom, ZoomMode mode = CenterZoom);
// Центрирует карту в нужной точке
void centerPos(const QPointF &pos);
private:
// Возвращает центр вида в географических координатах
QPointF getCenter(const QPoint viewPos) const;
// Изменят размер сцены в зависимости от масштаба
void changeSceneSize();
// Масштаб больше
void zoomIn(ZoomMode mode = CenterZoom);
// Масштаб меньше
void zoomOut(ZoomMode mode = CenterZoom);
protected:
// События мыши
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *event);
signals:
// Для вывода масштаба в строку статуса
void zoomLevelChanged(quint8 zoom);
// Для вывода координат в строку статуса
void mousePosition(QPointF point);
private:
QGScene *scene; // Сцена
TileBase *tileBase; // Тайловая основа
quint8 zoomLevel; // Масштаб
};
#endif // QGVIEWER_H
qgviewer.cpp
Код: Выделить всё
#include "stdafx.h"
#include "qgviewer.h"
#include "qgscene.h"
#include "tilebase.h"
#include <QGraphicsView>
QGViewer::QGViewer(QGScene *scene, QWidget *parent)
: QGraphicsView(scene, parent), scene(scene),
zoomLevel(0)
{
qDebug() << "QGViewer::QGViewer() ctor";
//this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->setMouseTracking(true);
this->setResizeAnchor(QGraphicsView::AnchorViewCenter);
this->setDragMode(QGraphicsView::ScrollHandDrag);
}
QGViewer::~QGViewer()
{
qDebug() << "QGViewer::QGViewer() dtor";
}
void QGViewer::setTileBase(TileBase *base)
{
qDebug() << "QGViewer::setTileBase()";
tileBase = base;
}
void QGViewer::setZoomLevel(quint8 zoom, ZoomMode mode)
{
qDebug() << "QGViewer::setZoomLevel() zoom:" << zoom;
const QPointF centerGeoPos = getCenter(QPoint(this->width() / 2, this->height() / 2));
qDebug() << "QGViewer::setZoomLevel() centerGeoPos:" << centerGeoPos;
QPointF mousePoint = this->mapToScene(this->mapFromGlobal(QCursor::pos()));
qDebug() << "QGViewer::setZoomLevel() mousePoint:" << mousePoint;
QRectF sceneRect = scene->sceneRect();
const float xRatio = mousePoint.x() / sceneRect.width();
const float yRatio = mousePoint.y() / sceneRect.height();
qDebug() << "QGViewer::setZoomLevel() xRatio:" << xRatio << "yRatio" << yRatio;
const QPointF centerPixPos = this->mapToScene(QPoint(this->width() / 2, this->height() / 2));
qDebug() << "QGViewer::setZoomLevel() centerPixPos:" << centerPixPos;
const QPointF offset = mousePoint - centerPixPos;
qDebug() << "QGViewer::setZoomLevel() offset:" << offset;
zoom = qMin(tileBase->getMaxZoomLevel(), qMax(tileBase->getMinZoomLevel(), zoom));
qDebug() << "QGViewer::setZoomLevel() zoom:" << zoom;
if (zoom == zoomLevel)
return;
zoomLevel = zoom;
changeSceneSize();
sceneRect = scene->sceneRect();
mousePoint = QPointF(sceneRect.width() * xRatio, sceneRect.height() * yRatio) - offset;
qDebug() << "QGViewer::setZoomLevel() mousePoint:" << mousePoint;
if (mode == MouseZoom)
{
qDebug() << "QGViewer::setZoomLevel() zMode = MouseZoom";
this->centerOn(mousePoint);
}
else
{
qDebug() << "QGViewer::setZoomLevel() zMode = CenterZoom";
centerPos(centerGeoPos);
}
emit zoomLevelChanged(zoom);
}
void QGViewer::centerPos(const QPointF &pos)
{
qDebug() << "QGViewer::centerPos()";
this->centerOn(tileBase->lonlat2pix(pos, zoomLevel));
}
QPointF QGViewer::getCenter(const QPoint viewPos) const
{
qDebug() << "QGViewer::getCenter()";
return tileBase->pix2lonlat(this->mapToScene(viewPos), zoomLevel);
}
void QGViewer::changeSceneSize()
{
qDebug() << "QGViewer::changeSceneSize()";
const quint64 dimension = tileBase->tilesOnZoomLevel(zoomLevel) * tileBase->getTileSize();
if (scene->sceneRect().width() != dimension)
scene->setSceneRect(0, 0, dimension, dimension);
qDebug() << "QGViewer::changeSceneSize() sceneRect:" << scene->sceneRect();
}
void QGViewer::zoomIn(ZoomMode mode)
{
qDebug() << "QGViewer::zoomIn()";
if (zoomLevel < tileBase->getMaxZoomLevel())
setZoomLevel(zoomLevel + 1, mode);
}
void QGViewer::zoomOut(ZoomMode mode)
{
qDebug() << "QGViewer::zoomOut()";
if (zoomLevel > tileBase->getMinZoomLevel())
setZoomLevel(zoomLevel - 1, mode);
}
void QGViewer::mousePressEvent(QMouseEvent *event)
{
qDebug() << "QGViewer::mousePressEvent()";
event->setAccepted(false);
if (!event->isAccepted())
QGraphicsView::mousePressEvent(event);
}
void QGViewer::mouseReleaseEvent(QMouseEvent *event)
{
qDebug() << "QGViewer::mouseReleaseEvent()";
event->setAccepted(false);
if (!event->isAccepted())
QGraphicsView::mouseReleaseEvent(event);
}
void QGViewer::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << "QGViewer::mouseMoveEvent()";
event->setAccepted(false);
emit mousePosition(getCenter(event->pos()));
if (!event->isAccepted())
QGraphicsView::mouseMoveEvent(event);
}
void QGViewer::wheelEvent(QWheelEvent *event)
{
qDebug() << "QGViewer::wheelEvent()";
event->setAccepted(true);
if (event->delta() > 0)
zoomIn(MouseZoom);
else
zoomOut(MouseZoom);
if (!event->isAccepted())
QGraphicsView::wheelEvent(event);
}
viewer.h
Код: Выделить всё
#ifndef VIEWER_H
#define VIEWER_H
#include <QtWidgets/QMainWindow>
#include "ui_viewer.h"
class QStatusBar;
class QLabel;
class QGScene;
class QGViewer;
// Главный класс программы
class Viewer : public QMainWindow, public Ui::ViewerClass
{
Q_OBJECT
public:
Viewer(QWidget *parent = 0);
~Viewer();
private slots :
// Показывает уровень масштаба в строке статуса
void zoomLevelChanged(quint8 zoom);
// Показывает гео.координаты мыши в строке статуса
void mousePosition(QPointF point);
private:
QStatusBar *statusBar;
QLabel *labelZoomLevel;
QLabel *labelPosition;
QGScene *scene;
QGViewer *viewer;
};
#endif // VIEWER_H
viewer.cpp
Код: Выделить всё
#include "stdafx.h"
#include "viewer.h"
#include "qgscene.h"
#include "qgviewer.h"
#include "tilebase.h"
#include <QStatusBar>
#include <QLabel>
Viewer::Viewer(QWidget *parent)
: QMainWindow(parent)
{
qDebug() << "Viewer::Viewer() ctor";
this->setupUi(this);
statusBar = new QStatusBar(this);
statusBar->setFont(QFont("Arial", 8));
this->setStatusBar(statusBar);
labelZoomLevel = new QLabel();
labelZoomLevel->setFont(QFont("Arial", 8));
statusBar->addWidget(labelZoomLevel);
labelPosition = new QLabel();
labelPosition->setFont(QFont("Arial", 8));
statusBar->addWidget(labelPosition);
scene = new QGScene(this);
viewer = new QGViewer(scene, this);
TileBase *tileBase = new TileBase();
connect(viewer, SIGNAL(mousePosition(QPointF)), this, SLOT(mousePosition(QPointF)));
connect(viewer, SIGNAL(zoomLevelChanged(quint8)), this, SLOT(zoomLevelChanged(quint8)));
viewer->setTileBase(tileBase);
viewer->setZoomLevel(8);
viewer->centerPos(QPointF(142.500000, 46.500000));
labelPosition->setText(QString("lat: 46.500000 | lon: 142.500000"));
this->gridLayout->addWidget(viewer);
}
Viewer::~Viewer()
{
qDebug() << "Viewer::Viewer() dtor";
}
void Viewer::zoomLevelChanged(quint8 zoom)
{
labelZoomLevel->setText(QString("zoom: %1").arg(zoom));
}
void Viewer::mousePosition(QPointF point)
{
labelPosition->setText(QString("lat: %1 | lon: %2").arg(point.y(), 0, 'd', 6).arg(point.x(), 0, 'd', 6));
}