Предварительно.
Сделал очень-очень простой вьювер на тайловой основе. Пока только без карты. Умеет изменять масштаб, показывать координаты, переходить или центрировать вид по координатам. В общих чертах я понял, как примерно строятся "Вьюверы"...
Теперь нужно отработать технологию добавления самой карты, тех же самых тайлов того же самого 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));
}