mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-24 03:35:53 +01:00
Added initial WMTS support
This commit is contained in:
parent
3202fc4c15
commit
1bc4833a81
10
gpxsee.pro
10
gpxsee.pro
@ -116,7 +116,10 @@ HEADERS += src/config.h \
|
||||
src/map/primemeridian.h \
|
||||
src/map/linearunits.h \
|
||||
src/map/ct.h \
|
||||
src/map/mapsource.h
|
||||
src/map/mapsource.h \
|
||||
src/map/tileloader.h \
|
||||
src/map/wmtsmap.h \
|
||||
src/map/wmts.h
|
||||
SOURCES += src/main.cpp \
|
||||
src/common/coordinates.cpp \
|
||||
src/common/rectc.cpp \
|
||||
@ -202,7 +205,10 @@ SOURCES += src/main.cpp \
|
||||
src/map/angularunits.cpp \
|
||||
src/map/primemeridian.cpp \
|
||||
src/map/linearunits.cpp \
|
||||
src/map/mapsource.cpp
|
||||
src/map/mapsource.cpp \
|
||||
src/map/tileloader.cpp \
|
||||
src/map/wmtsmap.cpp \
|
||||
src/map/wmts.cpp
|
||||
RESOURCES += gpxsee.qrc
|
||||
TRANSLATIONS = lang/gpxsee_cs.ts \
|
||||
lang/gpxsee_sv.ts \
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include <QFileOpenEvent>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QLibraryInfo>
|
||||
#include "map/onlinemap.h"
|
||||
#include "map/wmts.h"
|
||||
#include "map/tileloader.h"
|
||||
#include "map/downloader.h"
|
||||
#include "map/ellipsoid.h"
|
||||
#include "map/gcs.h"
|
||||
@ -36,7 +37,9 @@ App::App(int &argc, char **argv) : QApplication(argc, argv),
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
OnlineMap::setDownloader(new Downloader(this));
|
||||
Downloader *dl = new Downloader(this);
|
||||
TileLoader::setDownloader(dl);
|
||||
WMTS::setDownloader(dl);
|
||||
OPENGL_SET_SAMPLES(4);
|
||||
loadDatums();
|
||||
loadPCSs();
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
void unload();
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
const QString &errorString() const {return _errorString;}
|
||||
QString errorString() const {return _errorString;}
|
||||
|
||||
static bool isAtlas(const QString &path);
|
||||
|
||||
|
@ -61,7 +61,7 @@ bool Downloader::saveToDisk(const QString &filename, QIODevice *data)
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qWarning("Error writing map tile: %s: %s\n",
|
||||
qWarning("Error writing file: %s: %s\n",
|
||||
qPrintable(filename), qPrintable(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
@ -80,11 +80,11 @@ void Downloader::downloadFinished(QNetworkReply *reply)
|
||||
QUrl origin = reply->request().attribute(ATTR_ORIGIN).toUrl();
|
||||
if (origin.isEmpty()) {
|
||||
_errorDownloads.insert(url);
|
||||
qWarning("Error downloading map tile: %s: %s\n",
|
||||
qWarning("Error downloading file: %s: %s\n",
|
||||
url.toEncoded().constData(), qPrintable(reply->errorString()));
|
||||
} else {
|
||||
_errorDownloads.insert(origin);
|
||||
qWarning("Error downloading map tile: %s -> %s: %s\n",
|
||||
qWarning("Error downloading file: %s -> %s: %s\n",
|
||||
origin.toEncoded().constData(), url.toEncoded().constData(),
|
||||
qPrintable(reply->errorString()));
|
||||
}
|
||||
@ -99,11 +99,11 @@ void Downloader::downloadFinished(QNetworkReply *reply)
|
||||
|
||||
if (location == url) {
|
||||
_errorDownloads.insert(url);
|
||||
qWarning("Error downloading map tile: %s: "
|
||||
qWarning("Error downloading file: %s: "
|
||||
"redirect loop\n", url.toEncoded().constData());
|
||||
} else if (level >= MAX_REDIRECT_LEVEL) {
|
||||
_errorDownloads.insert(origin);
|
||||
qWarning("Error downloading map tile: %s: "
|
||||
qWarning("Error downloading file: %s: "
|
||||
"redirect level limit reached\n",
|
||||
origin.toEncoded().constData());
|
||||
} else {
|
||||
|
@ -40,6 +40,9 @@ public:
|
||||
|
||||
void setBackgroundColor(const QColor &color) {_backgroundColor = color;}
|
||||
|
||||
virtual bool isValid() const {return true;}
|
||||
virtual QString errorString() const {return QString();}
|
||||
|
||||
signals:
|
||||
void loaded();
|
||||
|
||||
|
@ -13,7 +13,7 @@ bool MapList::loadSource(const QString &path, bool dir)
|
||||
MapSource ms;
|
||||
Map *map;
|
||||
|
||||
if (!ms.loadFile(path, &map)) {
|
||||
if (!(map = ms.loadFile(path))) {
|
||||
if (dir)
|
||||
_errorString += path + ": " + ms.errorString() + "\n";
|
||||
else
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include "onlinemap.h"
|
||||
#include "wmtsmap.h"
|
||||
#include "mapsource.h"
|
||||
|
||||
|
||||
@ -121,13 +122,17 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
||||
return RectC(Coordinates(left, top), Coordinates(right, bottom));
|
||||
}
|
||||
|
||||
void MapSource::map(QXmlStreamReader &reader, Map **map)
|
||||
Map *MapSource::map(QXmlStreamReader &reader)
|
||||
{
|
||||
QString name, url;
|
||||
QString name, url, format, layer, style, tileMatrixSet;
|
||||
Range z(ZOOM_MIN, ZOOM_MAX);
|
||||
RectC b(Coordinates(BOUNDS_LEFT, BOUNDS_TOP),
|
||||
Coordinates(BOUNDS_RIGHT, BOUNDS_BOTTOM));
|
||||
|
||||
const QXmlStreamAttributes &attr = reader.attributes();
|
||||
bool wmts = (attr.hasAttribute("type") && attr.value("type") == "WMTS")
|
||||
? true : false;
|
||||
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "name")
|
||||
name = reader.readElementText();
|
||||
@ -139,34 +144,53 @@ void MapSource::map(QXmlStreamReader &reader, Map **map)
|
||||
} else if (reader.name() == "bounds") {
|
||||
b = bounds(reader);
|
||||
reader.skipCurrentElement();
|
||||
} else
|
||||
} else if (reader.name() == "format")
|
||||
format = reader.readElementText();
|
||||
else if (reader.name() == "layer")
|
||||
layer = reader.readElementText();
|
||||
else if (reader.name() == "style")
|
||||
style = reader.readElementText();
|
||||
else if (reader.name() == "tilematrixset")
|
||||
tileMatrixSet = reader.readElementText();
|
||||
else
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
|
||||
*map = reader.error() ? 0 : new OnlineMap(name, url, z, b);
|
||||
if (reader.error())
|
||||
return 0;
|
||||
else if (wmts)
|
||||
return new WMTSMap(name, url, format, layer, style, tileMatrixSet);
|
||||
else
|
||||
return new OnlineMap(name, url, z, b);
|
||||
}
|
||||
|
||||
bool MapSource::loadFile(const QString &path, Map **map)
|
||||
Map *MapSource::loadFile(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
QXmlStreamReader reader;
|
||||
Map *map = 0;
|
||||
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
_errorString = file.errorString();
|
||||
return false;
|
||||
return map;
|
||||
}
|
||||
|
||||
reader.setDevice(&file);
|
||||
|
||||
if (reader.readNextStartElement()) {
|
||||
if (reader.name() == "map")
|
||||
MapSource::map(reader, map);
|
||||
map = MapSource::map(reader);
|
||||
else
|
||||
reader.raiseError("Not an online map source file");
|
||||
}
|
||||
|
||||
_errorString = reader.error() ? QString("%1: %2").arg(reader.lineNumber())
|
||||
.arg(reader.errorString()) : QString();
|
||||
|
||||
return !reader.error();
|
||||
if (!map)
|
||||
_errorString = QString("%1: %2").arg(reader.lineNumber())
|
||||
.arg(reader.errorString());
|
||||
else if (!map->isValid()) {
|
||||
_errorString = map->errorString();
|
||||
delete map; map = 0;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
@ -11,13 +11,13 @@ class QXmlStreamReader;
|
||||
class MapSource
|
||||
{
|
||||
public:
|
||||
bool loadFile(const QString &path, Map **map);
|
||||
Map *loadFile(const QString &path);
|
||||
const QString &errorString() const {return _errorString;}
|
||||
|
||||
private:
|
||||
RectC bounds(QXmlStreamReader &reader);
|
||||
Range zooms(QXmlStreamReader &reader);
|
||||
void map(QXmlStreamReader &reader, Map **map);
|
||||
Map *map(QXmlStreamReader &reader);
|
||||
|
||||
QString _errorString;
|
||||
};
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
void unload();
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
const QString &errorString() const {return _errorString;}
|
||||
QString errorString() const {return _errorString;}
|
||||
|
||||
QPointF ll2pp(const Coordinates &c) const
|
||||
{return _projection.ll2xy(c);}
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QPainter>
|
||||
#include "common/coordinates.h"
|
||||
#include "common/rectc.h"
|
||||
@ -41,45 +39,26 @@ static int scale2zoom(qreal scale)
|
||||
return (int)log2(360.0/(scale * (qreal)TILE_SIZE));
|
||||
}
|
||||
|
||||
static bool loadTileFile(Tile &tile, const QString &file)
|
||||
{
|
||||
if (!tile.pixmap().load(file)) {
|
||||
qWarning("%s: error loading tile file\n", qPrintable(file));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Downloader *OnlineMap::downloader;
|
||||
|
||||
OnlineMap::OnlineMap(const QString &name, const QString &url,
|
||||
const Range &zooms, const RectC &bounds, QObject *parent)
|
||||
: Map(parent), _name(name), _url(url), _zooms(zooms), _bounds(bounds)
|
||||
: Map(parent), _name(name), _zooms(zooms), _bounds(bounds)
|
||||
{
|
||||
_block = false;
|
||||
_zoom = _zooms.max();
|
||||
|
||||
QString path = TILES_DIR + QString("/") + name;
|
||||
if (!QDir().mkpath(path))
|
||||
qWarning("Error creating tiles dir: %s\n", qPrintable(path));
|
||||
_tileLoader = TileLoader(url, TILES_DIR + "/" + name);
|
||||
}
|
||||
|
||||
void OnlineMap::load()
|
||||
{
|
||||
connect(downloader, SIGNAL(finished()), this, SLOT(emitLoaded()));
|
||||
connect(TileLoader::downloader(), SIGNAL(finished()), this,
|
||||
SLOT(emitLoaded()));
|
||||
}
|
||||
|
||||
void OnlineMap::unload()
|
||||
{
|
||||
disconnect(downloader, SIGNAL(finished()), this, SLOT(emitLoaded()));
|
||||
}
|
||||
|
||||
void OnlineMap::fillTile(Tile &tile)
|
||||
{
|
||||
tile.pixmap() = QPixmap(TILE_SIZE, TILE_SIZE);
|
||||
tile.pixmap().fill(_backgroundColor);
|
||||
disconnect(TileLoader::downloader(), SIGNAL(finished()), this,
|
||||
SLOT(emitLoaded()));
|
||||
}
|
||||
|
||||
void OnlineMap::emitLoaded()
|
||||
@ -87,91 +66,6 @@ void OnlineMap::emitLoaded()
|
||||
emit loaded();
|
||||
}
|
||||
|
||||
void OnlineMap::loadTilesAsync(QList<Tile> &list)
|
||||
{
|
||||
QList<Download> dl;
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
QString file = tileFile(t);
|
||||
QFileInfo fi(file);
|
||||
|
||||
if (!fi.exists()) {
|
||||
fillTile(t);
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
} else
|
||||
loadTileFile(t, file);
|
||||
}
|
||||
|
||||
if (!dl.empty())
|
||||
downloader->get(dl);
|
||||
}
|
||||
|
||||
void OnlineMap::loadTilesSync(QList<Tile> &list)
|
||||
{
|
||||
QList<Download> dl;
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
QString file = tileFile(t);
|
||||
QFileInfo fi(file);
|
||||
|
||||
if (!fi.exists())
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
else
|
||||
loadTileFile(t, file);
|
||||
}
|
||||
|
||||
if (dl.empty())
|
||||
return;
|
||||
|
||||
QEventLoop wait;
|
||||
connect(downloader, SIGNAL(finished()), &wait, SLOT(quit()));
|
||||
if (downloader->get(dl))
|
||||
wait.exec();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
|
||||
if (t.pixmap().isNull()) {
|
||||
QString file = tileFile(t);
|
||||
QFileInfo fi(file);
|
||||
|
||||
if (!(fi.exists() && loadTileFile(t, file)))
|
||||
fillTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString OnlineMap::tileUrl(const Tile &tile) const
|
||||
{
|
||||
QString url(_url);
|
||||
|
||||
url.replace("$z", QString::number(tile.zoom()));
|
||||
url.replace("$x", QString::number(tile.xy().x()));
|
||||
url.replace("$y", QString::number(tile.xy().y()));
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
QString OnlineMap::tileFile(const Tile &tile) const
|
||||
{
|
||||
QString file = TILES_DIR + QString("/%1/%2-%3-%4").arg(name())
|
||||
.arg(tile.zoom()).arg(tile.xy().x()).arg(tile.xy().y());
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
void OnlineMap::clearCache()
|
||||
{
|
||||
QString path = TILES_DIR + QString("/") + name();
|
||||
QDir dir = QDir(path);
|
||||
QStringList list = dir.entryList();
|
||||
|
||||
for (int i = 0; i < list.count(); i++)
|
||||
dir.remove(list.at(i));
|
||||
}
|
||||
|
||||
QRectF OnlineMap::bounds() const
|
||||
{
|
||||
return QRectF(ll2xy(_bounds.topLeft()), ll2xy(_bounds.bottomRight()));
|
||||
@ -245,14 +139,18 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect)
|
||||
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom));
|
||||
|
||||
if (_block)
|
||||
loadTilesSync(tiles);
|
||||
_tileLoader.loadTilesSync(tiles);
|
||||
else
|
||||
loadTilesAsync(tiles);
|
||||
_tileLoader.loadTilesAsync(tiles);
|
||||
|
||||
for (int i = 0; i < tiles.count(); i++) {
|
||||
Tile &t = tiles[i];
|
||||
QPoint tp(tl.x() + (t.xy().x() - tile.x()) * TILE_SIZE,
|
||||
tl.y() + (t.xy().y() - tile.y()) * TILE_SIZE);
|
||||
if (t.pixmap().isNull())
|
||||
painter->fillRect(QRect(tp, QSize(TILE_SIZE, TILE_SIZE)),
|
||||
_backgroundColor);
|
||||
else
|
||||
painter->drawPixmap(tp, t.pixmap());
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,7 @@
|
||||
#include "common/range.h"
|
||||
#include "common/rectc.h"
|
||||
#include "map.h"
|
||||
#include "tile.h"
|
||||
|
||||
class Downloader;
|
||||
#include "tileloader.h"
|
||||
|
||||
class OnlineMap : public Map
|
||||
{
|
||||
@ -36,10 +34,7 @@ public:
|
||||
void draw(QPainter *painter, const QRectF &rect);
|
||||
|
||||
void setBlockingMode(bool block) {_block = block;}
|
||||
void clearCache();
|
||||
|
||||
static void setDownloader(Downloader *downloader)
|
||||
{OnlineMap::downloader = downloader;}
|
||||
void clearCache() {_tileLoader.clearCache();}
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
@ -50,22 +45,14 @@ private slots:
|
||||
private:
|
||||
QPointF ll2xy(const Coordinates &c) const;
|
||||
Coordinates xy2ll(const QPointF &p) const;
|
||||
|
||||
void fillTile(Tile &tile);
|
||||
QString tileUrl(const Tile &tile) const;
|
||||
QString tileFile(const Tile &tile) const;
|
||||
void loadTilesAsync(QList<Tile> &list);
|
||||
void loadTilesSync(QList<Tile> &list);
|
||||
int limitZoom(int zoom) const;
|
||||
|
||||
TileLoader _tileLoader;
|
||||
QString _name;
|
||||
QString _url;
|
||||
Range _zooms;
|
||||
RectC _bounds;
|
||||
int _zoom;
|
||||
bool _block;
|
||||
|
||||
static Downloader *downloader;
|
||||
};
|
||||
|
||||
#endif // ONLINEMAP_H
|
||||
|
@ -77,9 +77,14 @@ public:
|
||||
|
||||
Projection &operator=(const Projection &p);
|
||||
|
||||
bool isNull() const {return (_gcs == 0 && _ct == 0 && _units.isNull());}
|
||||
bool isValid() const {return (_gcs == 0 || _ct == 0 || _units.isNull());}
|
||||
|
||||
QPointF ll2xy(const Coordinates &c) const;
|
||||
Coordinates xy2ll(const QPointF &p) const;
|
||||
|
||||
const LinearUnits &units() const {return _units;}
|
||||
|
||||
private:
|
||||
const GCS * _gcs;
|
||||
CT *_ct;
|
||||
|
106
src/map/tileloader.cpp
Normal file
106
src/map/tileloader.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QEventLoop>
|
||||
#include "downloader.h"
|
||||
#include "tileloader.h"
|
||||
|
||||
|
||||
static bool loadTileFile(Tile &tile, const QString &file)
|
||||
{
|
||||
if (!tile.pixmap().load(file)) {
|
||||
qWarning("%s: error loading tile file\n", qPrintable(file));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Downloader *TileLoader::_downloader = 0;
|
||||
|
||||
TileLoader::TileLoader(const QString &url, const QString &dir)
|
||||
: _url(url), _dir(dir)
|
||||
{
|
||||
if (!QDir().mkpath(_dir))
|
||||
qWarning("Error creating tiles dir: %s\n", qPrintable(_dir));
|
||||
}
|
||||
|
||||
void TileLoader::loadTilesAsync(QList<Tile> &list)
|
||||
{
|
||||
QList<Download> dl;
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
QString file = tileFile(t);
|
||||
QFileInfo fi(file);
|
||||
|
||||
if (!fi.exists())
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
else
|
||||
loadTileFile(t, file);
|
||||
}
|
||||
|
||||
if (!dl.empty())
|
||||
_downloader->get(dl);
|
||||
}
|
||||
|
||||
void TileLoader::loadTilesSync(QList<Tile> &list)
|
||||
{
|
||||
QList<Download> dl;
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
QString file = tileFile(t);
|
||||
QFileInfo fi(file);
|
||||
|
||||
if (!fi.exists())
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
else
|
||||
loadTileFile(t, file);
|
||||
}
|
||||
|
||||
if (dl.empty())
|
||||
return;
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(_downloader, SIGNAL(finished()), &wait, SLOT(quit()));
|
||||
if (_downloader->get(dl))
|
||||
wait.exec();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
|
||||
if (t.pixmap().isNull()) {
|
||||
QString file = tileFile(t);
|
||||
if (QFileInfo(file).exists())
|
||||
loadTileFile(t, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileLoader::clearCache()
|
||||
{
|
||||
QDir dir = QDir(_dir);
|
||||
QStringList list = dir.entryList();
|
||||
|
||||
for (int i = 0; i < list.count(); i++)
|
||||
dir.remove(list.at(i));
|
||||
}
|
||||
|
||||
QString TileLoader::tileUrl(const Tile &tile) const
|
||||
{
|
||||
QString url(_url);
|
||||
|
||||
url.replace("$z", QString::number(tile.zoom()));
|
||||
url.replace("$x", QString::number(tile.xy().x()));
|
||||
url.replace("$y", QString::number(tile.xy().y()));
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
QString TileLoader::tileFile(const Tile &tile) const
|
||||
{
|
||||
QString file = _dir + QString("/%1-%2-%3").arg(tile.zoom())
|
||||
.arg(tile.xy().x()).arg(tile.xy().y());
|
||||
|
||||
return file;
|
||||
}
|
33
src/map/tileloader.h
Normal file
33
src/map/tileloader.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef TILELOADER_H
|
||||
#define TILELOADER_H
|
||||
|
||||
#include <QString>
|
||||
#include "tile.h"
|
||||
#include "downloader.h"
|
||||
|
||||
class TileLoader
|
||||
{
|
||||
public:
|
||||
TileLoader() {}
|
||||
TileLoader(const QString &url, const QString &dir);
|
||||
|
||||
void loadTilesAsync(QList<Tile> &list);
|
||||
void loadTilesSync(QList<Tile> &list);
|
||||
void clearCache();
|
||||
|
||||
static Downloader *downloader() {return _downloader;}
|
||||
static void setDownloader(Downloader *downloader)
|
||||
{_downloader = downloader;}
|
||||
|
||||
private:
|
||||
void fillTile(Tile &tile);
|
||||
QString tileUrl(const Tile &tile) const;
|
||||
QString tileFile(const Tile &tile) const;
|
||||
|
||||
QString _url;
|
||||
QString _dir;
|
||||
|
||||
static Downloader *_downloader;
|
||||
};
|
||||
|
||||
#endif // TILELOADER_Honlinemap
|
169
src/map/wmts.cpp
Normal file
169
src/map/wmts.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include <QXmlStreamReader>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QEventLoop>
|
||||
#include <QTextStream>
|
||||
#include "downloader.h"
|
||||
#include "pcs.h"
|
||||
#include "wmts.h"
|
||||
|
||||
|
||||
Downloader *WMTS::_downloader = 0;
|
||||
|
||||
bool WMTS::createProjection(const QString &crs)
|
||||
{
|
||||
QStringList list(crs.split(':'));
|
||||
QString authority, code;
|
||||
const PCS *pcs;
|
||||
|
||||
switch (list.size()) {
|
||||
case 2:
|
||||
authority = list.at(0);
|
||||
code = list.at(1);
|
||||
break;
|
||||
case 7:
|
||||
authority = list.at(4);
|
||||
code = list.at(6);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authority != "EPSG")
|
||||
return false;
|
||||
if (!(pcs = PCS::pcs(code.toInt())))
|
||||
return false;
|
||||
|
||||
_projection = Projection(pcs->gcs(), pcs->method(), pcs->setup(),
|
||||
pcs->units());
|
||||
return true;
|
||||
}
|
||||
|
||||
void WMTS::tileMatrix(QXmlStreamReader &reader)
|
||||
{
|
||||
Zoom zoom;
|
||||
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "ScaleDenominator")
|
||||
zoom.scaleDenominator = reader.readElementText().toDouble();
|
||||
else if (reader.name() == "TopLeftCorner") {
|
||||
QString str = reader.readElementText();
|
||||
QTextStream(&str) >> zoom.topLeft.rx() >> zoom.topLeft.ry();
|
||||
} else if (reader.name() == "TileWidth")
|
||||
zoom.tile.setWidth(reader.readElementText().toInt());
|
||||
else if (reader.name() == "TileHeight")
|
||||
zoom.tile.setHeight(reader.readElementText().toInt());
|
||||
else if (reader.name() == "MatrixWidth")
|
||||
zoom.matrix.setWidth(reader.readElementText().toInt());
|
||||
else if (reader.name() == "MatrixHeight")
|
||||
zoom.matrix.setHeight(reader.readElementText().toInt());
|
||||
else
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
|
||||
_zooms.append(zoom);
|
||||
}
|
||||
|
||||
void WMTS::tileMatrixSet(QXmlStreamReader &reader, const QString &set)
|
||||
{
|
||||
QString id;
|
||||
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "Identifier")
|
||||
id = reader.readElementText();
|
||||
else if (reader.name() == "SupportedCRS" && id == set) {
|
||||
if (!createProjection(reader.readElementText()))
|
||||
reader.raiseError("Invalid/unknown CRS");
|
||||
} else if (reader.name() == "TileMatrix" && id == set)
|
||||
tileMatrix(reader);
|
||||
else
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void WMTS::contents(QXmlStreamReader &reader, const QString &set)
|
||||
{
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "TileMatrixSet")
|
||||
tileMatrixSet(reader, set);
|
||||
else
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
void WMTS::capabilities(QXmlStreamReader &reader, const QString &set)
|
||||
{
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "Contents")
|
||||
contents(reader, set);
|
||||
else
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
bool WMTS::parseCapabilities(const QString &path, const QString &tileMatrixSet)
|
||||
{
|
||||
QFile file(path);
|
||||
QXmlStreamReader reader;
|
||||
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
_errorString = file.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
reader.setDevice(&file);
|
||||
|
||||
if (reader.readNextStartElement()) {
|
||||
if (reader.name() == "Capabilities")
|
||||
capabilities(reader, tileMatrixSet);
|
||||
else
|
||||
reader.raiseError("Not a Capabilities XML file");
|
||||
}
|
||||
|
||||
_errorString = reader.error() ? QString("%1:%2: %3").arg(path)
|
||||
.arg(reader.lineNumber()).arg(reader.errorString()) : QString();
|
||||
|
||||
return reader.error() ? false : true;
|
||||
}
|
||||
|
||||
bool WMTS::getCapabilities(const QString &url, const QString &file)
|
||||
{
|
||||
QList<Download> dl;
|
||||
|
||||
QString capabilitiesUrl = QString("%1?service=WMTS&Version=1.0.0"
|
||||
"&request=GetCapabilities").arg(url);
|
||||
dl.append(Download(capabilitiesUrl, file));
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(_downloader, SIGNAL(finished()), &wait, SLOT(quit()));
|
||||
if (_downloader->get(dl))
|
||||
wait.exec();
|
||||
|
||||
if (QFileInfo(file).exists())
|
||||
return true;
|
||||
else {
|
||||
_errorString = "Error downloading capabilities XML file";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WMTS::load(const QString &file, const QString &url,
|
||||
const QString &tileMatrixSet)
|
||||
{
|
||||
if (!QFileInfo(file).exists())
|
||||
if (!getCapabilities(url, file))
|
||||
return false;
|
||||
if (!parseCapabilities(file, tileMatrixSet))
|
||||
return false;
|
||||
|
||||
if (_projection.isNull()) {
|
||||
_errorString = "Missing CRS definition";
|
||||
return false;
|
||||
}
|
||||
if (_zooms.isEmpty()) {
|
||||
_errorString = "No tile matrix found";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
50
src/map/wmts.h
Normal file
50
src/map/wmts.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef WMTS_H
|
||||
#define WMTS_H
|
||||
|
||||
#include <QSize>
|
||||
#include <QList>
|
||||
#include "projection.h"
|
||||
|
||||
class QXmlStreamReader;
|
||||
class Downloader;
|
||||
|
||||
class WMTS
|
||||
{
|
||||
public:
|
||||
struct Zoom {
|
||||
qreal scaleDenominator;
|
||||
QPointF topLeft;
|
||||
QSize tile;
|
||||
QSize matrix;
|
||||
};
|
||||
|
||||
bool load(const QString &path, const QString &url,
|
||||
const QString &tileMatrixSet);
|
||||
const QString &errorString() const {return _errorString;}
|
||||
|
||||
const QList<Zoom> &zooms() {return _zooms;}
|
||||
const Projection &projection() {return _projection;}
|
||||
|
||||
static Downloader *downloader() {return _downloader;}
|
||||
static void setDownloader(Downloader *downloader)
|
||||
{_downloader = downloader;}
|
||||
|
||||
private:
|
||||
bool createProjection(const QString &crs);
|
||||
|
||||
void tileMatrix(QXmlStreamReader &reader);
|
||||
void tileMatrixSet(QXmlStreamReader &reader, const QString &set);
|
||||
void contents(QXmlStreamReader &reader, const QString &set);
|
||||
void capabilities(QXmlStreamReader &reader, const QString &set);
|
||||
bool parseCapabilities(const QString &path, const QString &tileMatrixSet);
|
||||
bool getCapabilities(const QString &url, const QString &file);
|
||||
|
||||
QList<Zoom> _zooms;
|
||||
Projection _projection;
|
||||
|
||||
QString _errorString;
|
||||
|
||||
static Downloader *_downloader;
|
||||
};
|
||||
|
||||
#endif // WMTS_H
|
203
src/map/wmtsmap.cpp
Normal file
203
src/map/wmtsmap.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include <QPainter>
|
||||
#include "common/rectc.h"
|
||||
#include "config.h"
|
||||
#include "transform.h"
|
||||
#include "wmts.h"
|
||||
#include "wmtsmap.h"
|
||||
|
||||
|
||||
#define CAPABILITIES_FILE "capabilities.xml"
|
||||
|
||||
WMTSMap::WMTSMap(const QString &name, const QString &url, const QString &format,
|
||||
const QString &layer, const QString &style, const QString &tileMatrixSet,
|
||||
QObject *parent) : Map(parent), _name(name), _url(url),
|
||||
_tileMatrixSet(tileMatrixSet), _zoom(0), _valid(false)
|
||||
{
|
||||
QString dir(TILES_DIR + "/" + _name);
|
||||
QString file = dir + "/" + CAPABILITIES_FILE;
|
||||
|
||||
QString tileUrl = QString("%1?service=WMTS&Version=1.0.0&request=GetTile"
|
||||
"&Format=%2&Layer=%3&Style=%4&TileMatrixSet=%5&TileMatrix=$z&TileRow=$y"
|
||||
"&TileCol=$x").arg(_url).arg(format).arg(layer).arg(style)
|
||||
.arg(_tileMatrixSet);
|
||||
_tileLoader = TileLoader(tileUrl, dir);
|
||||
|
||||
WMTS wmts;
|
||||
if (!wmts.load(file, _url, _tileMatrixSet)) {
|
||||
_errorString = wmts.errorString();
|
||||
return;
|
||||
}
|
||||
_zooms = wmts.zooms();
|
||||
_projection = wmts.projection();
|
||||
|
||||
updateTransform();
|
||||
|
||||
_block = false;
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
qreal WMTSMap::sd2res(qreal scaleDenominator) const
|
||||
{
|
||||
return scaleDenominator * 0.28e-3 * _projection.units().fromMeters(1.0);
|
||||
}
|
||||
|
||||
void WMTSMap::updateTransform()
|
||||
{
|
||||
const WMTS::Zoom &z = _zooms.at(_zoom);
|
||||
ReferencePoint tl, br;
|
||||
|
||||
qreal pixelSpan = sd2res(z.scaleDenominator);
|
||||
QPointF tileSpan(z.tile.width() * pixelSpan, z.tile.height() * pixelSpan);
|
||||
QPointF bottomRight(z.topLeft.x() + tileSpan.x() * z.matrix.width(),
|
||||
z.topLeft.y() - tileSpan.y() * z.matrix.height());
|
||||
|
||||
tl.xy = QPoint(0, 0);
|
||||
tl.pp = z.topLeft;
|
||||
br.xy = QPoint(z.tile.width() * z.matrix.width(),
|
||||
z.tile.height() * z.matrix.height());
|
||||
br.pp = bottomRight;
|
||||
|
||||
QList<ReferencePoint> points;
|
||||
points << tl << br;
|
||||
Transform tr(points);
|
||||
_transform = tr.transform();
|
||||
_inverted = _transform.inverted();
|
||||
}
|
||||
|
||||
void WMTSMap::load()
|
||||
{
|
||||
connect(TileLoader::downloader(), SIGNAL(finished()), this,
|
||||
SLOT(emitLoaded()));
|
||||
}
|
||||
|
||||
void WMTSMap::unload()
|
||||
{
|
||||
disconnect(TileLoader::downloader(), SIGNAL(finished()), this,
|
||||
SLOT(emitLoaded()));
|
||||
}
|
||||
|
||||
void WMTSMap::clearCache()
|
||||
{
|
||||
QString dir(TILES_DIR + "/" + _name);
|
||||
QString file = dir + "/" + CAPABILITIES_FILE;
|
||||
|
||||
_tileLoader.clearCache();
|
||||
|
||||
WMTS wmts;
|
||||
if (!wmts.load(file, _url, _tileMatrixSet))
|
||||
return;
|
||||
_zooms = wmts.zooms();
|
||||
_projection = wmts.projection();
|
||||
|
||||
if (_zoom >= _zooms.size())
|
||||
_zoom = _zooms.size() - 1;
|
||||
updateTransform();
|
||||
}
|
||||
|
||||
void WMTSMap::emitLoaded()
|
||||
{
|
||||
emit loaded();
|
||||
}
|
||||
|
||||
QRectF WMTSMap::bounds() const
|
||||
{
|
||||
const WMTS::Zoom &z = _zooms.at(_zoom);
|
||||
return QRectF(QPointF(0, 0), QSize(z.tile.width() * z.matrix.width(),
|
||||
z.tile.height() * z.matrix.height()));
|
||||
}
|
||||
|
||||
qreal WMTSMap::zoomFit(const QSize &size, const RectC &br)
|
||||
{
|
||||
_zoom = 0;
|
||||
|
||||
if (br.isValid()) {
|
||||
QRectF tbr(_projection.ll2xy(br.topLeft()),
|
||||
_projection.ll2xy(br.bottomRight()));
|
||||
QPointF sc(tbr.width() / size.width(), tbr.height() / size.height());
|
||||
qreal resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
|
||||
|
||||
for (int i = 0; i < _zooms.size(); i++) {
|
||||
if (sd2res(_zooms.at(i).scaleDenominator) < resolution)
|
||||
break;
|
||||
_zoom = i;
|
||||
}
|
||||
}
|
||||
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
qreal WMTSMap::zoomFit(qreal resolution, const Coordinates &c)
|
||||
{
|
||||
Q_UNUSED(c);
|
||||
|
||||
_zoom = 0;
|
||||
|
||||
for (int i = 0; i < _zooms.size(); i++) {
|
||||
if (sd2res(_zooms.at(i).scaleDenominator) < resolution)
|
||||
break;
|
||||
_zoom = i;
|
||||
}
|
||||
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
qreal WMTSMap::resolution(const QPointF &p) const
|
||||
{
|
||||
Q_UNUSED(p);
|
||||
|
||||
return sd2res(_zooms.at(_zoom).scaleDenominator);
|
||||
}
|
||||
|
||||
qreal WMTSMap::zoomIn()
|
||||
{
|
||||
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
qreal WMTSMap::zoomOut()
|
||||
{
|
||||
_zoom = qMax(_zoom - 1, 0);
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
void WMTSMap::draw(QPainter *painter, const QRectF &rect)
|
||||
{
|
||||
const WMTS::Zoom &z = _zooms.at(_zoom);
|
||||
QPoint tl = QPoint((int)floor(rect.left() / (qreal)z.tile.width()),
|
||||
(int)floor(rect.top() / (qreal)z.tile.height()));
|
||||
QPoint br = QPoint((int)floor(rect.right() / (qreal)z.tile.width()),
|
||||
(int)floor(rect.bottom() / (qreal)z.tile.height()));
|
||||
|
||||
QList<Tile> tiles;
|
||||
for (int i = tl.x(); i <= br.x(); i++)
|
||||
for (int j = tl.y(); j <= br.y(); j++)
|
||||
tiles.append(Tile(QPoint(i, j), _zoom));
|
||||
|
||||
if (_block)
|
||||
_tileLoader.loadTilesSync(tiles);
|
||||
else
|
||||
_tileLoader.loadTilesAsync(tiles);
|
||||
|
||||
for (int i = 0; i < tiles.count(); i++) {
|
||||
Tile &t = tiles[i];
|
||||
QPoint tp(t.xy().x() * z.tile.width(), t.xy().y() * z.tile.height());
|
||||
if (t.pixmap().isNull())
|
||||
painter->fillRect(QRect(tp, z.tile), _backgroundColor);
|
||||
else
|
||||
painter->drawPixmap(tp, t.pixmap());
|
||||
}
|
||||
}
|
||||
|
||||
QPointF WMTSMap::ll2xy(const Coordinates &c) const
|
||||
{
|
||||
return _transform.map(_projection.ll2xy(c));
|
||||
}
|
||||
|
||||
Coordinates WMTSMap::xy2ll(const QPointF &p) const
|
||||
{
|
||||
return _projection.xy2ll(_inverted.map(p));
|
||||
}
|
69
src/map/wmtsmap.h
Normal file
69
src/map/wmtsmap.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef WMTSMAP_H
|
||||
#define WMTSMAP_H
|
||||
|
||||
#include <QTransform>
|
||||
#include "projection.h"
|
||||
#include "map.h"
|
||||
#include "wmts.h"
|
||||
#include "tileloader.h"
|
||||
|
||||
|
||||
class WMTSMap : public Map
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WMTSMap(const QString &name, const QString &url, const QString &format,
|
||||
const QString &layer, const QString &style, const QString &tileMatrixSet,
|
||||
QObject *parent = 0);
|
||||
|
||||
const QString &name() const {return _name;}
|
||||
|
||||
QRectF bounds() const;
|
||||
qreal resolution(const QPointF &p) const;
|
||||
|
||||
qreal zoom() const {return _zoom;}
|
||||
qreal zoomFit(const QSize &size, const RectC &br);
|
||||
qreal zoomFit(qreal resolution, const Coordinates &c);
|
||||
qreal zoomIn();
|
||||
qreal zoomOut();
|
||||
|
||||
QPointF ll2xy(const Coordinates &c)
|
||||
{return static_cast<const WMTSMap &>(*this).ll2xy(c);}
|
||||
Coordinates xy2ll(const QPointF &p)
|
||||
{return static_cast<const WMTSMap &>(*this).xy2ll(p);}
|
||||
|
||||
void draw(QPainter *painter, const QRectF &rect);
|
||||
|
||||
void setBlockingMode(bool block) {_block = block;}
|
||||
void clearCache();
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
QString errorString() const {return _errorString;}
|
||||
|
||||
private slots:
|
||||
void emitLoaded();
|
||||
|
||||
private:
|
||||
qreal sd2res(qreal scaleDenominator) const;
|
||||
void updateTransform();
|
||||
|
||||
QPointF ll2xy(const Coordinates &c) const;
|
||||
Coordinates xy2ll(const QPointF &p) const;
|
||||
|
||||
QString _name, _url, _tileMatrixSet;
|
||||
TileLoader _tileLoader;
|
||||
QList<WMTS::Zoom> _zooms;
|
||||
Projection _projection;
|
||||
QTransform _transform, _inverted;
|
||||
int _zoom;
|
||||
bool _block;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
};
|
||||
|
||||
#endif // WMTSMAP_H
|
Loading…
Reference in New Issue
Block a user