mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-27 21:24:47 +01:00
Initial WMS support (no multi-layer support for now)
This commit is contained in:
parent
8821536419
commit
bf3589812a
10
gpxsee.pro
10
gpxsee.pro
@ -119,7 +119,10 @@ HEADERS += src/config.h \
|
|||||||
src/map/mapsource.h \
|
src/map/mapsource.h \
|
||||||
src/map/tileloader.h \
|
src/map/tileloader.h \
|
||||||
src/map/wmtsmap.h \
|
src/map/wmtsmap.h \
|
||||||
src/map/wmts.h
|
src/map/wmts.h \
|
||||||
|
src/map/wmsmap.h \
|
||||||
|
src/map/wms.h \
|
||||||
|
src/map/crs.h
|
||||||
SOURCES += src/main.cpp \
|
SOURCES += src/main.cpp \
|
||||||
src/common/coordinates.cpp \
|
src/common/coordinates.cpp \
|
||||||
src/common/rectc.cpp \
|
src/common/rectc.cpp \
|
||||||
@ -208,7 +211,10 @@ SOURCES += src/main.cpp \
|
|||||||
src/map/mapsource.cpp \
|
src/map/mapsource.cpp \
|
||||||
src/map/tileloader.cpp \
|
src/map/tileloader.cpp \
|
||||||
src/map/wmtsmap.cpp \
|
src/map/wmtsmap.cpp \
|
||||||
src/map/wmts.cpp
|
src/map/wmts.cpp \
|
||||||
|
src/map/wmsmap.cpp \
|
||||||
|
src/map/wms.cpp \
|
||||||
|
src/map/crs.cpp
|
||||||
RESOURCES += gpxsee.qrc
|
RESOURCES += gpxsee.qrc
|
||||||
TRANSLATIONS = lang/gpxsee_cs.ts \
|
TRANSLATIONS = lang/gpxsee_cs.ts \
|
||||||
lang/gpxsee_sv.ts \
|
lang/gpxsee_sv.ts \
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QNetworkProxyFactory>
|
#include <QNetworkProxyFactory>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include "map/wmts.h"
|
#include "map/wmts.h"
|
||||||
|
#include "map/wms.h"
|
||||||
#include "map/tileloader.h"
|
#include "map/tileloader.h"
|
||||||
#include "map/downloader.h"
|
#include "map/downloader.h"
|
||||||
#include "map/ellipsoid.h"
|
#include "map/ellipsoid.h"
|
||||||
@ -40,6 +41,7 @@ App::App(int &argc, char **argv) : QApplication(argc, argv),
|
|||||||
Downloader *dl = new Downloader(this);
|
Downloader *dl = new Downloader(this);
|
||||||
TileLoader::setDownloader(dl);
|
TileLoader::setDownloader(dl);
|
||||||
WMTS::setDownloader(dl);
|
WMTS::setDownloader(dl);
|
||||||
|
WMS::setDownloader(dl);
|
||||||
OPENGL_SET_SAMPLES(4);
|
OPENGL_SET_SAMPLES(4);
|
||||||
loadDatums();
|
loadDatums();
|
||||||
loadPCSs();
|
loadPCSs();
|
||||||
|
@ -179,7 +179,7 @@ QList<PathItem *> MapView::loadData(const Data &data)
|
|||||||
if (_tracks.empty() && _routes.empty() && _waypoints.empty())
|
if (_tracks.empty() && _routes.empty() && _waypoints.empty())
|
||||||
return paths;
|
return paths;
|
||||||
|
|
||||||
if (mapZoom() != zoom)
|
if (fitMapZoom() != zoom)
|
||||||
rescale();
|
rescale();
|
||||||
else
|
else
|
||||||
updatePOIVisibility();
|
updatePOIVisibility();
|
||||||
@ -189,7 +189,7 @@ QList<PathItem *> MapView::loadData(const Data &data)
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MapView::mapZoom() const
|
int MapView::fitMapZoom() const
|
||||||
{
|
{
|
||||||
RectC br = _tr | _rr | _wr;
|
RectC br = _tr | _rr | _wr;
|
||||||
|
|
||||||
@ -389,7 +389,10 @@ void MapView::setCoordinatesFormat(CoordinatesFormat format)
|
|||||||
void MapView::clearMapCache()
|
void MapView::clearMapCache()
|
||||||
{
|
{
|
||||||
_map->clearCache();
|
_map->clearCache();
|
||||||
resetCachedContent();
|
|
||||||
|
fitMapZoom();
|
||||||
|
rescale();
|
||||||
|
centerOn(contentCenter());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapView::digitalZoom(int zoom)
|
void MapView::digitalZoom(int zoom)
|
||||||
@ -767,7 +770,7 @@ void MapView::resizeEvent(QResizeEvent *event)
|
|||||||
QGraphicsView::resizeEvent(event);
|
QGraphicsView::resizeEvent(event);
|
||||||
|
|
||||||
int zoom = _map->zoom();
|
int zoom = _map->zoom();
|
||||||
if (mapZoom() != zoom)
|
if (fitMapZoom() != zoom)
|
||||||
rescale();
|
rescale();
|
||||||
|
|
||||||
centerOn(contentCenter());
|
centerOn(contentCenter());
|
||||||
|
@ -80,7 +80,7 @@ private:
|
|||||||
void loadPOI();
|
void loadPOI();
|
||||||
void clearPOI();
|
void clearPOI();
|
||||||
|
|
||||||
int mapZoom() const;
|
int fitMapZoom() const;
|
||||||
QPointF contentCenter() const;
|
QPointF contentCenter() const;
|
||||||
void rescale();
|
void rescale();
|
||||||
void centerOn(const QPointF &pos);
|
void centerOn(const QPointF &pos);
|
||||||
|
@ -16,6 +16,9 @@ public:
|
|||||||
|
|
||||||
bool isValid() const {return size() >= 0;}
|
bool isValid() const {return size() >= 0;}
|
||||||
|
|
||||||
|
void setMin(int min) {_min = min;}
|
||||||
|
void setMax(int max) {_max = max;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _min, _max;
|
int _min, _max;
|
||||||
};
|
};
|
||||||
@ -32,6 +35,9 @@ public:
|
|||||||
|
|
||||||
bool isValid() const {return size() >= 0;}
|
bool isValid() const {return size() >= 0;}
|
||||||
|
|
||||||
|
void setMin(qreal min) {_min = min;}
|
||||||
|
void setMax(qreal max) {_max = max;}
|
||||||
|
|
||||||
void resize(qreal size);
|
void resize(qreal size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
46
src/map/crs.cpp
Normal file
46
src/map/crs.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include <QStringList>
|
||||||
|
#include "pcs.h"
|
||||||
|
#include "crs.h"
|
||||||
|
|
||||||
|
Projection CRS::projection(const QString &crs)
|
||||||
|
{
|
||||||
|
QStringList list(crs.split(':'));
|
||||||
|
QString authority, code;
|
||||||
|
bool res;
|
||||||
|
int epsg;
|
||||||
|
const PCS *pcs;
|
||||||
|
const GCS *gcs;
|
||||||
|
|
||||||
|
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 Projection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authority == "EPSG") {
|
||||||
|
epsg = code.toInt(&res);
|
||||||
|
if (!res)
|
||||||
|
return Projection();
|
||||||
|
|
||||||
|
if ((pcs = PCS::pcs(epsg)))
|
||||||
|
return Projection(pcs->gcs(), pcs->method(), pcs->setup(),
|
||||||
|
pcs->units());
|
||||||
|
else if ((gcs = GCS::gcs(epsg)))
|
||||||
|
return Projection(gcs);
|
||||||
|
else
|
||||||
|
return Projection();
|
||||||
|
} else if (authority == "OGC") {
|
||||||
|
if (code == "CRS84")
|
||||||
|
return Projection(GCS::gcs(4326));
|
||||||
|
else
|
||||||
|
return Projection();
|
||||||
|
} else
|
||||||
|
return Projection();
|
||||||
|
}
|
11
src/map/crs.h
Normal file
11
src/map/crs.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef CRS_H
|
||||||
|
#define CRS_H
|
||||||
|
|
||||||
|
#include "projection.h"
|
||||||
|
|
||||||
|
namespace CRS
|
||||||
|
{
|
||||||
|
Projection projection(const QString &crs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CRS_H
|
@ -2,6 +2,7 @@
|
|||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
#include "onlinemap.h"
|
#include "onlinemap.h"
|
||||||
#include "wmtsmap.h"
|
#include "wmtsmap.h"
|
||||||
|
#include "wmsmap.h"
|
||||||
#include "mapsource.h"
|
#include "mapsource.h"
|
||||||
|
|
||||||
#define ZOOM_MAX 19
|
#define ZOOM_MAX 19
|
||||||
@ -11,9 +12,10 @@
|
|||||||
#define BOUNDS_RIGHT 180
|
#define BOUNDS_RIGHT 180
|
||||||
#define BOUNDS_BOTTOM -85.0511
|
#define BOUNDS_BOTTOM -85.0511
|
||||||
|
|
||||||
MapSource::TMSConfig::TMSConfig()
|
|
||||||
: zooms(ZOOM_MIN, ZOOM_MAX), bounds(Coordinates(BOUNDS_LEFT, BOUNDS_TOP),
|
MapSource::Config::Config() : type(TMS), zooms(ZOOM_MIN, ZOOM_MAX),
|
||||||
Coordinates(BOUNDS_RIGHT, BOUNDS_BOTTOM)) {}
|
bounds(Coordinates(BOUNDS_LEFT, BOUNDS_TOP), Coordinates(BOUNDS_RIGHT,
|
||||||
|
BOUNDS_BOTTOM)), format("image/png"), rest(false), yx(false) {}
|
||||||
|
|
||||||
|
|
||||||
Range MapSource::zooms(QXmlStreamReader &reader)
|
Range MapSource::zooms(QXmlStreamReader &reader)
|
||||||
@ -23,11 +25,7 @@ Range MapSource::zooms(QXmlStreamReader &reader)
|
|||||||
bool res;
|
bool res;
|
||||||
|
|
||||||
if (attr.hasAttribute("min")) {
|
if (attr.hasAttribute("min")) {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
||||||
min = attr.value("min").toString().toInt(&res);
|
min = attr.value("min").toString().toInt(&res);
|
||||||
#else // QT_VERSION < 5
|
|
||||||
min = attr.value("min").toInt(&res);
|
|
||||||
#endif // QT_VERSION < 5
|
|
||||||
if (!res || (min < ZOOM_MIN || min > ZOOM_MAX)) {
|
if (!res || (min < ZOOM_MIN || min > ZOOM_MAX)) {
|
||||||
reader.raiseError("Invalid minimal zoom level");
|
reader.raiseError("Invalid minimal zoom level");
|
||||||
return Range();
|
return Range();
|
||||||
@ -36,11 +34,7 @@ Range MapSource::zooms(QXmlStreamReader &reader)
|
|||||||
min = ZOOM_MIN;
|
min = ZOOM_MIN;
|
||||||
|
|
||||||
if (attr.hasAttribute("max")) {
|
if (attr.hasAttribute("max")) {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
||||||
max = attr.value("max").toString().toInt(&res);
|
max = attr.value("max").toString().toInt(&res);
|
||||||
#else // QT_VERSION < 5
|
|
||||||
max = attr.value("max").toInt(&res);
|
|
||||||
#endif // QT_VERSION < 5
|
|
||||||
if (!res || (max < ZOOM_MIN || max > ZOOM_MAX)) {
|
if (!res || (max < ZOOM_MIN || max > ZOOM_MAX)) {
|
||||||
reader.raiseError("Invalid maximal zoom level");
|
reader.raiseError("Invalid maximal zoom level");
|
||||||
return Range();
|
return Range();
|
||||||
@ -63,11 +57,7 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
|||||||
bool res;
|
bool res;
|
||||||
|
|
||||||
if (attr.hasAttribute("top")) {
|
if (attr.hasAttribute("top")) {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
||||||
top = attr.value("top").toString().toDouble(&res);
|
top = attr.value("top").toString().toDouble(&res);
|
||||||
#else // QT_VERSION < 5
|
|
||||||
top = attr.value("top").toDouble(&res);
|
|
||||||
#endif // QT_VERSION < 5
|
|
||||||
if (!res || (top < BOUNDS_BOTTOM || top > BOUNDS_TOP)) {
|
if (!res || (top < BOUNDS_BOTTOM || top > BOUNDS_TOP)) {
|
||||||
reader.raiseError("Invalid bounds top value");
|
reader.raiseError("Invalid bounds top value");
|
||||||
return RectC();
|
return RectC();
|
||||||
@ -76,11 +66,7 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
|||||||
top = BOUNDS_TOP;
|
top = BOUNDS_TOP;
|
||||||
|
|
||||||
if (attr.hasAttribute("bottom")) {
|
if (attr.hasAttribute("bottom")) {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
||||||
bottom = attr.value("bottom").toString().toDouble(&res);
|
bottom = attr.value("bottom").toString().toDouble(&res);
|
||||||
#else // QT_VERSION < 5
|
|
||||||
bottom = attr.value("bottom").toDouble(&res);
|
|
||||||
#endif // QT_VERSION < 5
|
|
||||||
if (!res || (bottom < BOUNDS_BOTTOM || bottom > BOUNDS_TOP)) {
|
if (!res || (bottom < BOUNDS_BOTTOM || bottom > BOUNDS_TOP)) {
|
||||||
reader.raiseError("Invalid bounds bottom value");
|
reader.raiseError("Invalid bounds bottom value");
|
||||||
return RectC();
|
return RectC();
|
||||||
@ -89,11 +75,7 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
|||||||
bottom = BOUNDS_BOTTOM;
|
bottom = BOUNDS_BOTTOM;
|
||||||
|
|
||||||
if (attr.hasAttribute("left")) {
|
if (attr.hasAttribute("left")) {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
||||||
left = attr.value("left").toString().toDouble(&res);
|
left = attr.value("left").toString().toDouble(&res);
|
||||||
#else // QT_VERSION < 5
|
|
||||||
left = attr.value("left").toDouble(&res);
|
|
||||||
#endif // QT_VERSION < 5
|
|
||||||
if (!res || (left < BOUNDS_LEFT || left > BOUNDS_RIGHT)) {
|
if (!res || (left < BOUNDS_LEFT || left > BOUNDS_RIGHT)) {
|
||||||
reader.raiseError("Invalid bounds left value");
|
reader.raiseError("Invalid bounds left value");
|
||||||
return RectC();
|
return RectC();
|
||||||
@ -102,11 +84,7 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
|||||||
left = BOUNDS_LEFT;
|
left = BOUNDS_LEFT;
|
||||||
|
|
||||||
if (attr.hasAttribute("right")) {
|
if (attr.hasAttribute("right")) {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
|
||||||
right = attr.value("right").toString().toDouble(&res);
|
right = attr.value("right").toString().toDouble(&res);
|
||||||
#else // QT_VERSION < 5
|
|
||||||
right = attr.value("right").toDouble(&res);
|
|
||||||
#endif // QT_VERSION < 5
|
|
||||||
if (!res || (right < BOUNDS_LEFT || right > BOUNDS_RIGHT)) {
|
if (!res || (right < BOUNDS_LEFT || right > BOUNDS_RIGHT)) {
|
||||||
reader.raiseError("Invalid bounds right value");
|
reader.raiseError("Invalid bounds right value");
|
||||||
return RectC();
|
return RectC();
|
||||||
@ -128,38 +106,44 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
|||||||
|
|
||||||
void MapSource::map(QXmlStreamReader &reader, Config &config)
|
void MapSource::map(QXmlStreamReader &reader, Config &config)
|
||||||
{
|
{
|
||||||
config.type = (reader.attributes().value("type") == "WMTS") ? WMTS : TMS;
|
const QXmlStreamAttributes &attr = reader.attributes();
|
||||||
|
config.type = (attr.value("type") == "WMTS") ? WMTS
|
||||||
|
: (attr.value("type") == "WMS") ? WMS : TMS;
|
||||||
|
|
||||||
while (reader.readNextStartElement()) {
|
while (reader.readNextStartElement()) {
|
||||||
if (reader.name() == "name")
|
if (reader.name() == "name")
|
||||||
config.name = reader.readElementText();
|
config.name = reader.readElementText();
|
||||||
else if (reader.name() == "url") {
|
else if (reader.name() == "url") {
|
||||||
config.wmts.rest = (reader.attributes().value("type") == "REST")
|
config.rest = (reader.attributes().value("type") == "REST")
|
||||||
? true : false;
|
? true : false;
|
||||||
config.url = reader.readElementText();
|
config.url = reader.readElementText();
|
||||||
} else if (reader.name() == "zoom") {
|
} else if (reader.name() == "zoom") {
|
||||||
config.tms.zooms = zooms(reader);
|
config.zooms = zooms(reader);
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
} else if (reader.name() == "bounds") {
|
} else if (reader.name() == "bounds") {
|
||||||
config.tms.bounds = bounds(reader);
|
config.bounds = bounds(reader);
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
} else if (reader.name() == "format") {
|
} else if (reader.name() == "format") {
|
||||||
config.wmts.format = reader.readElementText();
|
config.format = reader.readElementText();
|
||||||
} else if (reader.name() == "layer")
|
} else if (reader.name() == "layer")
|
||||||
config.wmts.layer = reader.readElementText();
|
config.layer = reader.readElementText();
|
||||||
else if (reader.name() == "style")
|
else if (reader.name() == "style")
|
||||||
config.wmts.style = reader.readElementText();
|
config.style = reader.readElementText();
|
||||||
else if (reader.name() == "set") {
|
else if (reader.name() == "set") {
|
||||||
config.wmts.yx = (reader.attributes().value("axis") == "yx")
|
config.yx = (reader.attributes().value("axis") == "yx")
|
||||||
? true : false;
|
? true : false;
|
||||||
config.wmts.set = reader.readElementText();
|
config.set = reader.readElementText();
|
||||||
} else if (reader.name() == "dimension") {
|
} else if (reader.name() == "dimension") {
|
||||||
QXmlStreamAttributes attr = reader.attributes();
|
QXmlStreamAttributes attr = reader.attributes();
|
||||||
if (!attr.hasAttribute("id"))
|
if (!attr.hasAttribute("id"))
|
||||||
reader.raiseError("Missing dimension id");
|
reader.raiseError("Missing dimension id");
|
||||||
else
|
else
|
||||||
config.wmts.dimensions.append(QPair<QString, QString>(
|
config.dimensions.append(QPair<QString, QString>(
|
||||||
attr.value("id").toString(), reader.readElementText()));
|
attr.value("id").toString(), reader.readElementText()));
|
||||||
|
} else if (reader.name() == "crs") {
|
||||||
|
config.yx = (reader.attributes().value("axis") == "yx")
|
||||||
|
? true : false;
|
||||||
|
config.crs = reader.readElementText();
|
||||||
} else
|
} else
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
@ -170,6 +154,8 @@ Map *MapSource::loadFile(const QString &path)
|
|||||||
QFile file(path);
|
QFile file(path);
|
||||||
QXmlStreamReader reader;
|
QXmlStreamReader reader;
|
||||||
Config config;
|
Config config;
|
||||||
|
Map *m;
|
||||||
|
|
||||||
|
|
||||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
_errorString = file.errorString();
|
_errorString = file.errorString();
|
||||||
@ -197,33 +183,43 @@ Map *MapSource::loadFile(const QString &path)
|
|||||||
_errorString = "Missing URL definition";
|
_errorString = "Missing URL definition";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (config.type == WMTS) {
|
if (config.type == WMTS || config.type == WMS) {
|
||||||
if (config.wmts.layer.isEmpty()) {
|
if (config.layer.isEmpty()) {
|
||||||
_errorString = "Missing layer definition";
|
_errorString = "Missing layer definition";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (config.wmts.style.isEmpty()) {
|
if (config.style.isEmpty()) {
|
||||||
_errorString = "Missing style definiton";
|
_errorString = "Missing style definiton";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (config.wmts.set.isEmpty()) {
|
if (config.format.isEmpty()) {
|
||||||
_errorString = "Missing set definiton";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (config.wmts.format.isEmpty()) {
|
|
||||||
_errorString = "Missing format definition";
|
_errorString = "Missing format definition";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (config.type == WMTS) {
|
||||||
|
if (config.set.isEmpty()) {
|
||||||
|
_errorString = "Missing set definiton";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.type == WMS) {
|
||||||
|
if (config.crs.isEmpty()) {
|
||||||
|
_errorString = "Missing CRS definiton";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Map *m;
|
|
||||||
if (config.type == WMTS)
|
if (config.type == WMTS)
|
||||||
m = new WMTSMap(config.name, WMTS::Setup(config.url, config.wmts.layer,
|
m = new WMTSMap(config.name, WMTS::Setup(config.url, config.layer,
|
||||||
config.wmts.set, config.wmts.style, config.wmts.format,
|
config.set, config.style, config.format, config.rest, config.yx,
|
||||||
config.wmts.rest, config.wmts.yx, config.wmts.dimensions));
|
config.dimensions));
|
||||||
|
else if (config.type == WMS)
|
||||||
|
m = new WMSMap(config.name, WMS::Setup(config.url, config.layer,
|
||||||
|
config.style, config.format, config.crs, config.yx));
|
||||||
else
|
else
|
||||||
m = new OnlineMap(config.name, config.url, config.tms.zooms,
|
m = new OnlineMap(config.name, config.url, config.zooms, config.bounds);
|
||||||
config.tms.bounds);
|
|
||||||
if (!m->isValid()) {
|
if (!m->isValid()) {
|
||||||
_errorString = m->errorString();
|
_errorString = m->errorString();
|
||||||
delete m;
|
delete m;
|
||||||
|
@ -17,36 +17,26 @@ public:
|
|||||||
private:
|
private:
|
||||||
enum Type {
|
enum Type {
|
||||||
TMS,
|
TMS,
|
||||||
WMTS
|
WMTS,
|
||||||
|
WMS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TMSConfig {
|
struct Config {
|
||||||
|
Type type;
|
||||||
|
QString name;
|
||||||
|
QString url;
|
||||||
Range zooms;
|
Range zooms;
|
||||||
RectC bounds;
|
RectC bounds;
|
||||||
|
|
||||||
TMSConfig();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WMTSConfig {
|
|
||||||
QString layer;
|
QString layer;
|
||||||
QString style;
|
QString style;
|
||||||
QString set;
|
QString set;
|
||||||
QString format;
|
QString format;
|
||||||
|
QString crs;
|
||||||
bool rest;
|
bool rest;
|
||||||
bool yx;
|
bool yx;
|
||||||
QList<QPair<QString, QString> > dimensions;
|
QList<QPair<QString, QString> > dimensions;
|
||||||
|
|
||||||
WMTSConfig() : format("image/png"), rest(false), yx(false) {}
|
Config();
|
||||||
};
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
QString name;
|
|
||||||
QString url;
|
|
||||||
Type type;
|
|
||||||
WMTSConfig wmts;
|
|
||||||
TMSConfig tms;
|
|
||||||
|
|
||||||
Config() : type(TMS) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RectC bounds(QXmlStreamReader &reader);
|
RectC bounds(QXmlStreamReader &reader);
|
||||||
|
@ -8,23 +8,26 @@
|
|||||||
class Tile
|
class Tile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Tile(const QPoint &xy, const QVariant &zoom)
|
Tile(const QPoint &xy, const QVariant &zoom, const QRectF &bbox = QRectF())
|
||||||
{_xy = xy; _zoom = zoom;}
|
{_xy = xy; _zoom = zoom; _bbox = bbox;}
|
||||||
|
|
||||||
QVariant zoom() const {return _zoom;}
|
const QVariant &zoom() const {return _zoom;}
|
||||||
const QPoint& xy() const {return _xy;}
|
const QPoint &xy() const {return _xy;}
|
||||||
|
const QRectF &bbox() const {return _bbox;}
|
||||||
QPixmap& pixmap() {return _pixmap;}
|
QPixmap& pixmap() {return _pixmap;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant _zoom;
|
QVariant _zoom;
|
||||||
QPoint _xy;
|
QPoint _xy;
|
||||||
|
QRectF _bbox;
|
||||||
QPixmap _pixmap;
|
QPixmap _pixmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
inline QDebug operator<<(QDebug dbg, const Tile &tile)
|
inline QDebug operator<<(QDebug dbg, const Tile &tile)
|
||||||
{
|
{
|
||||||
dbg.nospace() << "Tile(" << tile.zoom() << ", " << tile.xy() << ")";
|
dbg.nospace() << "Tile(" << tile.zoom() << ", " << tile.xy() << ", "
|
||||||
|
<< tile.bbox() << ")";
|
||||||
return dbg.space();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
#endif // QT_NO_DEBUG
|
#endif // QT_NO_DEBUG
|
||||||
|
@ -85,9 +85,18 @@ QString TileLoader::tileUrl(const Tile &tile) const
|
|||||||
{
|
{
|
||||||
QString url(_url);
|
QString url(_url);
|
||||||
|
|
||||||
url.replace("$z", tile.zoom().toString());
|
if (!tile.bbox().isNull()) {
|
||||||
url.replace("$x", QString::number(tile.xy().x()));
|
QString bbox = QString("%1,%2,%3,%4").arg(
|
||||||
url.replace("$y", QString::number(tile.xy().y()));
|
QString::number(tile.bbox().left(), 'f', 6),
|
||||||
|
QString::number(tile.bbox().bottom(), 'f', 6),
|
||||||
|
QString::number(tile.bbox().right(), 'f', 6),
|
||||||
|
QString::number(tile.bbox().top(), 'f', 6));
|
||||||
|
url.replace("$bbox", bbox);
|
||||||
|
} else {
|
||||||
|
url.replace("$z", tile.zoom().toString());
|
||||||
|
url.replace("$x", QString::number(tile.xy().x()));
|
||||||
|
url.replace("$y", QString::number(tile.xy().y()));
|
||||||
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ public:
|
|||||||
{_downloader = downloader;}
|
{_downloader = downloader;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fillTile(Tile &tile);
|
|
||||||
QString tileUrl(const Tile &tile) const;
|
QString tileUrl(const Tile &tile) const;
|
||||||
QString tileFile(const Tile &tile) const;
|
QString tileFile(const Tile &tile) const;
|
||||||
|
|
||||||
|
215
src/map/wms.cpp
Normal file
215
src/map/wms.cpp
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
#include <QFileInfo>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include "downloader.h"
|
||||||
|
#include "crs.h"
|
||||||
|
#include "wms.h"
|
||||||
|
|
||||||
|
|
||||||
|
Downloader *WMS::_downloader = 0;
|
||||||
|
|
||||||
|
void WMS::getMap(QXmlStreamReader &reader, CTX &ctx)
|
||||||
|
{
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "Format") {
|
||||||
|
if (reader.readElementText() == ctx.setup.format())
|
||||||
|
ctx.format = true;
|
||||||
|
} else
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMS::request(QXmlStreamReader &reader, CTX &ctx)
|
||||||
|
{
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "GetMap")
|
||||||
|
getMap(reader, ctx);
|
||||||
|
else
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WMS::style(QXmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "Name")
|
||||||
|
name = reader.readElementText();
|
||||||
|
else
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMS::layer(QXmlStreamReader &reader, CTX &ctx,
|
||||||
|
const QList<QString> &pCRSs, const QList<QString> &pStyles)
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QList<QString> CRSs(pCRSs);
|
||||||
|
QList<QString> styles(pStyles);
|
||||||
|
RangeF scaleDenominator(2132.729583849784, 559082264.0287178);
|
||||||
|
QRectF boundingBox;
|
||||||
|
|
||||||
|
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "Name")
|
||||||
|
name = reader.readElementText();
|
||||||
|
else if (reader.name() == "CRS")
|
||||||
|
CRSs.append(reader.readElementText());
|
||||||
|
else if (reader.name() == "Style")
|
||||||
|
styles.append(style(reader));
|
||||||
|
else if (reader.name() == "MinScaleDenominator")
|
||||||
|
scaleDenominator.setMin(reader.readElementText().toDouble());
|
||||||
|
else if (reader.name() == "MaxScaleDenominator")
|
||||||
|
scaleDenominator.setMax(reader.readElementText().toDouble());
|
||||||
|
else if (reader.name() == "BoundingBox") {
|
||||||
|
QXmlStreamAttributes attr = reader.attributes();
|
||||||
|
if (attr.value("CRS") == ctx.setup.crs()) {
|
||||||
|
if (ctx.setup.yx())
|
||||||
|
boundingBox = QRectF(QPointF(
|
||||||
|
attr.value("miny").toString().toDouble(),
|
||||||
|
attr.value("maxx").toString().toDouble()),
|
||||||
|
QPointF(attr.value("maxy").toString().toDouble(),
|
||||||
|
attr.value("minx").toString().toDouble()));
|
||||||
|
else
|
||||||
|
boundingBox = QRectF(QPointF(
|
||||||
|
attr.value("minx").toString().toDouble(),
|
||||||
|
attr.value("maxy").toString().toDouble()),
|
||||||
|
QPointF(attr.value("maxx").toString().toDouble(),
|
||||||
|
attr.value("miny").toString().toDouble()));
|
||||||
|
}
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
} else if (reader.name() == "Layer")
|
||||||
|
layer(reader, ctx, CRSs, styles);
|
||||||
|
else
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == ctx.setup.layer()) {
|
||||||
|
ctx.scaleDenominator = scaleDenominator;
|
||||||
|
ctx.boundingBox = boundingBox;
|
||||||
|
ctx.layer = true;
|
||||||
|
ctx.style = styles.contains(ctx.setup.style());
|
||||||
|
ctx.crs = CRSs.contains(ctx.setup.crs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMS::capability(QXmlStreamReader &reader, CTX &ctx)
|
||||||
|
{
|
||||||
|
QList<QString> CRSs;
|
||||||
|
QList<QString> styles;
|
||||||
|
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "Layer")
|
||||||
|
layer(reader, ctx, CRSs, styles);
|
||||||
|
else if (reader.name() == "Request")
|
||||||
|
request(reader, ctx);
|
||||||
|
else
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMS::capabilities(QXmlStreamReader &reader, CTX &ctx)
|
||||||
|
{
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "Capability")
|
||||||
|
capability(reader, ctx);
|
||||||
|
else
|
||||||
|
reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WMS::parseCapabilities(const QString &path, const Setup &setup)
|
||||||
|
{
|
||||||
|
QFile file(path);
|
||||||
|
CTX ctx(setup);
|
||||||
|
QXmlStreamReader reader;
|
||||||
|
|
||||||
|
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
_errorString = file.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.setDevice(&file);
|
||||||
|
if (reader.readNextStartElement()) {
|
||||||
|
if (reader.name() == "WMS_Capabilities")
|
||||||
|
capabilities(reader, ctx);
|
||||||
|
else
|
||||||
|
reader.raiseError("Not a WMS_Capabilities XML file");
|
||||||
|
}
|
||||||
|
if (reader.error()) {
|
||||||
|
_errorString = QString("%1:%2: %3").arg(path).arg(reader.lineNumber())
|
||||||
|
.arg(reader.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.layer) {
|
||||||
|
_errorString = ctx.setup.layer() + ": layer not provided";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctx.style) {
|
||||||
|
_errorString = ctx.setup.style() + ": style not provided";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctx.format) {
|
||||||
|
_errorString = ctx.setup.format() + ": format not provided";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctx.crs) {
|
||||||
|
_errorString = ctx.setup.crs() + ": CRS not provided";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_projection = CRS::projection(ctx.setup.crs());
|
||||||
|
if (_projection.isNull()) {
|
||||||
|
_errorString = ctx.setup.crs() + ": unknown CRS";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctx.scaleDenominator.isValid()) {
|
||||||
|
_errorString = "Invalid scale denominator range";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ctx.boundingBox.isNull()) {
|
||||||
|
_errorString = "Missing bounding box";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_boundingBox = ctx.boundingBox;
|
||||||
|
_scaleDenominator = ctx.scaleDenominator;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WMS::getCapabilities(const QString &url, const QString &file)
|
||||||
|
{
|
||||||
|
QList<Download> dl;
|
||||||
|
|
||||||
|
dl.append(Download(url, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WMS::WMS(const QString &file, const WMS::Setup &setup) : _valid(false)
|
||||||
|
{
|
||||||
|
QString capaUrl = QString("%1?service=WMS&request=GetCapabilities")
|
||||||
|
.arg(setup.url());
|
||||||
|
|
||||||
|
if (!QFileInfo(file).exists())
|
||||||
|
if (!getCapabilities(capaUrl, file))
|
||||||
|
return;
|
||||||
|
if (!parseCapabilities(file, setup))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_valid = true;
|
||||||
|
}
|
86
src/map/wms.h
Normal file
86
src/map/wms.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#ifndef WMS_H
|
||||||
|
#define WMS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QRectF>
|
||||||
|
#include "common/range.h"
|
||||||
|
#include "projection.h"
|
||||||
|
|
||||||
|
class QXmlStreamReader;
|
||||||
|
class Downloader;
|
||||||
|
|
||||||
|
class WMS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Setup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Setup(const QString &url, const QString &layer, const QString &style,
|
||||||
|
const QString &format, const QString &crs, bool yx)
|
||||||
|
: _url(url), _layer(layer), _style(style), _format(format), _crs(crs),
|
||||||
|
_yx(yx) {}
|
||||||
|
|
||||||
|
const QString &url() const {return _url;}
|
||||||
|
const QString &layer() const {return _layer;}
|
||||||
|
const QString &style() const {return _style;}
|
||||||
|
const QString &format() const {return _format;}
|
||||||
|
const QString &crs() const {return _crs;}
|
||||||
|
bool yx() const {return _yx;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _url;
|
||||||
|
QString _layer;
|
||||||
|
QString _style;
|
||||||
|
QString _format;
|
||||||
|
QString _crs;
|
||||||
|
bool _yx;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
WMS(const QString &path, const Setup &setup);
|
||||||
|
|
||||||
|
const Projection &projection() const {return _projection;}
|
||||||
|
const RangeF &scaleDenominator() const {return _scaleDenominator;}
|
||||||
|
const QRectF &boundingBox() const {return _boundingBox;}
|
||||||
|
|
||||||
|
bool isValid() const {return _valid;}
|
||||||
|
const QString &errorString() const {return _errorString;}
|
||||||
|
|
||||||
|
static void setDownloader(Downloader *downloader)
|
||||||
|
{_downloader = downloader;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct CTX {
|
||||||
|
const Setup &setup;
|
||||||
|
RangeF scaleDenominator;
|
||||||
|
QRectF boundingBox;
|
||||||
|
bool layer;
|
||||||
|
bool style;
|
||||||
|
bool format;
|
||||||
|
bool crs;
|
||||||
|
|
||||||
|
CTX(const Setup &setup) : setup(setup), layer(false), style(false),
|
||||||
|
format(false), crs(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
QString style(QXmlStreamReader &reader);
|
||||||
|
void getMap(QXmlStreamReader &reader, CTX &ctx);
|
||||||
|
void request(QXmlStreamReader &reader, CTX &ctx);
|
||||||
|
void layer(QXmlStreamReader &reader, CTX &ctx, const QList<QString> &pCRSs,
|
||||||
|
const QList<QString> &pStyles);
|
||||||
|
void capability(QXmlStreamReader &reader, CTX &ctx);
|
||||||
|
void capabilities(QXmlStreamReader &reader, CTX &ctx);
|
||||||
|
bool parseCapabilities(const QString &path, const Setup &setup);
|
||||||
|
bool getCapabilities(const QString &url, const QString &file);
|
||||||
|
|
||||||
|
Projection _projection;
|
||||||
|
RangeF _scaleDenominator;
|
||||||
|
QRectF _boundingBox;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
QString _errorString;
|
||||||
|
|
||||||
|
static Downloader *_downloader;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WMS_H
|
230
src/map/wmsmap.cpp
Normal file
230
src/map/wmsmap.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
#include <QDir>
|
||||||
|
#include <QPainter>
|
||||||
|
#include "common/wgs84.h"
|
||||||
|
#include "common/rectc.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "downloader.h"
|
||||||
|
#include "wmsmap.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define CAPABILITIES_FILE "capabilities.xml"
|
||||||
|
#define TILE_SIZE 256
|
||||||
|
|
||||||
|
qreal WMSMap::sd2res(qreal scaleDenominator) const
|
||||||
|
{
|
||||||
|
return scaleDenominator * 0.28e-3 * _projection.units().fromMeters(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WMSMap::tileUrl() const
|
||||||
|
{
|
||||||
|
return QString("%1?version=1.3.0&request=GetMap&CRS=%2&bbox=$bbox"
|
||||||
|
"&width=%3&height=%4&layers=%5&styles=%6&format=%7&transparent=true")
|
||||||
|
.arg(_setup.url(), _setup.crs(), QString::number(TILE_SIZE),
|
||||||
|
QString::number(TILE_SIZE), _setup.layer(), _setup.style(),
|
||||||
|
_setup.format());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WMSMap::tilesDir() const
|
||||||
|
{
|
||||||
|
return QString(TILES_DIR + "/" + _name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::computeZooms(const RangeF &scaleDenominator)
|
||||||
|
{
|
||||||
|
_zooms.clear();
|
||||||
|
|
||||||
|
if (scaleDenominator.size() > 0) {
|
||||||
|
qreal ld = log2(scaleDenominator.max()) - log2(scaleDenominator.min());
|
||||||
|
int cld = ceil(ld);
|
||||||
|
qreal step = ld / (qreal)cld;
|
||||||
|
qreal lmax = log2(scaleDenominator.max());
|
||||||
|
for (int i = 0; i <= cld; i++)
|
||||||
|
_zooms.append(pow(2.0, lmax - i * step));
|
||||||
|
} else
|
||||||
|
_zooms.append(scaleDenominator.min());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::updateTransform()
|
||||||
|
{
|
||||||
|
qreal scaleDenominator = _zooms.at(_zoom);
|
||||||
|
ReferencePoint tl, br;
|
||||||
|
|
||||||
|
qreal pixelSpan = sd2res(scaleDenominator);
|
||||||
|
if (_projection.isGeographic())
|
||||||
|
pixelSpan /= deg2rad(WGS84_RADIUS);
|
||||||
|
|
||||||
|
tl.xy = QPoint(0, 0);
|
||||||
|
tl.pp = _boundingBox.topLeft();
|
||||||
|
br.xy = QPoint(_boundingBox.width() / pixelSpan, -_boundingBox.height()
|
||||||
|
/ pixelSpan);
|
||||||
|
br.pp = _boundingBox.bottomRight();
|
||||||
|
|
||||||
|
QList<ReferencePoint> points;
|
||||||
|
points << tl << br;
|
||||||
|
_transform = Transform(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WMSMap::loadWMS()
|
||||||
|
{
|
||||||
|
QString file = tilesDir() + "/" + CAPABILITIES_FILE;
|
||||||
|
|
||||||
|
WMS wms(file, _setup);
|
||||||
|
if (!wms.isValid()) {
|
||||||
|
_errorString = wms.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_projection = wms.projection();
|
||||||
|
_boundingBox = wms.boundingBox();
|
||||||
|
_tileLoader = TileLoader(tileUrl(), tilesDir());
|
||||||
|
|
||||||
|
computeZooms(wms.scaleDenominator());
|
||||||
|
updateTransform();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WMSMap::WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent)
|
||||||
|
: Map(parent), _name(name), _setup(setup), _zoom(0), _block(false),
|
||||||
|
_valid(false)
|
||||||
|
{
|
||||||
|
if (!QDir().mkpath(tilesDir())) {
|
||||||
|
_errorString = "Error creating tiles dir";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_valid = loadWMS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::clearCache()
|
||||||
|
{
|
||||||
|
_tileLoader.clearCache();
|
||||||
|
_zoom = 0;
|
||||||
|
|
||||||
|
if (!loadWMS())
|
||||||
|
qWarning("%s: %s\n", qPrintable(_name), qPrintable(_errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::load()
|
||||||
|
{
|
||||||
|
connect(TileLoader::downloader(), SIGNAL(finished()), this,
|
||||||
|
SLOT(emitLoaded()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::unload()
|
||||||
|
{
|
||||||
|
disconnect(TileLoader::downloader(), SIGNAL(finished()), this,
|
||||||
|
SLOT(emitLoaded()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::emitLoaded()
|
||||||
|
{
|
||||||
|
emit loaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF WMSMap::bounds() const
|
||||||
|
{
|
||||||
|
qreal pixelSpan = sd2res(_zooms.at(_zoom));
|
||||||
|
if (_projection.isGeographic())
|
||||||
|
pixelSpan /= deg2rad(WGS84_RADIUS);
|
||||||
|
QSizeF size(_boundingBox.width() / pixelSpan, -_boundingBox.height()
|
||||||
|
/ pixelSpan);
|
||||||
|
|
||||||
|
return QRectF(QPointF(0, 0), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal WMSMap::resolution(const QRectF &rect) const
|
||||||
|
{
|
||||||
|
Coordinates tl = xy2ll((rect.topLeft()));
|
||||||
|
Coordinates br = xy2ll(rect.bottomRight());
|
||||||
|
|
||||||
|
qreal ds = tl.distanceTo(br);
|
||||||
|
qreal ps = QLineF(rect.topLeft(), rect.bottomRight()).length();
|
||||||
|
|
||||||
|
return ds/ps;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WMSMap::zoomFit(const QSize &size, const RectC &br)
|
||||||
|
{
|
||||||
|
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()));
|
||||||
|
if (_projection.isGeographic())
|
||||||
|
resolution *= deg2rad(WGS84_RADIUS);
|
||||||
|
|
||||||
|
_zoom = 0;
|
||||||
|
for (int i = 0; i < _zooms.size(); i++) {
|
||||||
|
if (sd2res(_zooms.at(i)) < resolution)
|
||||||
|
break;
|
||||||
|
_zoom = i;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
_zoom = _zooms.size() - 1;
|
||||||
|
|
||||||
|
updateTransform();
|
||||||
|
return _zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WMSMap::zoomIn()
|
||||||
|
{
|
||||||
|
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
|
||||||
|
updateTransform();
|
||||||
|
return _zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WMSMap::zoomOut()
|
||||||
|
{
|
||||||
|
_zoom = qMax(_zoom - 1, 0);
|
||||||
|
updateTransform();
|
||||||
|
return _zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF WMSMap::ll2xy(const Coordinates &c) const
|
||||||
|
{
|
||||||
|
return _transform.proj2img(_projection.ll2xy(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
Coordinates WMSMap::xy2ll(const QPointF &p) const
|
||||||
|
{
|
||||||
|
return _projection.xy2ll(_transform.img2proj(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WMSMap::draw(QPainter *painter, const QRectF &rect)
|
||||||
|
{
|
||||||
|
QPoint tl = QPoint((int)floor(rect.left() / (qreal)TILE_SIZE),
|
||||||
|
(int)floor(rect.top() / (qreal)TILE_SIZE));
|
||||||
|
QPoint br = QPoint((int)ceil(rect.right() / (qreal)TILE_SIZE),
|
||||||
|
(int)ceil(rect.bottom() / (qreal)TILE_SIZE));
|
||||||
|
|
||||||
|
QList<Tile> tiles;
|
||||||
|
for (int i = tl.x(); i < br.x(); i++) {
|
||||||
|
for (int j = tl.y(); j < br.y(); j++) {
|
||||||
|
QPointF ttl(_transform.img2proj(QPointF(i * TILE_SIZE,
|
||||||
|
j * TILE_SIZE)));
|
||||||
|
QPointF tbr(_transform.img2proj(QPointF(i * TILE_SIZE + TILE_SIZE
|
||||||
|
- 1, j * TILE_SIZE + TILE_SIZE - 1)));
|
||||||
|
QRectF bbox = _setup.yx()
|
||||||
|
? QRectF(QPointF(tbr.y(), tbr.x()), QPointF(ttl.y(), ttl.x()))
|
||||||
|
: QRectF(ttl, tbr);
|
||||||
|
|
||||||
|
tiles.append(Tile(QPoint(i, j), _zoom, bbox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() * TILE_SIZE, t.xy().y() * TILE_SIZE);
|
||||||
|
if (t.pixmap().isNull())
|
||||||
|
painter->fillRect(QRect(tp, QSize(TILE_SIZE, TILE_SIZE)),
|
||||||
|
_backgroundColor);
|
||||||
|
else
|
||||||
|
painter->drawPixmap(tp, t.pixmap());
|
||||||
|
}
|
||||||
|
}
|
73
src/map/wmsmap.h
Normal file
73
src/map/wmsmap.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#ifndef WMSMAP_H
|
||||||
|
#define WMSMAP_H
|
||||||
|
|
||||||
|
#include "transform.h"
|
||||||
|
#include "projection.h"
|
||||||
|
#include "map.h"
|
||||||
|
#include "wms.h"
|
||||||
|
#include "tileloader.h"
|
||||||
|
|
||||||
|
|
||||||
|
class WMSMap : public Map
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent = 0);
|
||||||
|
|
||||||
|
const QString &name() const {return _name;}
|
||||||
|
|
||||||
|
QRectF bounds() const;
|
||||||
|
qreal resolution(const QRectF &rect) const;
|
||||||
|
|
||||||
|
int zoom() const {return _zoom;}
|
||||||
|
int zoomFit(const QSize &size, const RectC &br);
|
||||||
|
int zoomIn();
|
||||||
|
int zoomOut();
|
||||||
|
|
||||||
|
QPointF ll2xy(const Coordinates &c)
|
||||||
|
{return static_cast<const WMSMap &>(*this).ll2xy(c);}
|
||||||
|
Coordinates xy2ll(const QPointF &p)
|
||||||
|
{return static_cast<const WMSMap &>(*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:
|
||||||
|
QString tileUrl() const;
|
||||||
|
qreal sd2res(qreal scaleDenominator) const;
|
||||||
|
QString tilesDir() const;
|
||||||
|
void computeZooms(const RangeF &scaleDenominator);
|
||||||
|
void updateTransform();
|
||||||
|
bool loadWMS();
|
||||||
|
|
||||||
|
QPointF ll2xy(const Coordinates &c) const;
|
||||||
|
Coordinates xy2ll(const QPointF &p) const;
|
||||||
|
|
||||||
|
QString _name;
|
||||||
|
|
||||||
|
WMS::Setup _setup;
|
||||||
|
TileLoader _tileLoader;
|
||||||
|
Projection _projection;
|
||||||
|
Transform _transform;
|
||||||
|
QVector<qreal> _zooms;
|
||||||
|
int _zoom;
|
||||||
|
QRectF _boundingBox;
|
||||||
|
bool _block;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
QString _errorString;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WMSMAP_H
|
118
src/map/wmts.cpp
118
src/map/wmts.cpp
@ -8,57 +8,12 @@
|
|||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
#include "downloader.h"
|
#include "downloader.h"
|
||||||
#include "pcs.h"
|
#include "pcs.h"
|
||||||
|
#include "crs.h"
|
||||||
#include "wmts.h"
|
#include "wmts.h"
|
||||||
|
|
||||||
|
|
||||||
Downloader *WMTS::_downloader = 0;
|
Downloader *WMTS::_downloader = 0;
|
||||||
|
|
||||||
bool WMTS::createProjection(const QString &crs)
|
|
||||||
{
|
|
||||||
QStringList list(crs.split(':'));
|
|
||||||
QString authority, code;
|
|
||||||
bool res;
|
|
||||||
int epsg;
|
|
||||||
const PCS *pcs;
|
|
||||||
const GCS *gcs;
|
|
||||||
|
|
||||||
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") {
|
|
||||||
epsg = code.toInt(&res);
|
|
||||||
if (!res)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ((pcs = PCS::pcs(epsg))) {
|
|
||||||
_projection = Projection(pcs->gcs(), pcs->method(), pcs->setup(),
|
|
||||||
pcs->units());
|
|
||||||
return true;
|
|
||||||
} else if ((gcs = GCS::gcs(epsg))) {
|
|
||||||
_projection = Projection(gcs);
|
|
||||||
return true;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
} else if (authority == "OGC") {
|
|
||||||
if (code == "CRS84") {
|
|
||||||
_projection = Projection(GCS::gcs(4326));
|
|
||||||
return true;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
WMTS::TileMatrix WMTS::tileMatrix(QXmlStreamReader &reader, bool yx)
|
WMTS::TileMatrix WMTS::tileMatrix(QXmlStreamReader &reader, bool yx)
|
||||||
{
|
{
|
||||||
TileMatrix matrix;
|
TileMatrix matrix;
|
||||||
@ -104,12 +59,12 @@ void WMTS::tileMatrixSet(QXmlStreamReader &reader, CTX &ctx)
|
|||||||
else if (reader.name() == "SupportedCRS")
|
else if (reader.name() == "SupportedCRS")
|
||||||
crs = reader.readElementText();
|
crs = reader.readElementText();
|
||||||
else if (reader.name() == "TileMatrix")
|
else if (reader.name() == "TileMatrix")
|
||||||
matrixes.insert(tileMatrix(reader, ctx.setup.yx));
|
matrixes.insert(tileMatrix(reader, ctx.setup.yx()));
|
||||||
else
|
else
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id == ctx.setup.set) {
|
if (id == ctx.setup.set()) {
|
||||||
ctx.crs = crs;
|
ctx.crs = crs;
|
||||||
_matrixes = matrixes;
|
_matrixes = matrixes;
|
||||||
}
|
}
|
||||||
@ -168,7 +123,7 @@ void WMTS::tileMatrixSetLink(QXmlStreamReader &reader, CTX &ctx)
|
|||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id == ctx.setup.set) {
|
if (id == ctx.setup.set()) {
|
||||||
ctx.set = true;
|
ctx.set = true;
|
||||||
_limits = limits;
|
_limits = limits;
|
||||||
}
|
}
|
||||||
@ -232,14 +187,14 @@ void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
|
|||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id == ctx.setup.layer) {
|
if (id == ctx.setup.layer()) {
|
||||||
ctx.layer = true;
|
ctx.layer = true;
|
||||||
_bounds = bounds;
|
_bounds = bounds;
|
||||||
if (ctx.setup.rest)
|
if (ctx.setup.rest())
|
||||||
_tileUrl = tpl;
|
_tileUrl = tpl;
|
||||||
if (styles.contains(ctx.setup.style))
|
if (styles.contains(ctx.setup.style()))
|
||||||
ctx.style = true;
|
ctx.style = true;
|
||||||
if (formats.contains(ctx.setup.format))
|
if (formats.contains(ctx.setup.format()))
|
||||||
ctx.format = true;
|
ctx.format = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,26 +246,27 @@ bool WMTS::parseCapabilities(const QString &path, const Setup &setup)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx.layer) {
|
if (!ctx.layer) {
|
||||||
_errorString = ctx.setup.layer + ": layer not provided";
|
_errorString = ctx.setup.layer() + ": layer not provided";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ctx.style) {
|
if (!ctx.style) {
|
||||||
_errorString = ctx.setup.style + ": style not provided";
|
_errorString = ctx.setup.style() + ": style not provided";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ctx.setup.rest && !ctx.format) {
|
if (!ctx.setup.rest() && !ctx.format) {
|
||||||
_errorString = ctx.setup.format + ": format not provided";
|
_errorString = ctx.setup.format() + ": format not provided";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ctx.set) {
|
if (!ctx.set) {
|
||||||
_errorString = ctx.setup.set + ": set not provided";
|
_errorString = ctx.setup.set() + ": set not provided";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ctx.crs.isNull()) {
|
if (ctx.crs.isNull()) {
|
||||||
_errorString = "Missing CRS definition";
|
_errorString = "Missing CRS definition";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!createProjection(ctx.crs)) {
|
_projection = CRS::projection(ctx.crs);
|
||||||
|
if (_projection.isNull()) {
|
||||||
_errorString = ctx.crs + ": unknown CRS";
|
_errorString = ctx.crs + ": unknown CRS";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -318,7 +274,7 @@ bool WMTS::parseCapabilities(const QString &path, const Setup &setup)
|
|||||||
_errorString = "No usable tile matrix found";
|
_errorString = "No usable tile matrix found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ctx.setup.rest && _tileUrl.isNull()) {
|
if (ctx.setup.rest() && _tileUrl.isNull()) {
|
||||||
_errorString = "Missing tile URL template";
|
_errorString = "Missing tile URL template";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -345,41 +301,41 @@ bool WMTS::getCapabilities(const QString &url, const QString &file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WMTS::load(const QString &file, const WMTS::Setup &setup)
|
WMTS::WMTS(const QString &file, const WMTS::Setup &setup) : _valid(false)
|
||||||
{
|
{
|
||||||
QString capaUrl = setup.rest ? setup.url :
|
QString capaUrl = setup.rest() ? setup.url() :
|
||||||
QString("%1?service=WMTS&Version=1.0.0&request=GetCapabilities")
|
QString("%1?service=WMTS&Version=1.0.0&request=GetCapabilities")
|
||||||
.arg(setup.url);
|
.arg(setup.url());
|
||||||
|
|
||||||
if (!QFileInfo(file).exists())
|
if (!QFileInfo(file).exists())
|
||||||
if (!getCapabilities(capaUrl, file))
|
if (!getCapabilities(capaUrl, file))
|
||||||
return false;
|
return;
|
||||||
if (!parseCapabilities(file, setup))
|
if (!parseCapabilities(file, setup))
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if (!setup.rest) {
|
if (!setup.rest()) {
|
||||||
_tileUrl = QString("%1?service=WMTS&Version=1.0.0&request=GetTile"
|
_tileUrl = QString("%1?service=WMTS&Version=1.0.0&request=GetTile"
|
||||||
"&Format=%2&Layer=%3&Style=%4&TileMatrixSet=%5&TileMatrix=$z"
|
"&Format=%2&Layer=%3&Style=%4&TileMatrixSet=%5&TileMatrix=$z"
|
||||||
"&TileRow=$y&TileCol=$x").arg(setup.url).arg(setup.format)
|
"&TileRow=$y&TileCol=$x").arg(setup.url()).arg(setup.format())
|
||||||
.arg(setup.layer).arg(setup.style).arg(setup.set);
|
.arg(setup.layer()).arg(setup.style()).arg(setup.set());
|
||||||
for (int i = 0; i < setup.dimensions.size(); i++) {
|
for (int i = 0; i < setup.dimensions().size(); i++) {
|
||||||
const QPair<QString, QString> &dim = setup.dimensions.at(i);
|
const QPair<QString, QString> &dim = setup.dimensions().at(i);
|
||||||
_tileUrl.append(QString("&%1=%2").arg(dim.first).arg(dim.second));
|
_tileUrl.append(QString("&%1=%2").arg(dim.first).arg(dim.second));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_tileUrl.replace("{Style}", setup.style, Qt::CaseInsensitive);
|
_tileUrl.replace("{Style}", setup.style(), Qt::CaseInsensitive);
|
||||||
_tileUrl.replace("{TileMatrixSet}", setup.set, Qt::CaseInsensitive);
|
_tileUrl.replace("{TileMatrixSet}", setup.set(), Qt::CaseInsensitive);
|
||||||
_tileUrl.replace("{TileMatrix}", "$z", Qt::CaseInsensitive);
|
_tileUrl.replace("{TileMatrix}", "$z", Qt::CaseInsensitive);
|
||||||
_tileUrl.replace("{TileRow}", "$y", Qt::CaseInsensitive);
|
_tileUrl.replace("{TileRow}", "$y", Qt::CaseInsensitive);
|
||||||
_tileUrl.replace("{TileCol}", "$x", Qt::CaseInsensitive);
|
_tileUrl.replace("{TileCol}", "$x", Qt::CaseInsensitive);
|
||||||
for (int i = 0; i < setup.dimensions.size(); i++) {
|
for (int i = 0; i < setup.dimensions().size(); i++) {
|
||||||
const QPair<QString, QString> &dim = setup.dimensions.at(i);
|
const QPair<QString, QString> &dim = setup.dimensions().at(i);
|
||||||
_tileUrl.replace(QString("{%1}").arg(dim.first), dim.second,
|
_tileUrl.replace(QString("{%1}").arg(dim.first), dim.second,
|
||||||
Qt::CaseInsensitive);
|
Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<WMTS::Zoom> WMTS::zooms() const
|
QList<WMTS::Zoom> WMTS::zooms() const
|
||||||
@ -405,17 +361,17 @@ QList<WMTS::Zoom> WMTS::zooms() const
|
|||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
QDebug operator<<(QDebug dbg, const WMTS::Setup &setup)
|
QDebug operator<<(QDebug dbg, const WMTS::Setup &setup)
|
||||||
{
|
{
|
||||||
dbg.nospace() << "Setup(" << setup.url << ", " << setup.layer << ", "
|
dbg.nospace() << "Setup(" << setup.url() << ", " << setup.layer() << ", "
|
||||||
<< setup.set << ", " << setup.style << ", " << setup.format << ", "
|
<< setup.set() << ", " << setup.style() << ", " << setup.format() << ", "
|
||||||
<< setup.rest << ")";
|
<< setup.rest() << ")";
|
||||||
return dbg.space();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug dbg, const WMTS::Zoom &zoom)
|
QDebug operator<<(QDebug dbg, const WMTS::Zoom &zoom)
|
||||||
{
|
{
|
||||||
dbg.nospace() << "Zoom(" << zoom.id << ", " << zoom.scaleDenominator << ", "
|
dbg.nospace() << "Zoom(" << zoom.id() << ", " << zoom.scaleDenominator()
|
||||||
<< zoom.topLeft << ", " << zoom.tile << ", " << zoom.matrix << ", "
|
<< ", " << zoom.topLeft() << ", " << zoom.tile() << ", " << zoom.matrix()
|
||||||
<< zoom.limits << ")";
|
<< ", " << zoom.limits() << ")";
|
||||||
return dbg.space();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
#endif // QT_NO_DEBUG
|
#endif // QT_NO_DEBUG
|
||||||
|
@ -15,49 +15,73 @@ class QXmlStreamReader;
|
|||||||
class WMTS
|
class WMTS
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Setup {
|
class Setup
|
||||||
QString url;
|
{
|
||||||
QString layer;
|
public:
|
||||||
QString set;
|
|
||||||
QString style;
|
|
||||||
QString format;
|
|
||||||
bool rest;
|
|
||||||
bool yx;
|
|
||||||
QList<QPair<QString, QString> > dimensions;
|
|
||||||
|
|
||||||
Setup(const QString &url, const QString &layer, const QString &set,
|
Setup(const QString &url, const QString &layer, const QString &set,
|
||||||
const QString &style, const QString &format, bool rest, bool yx,
|
const QString &style, const QString &format, bool rest, bool yx,
|
||||||
const QList<QPair<QString, QString> > &dimensions) :
|
const QList<QPair<QString, QString> > &dimensions) :
|
||||||
url(url), layer(layer), set(set), style(style), format(format),
|
_url(url), _layer(layer), _set(set), _style(style), _format(format),
|
||||||
rest(rest), yx(yx), dimensions(dimensions) {}
|
_rest(rest), _yx(yx), _dimensions(dimensions) {}
|
||||||
|
|
||||||
|
const QString &url() const {return _url;}
|
||||||
|
const QString &layer() const {return _layer;}
|
||||||
|
const QString &set() const {return _set;}
|
||||||
|
const QString &style() const {return _style;}
|
||||||
|
const QString &format() const {return _format;}
|
||||||
|
bool rest() const {return _rest;}
|
||||||
|
bool yx() const {return _yx;}
|
||||||
|
const QList<QPair<QString, QString> > &dimensions() const
|
||||||
|
{return _dimensions;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _url;
|
||||||
|
QString _layer;
|
||||||
|
QString _set;
|
||||||
|
QString _style;
|
||||||
|
QString _format;
|
||||||
|
bool _rest;
|
||||||
|
bool _yx;
|
||||||
|
QList<QPair<QString, QString> > _dimensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Zoom {
|
class Zoom
|
||||||
QString id;
|
{
|
||||||
qreal scaleDenominator;
|
public:
|
||||||
QPointF topLeft;
|
|
||||||
QSize tile;
|
|
||||||
QSize matrix;
|
|
||||||
QRect limits;
|
|
||||||
|
|
||||||
Zoom() {}
|
|
||||||
Zoom(const QString &id, qreal scaleDenominator, const QPointF &topLeft,
|
Zoom(const QString &id, qreal scaleDenominator, const QPointF &topLeft,
|
||||||
const QSize &tile, const QSize &matrix, const QRect &limits) :
|
const QSize &tile, const QSize &matrix, const QRect &limits) :
|
||||||
id(id), scaleDenominator(scaleDenominator), topLeft(topLeft),
|
_id(id), _scaleDenominator(scaleDenominator), _topLeft(topLeft),
|
||||||
tile(tile), matrix(matrix), limits(limits) {}
|
_tile(tile), _matrix(matrix), _limits(limits) {}
|
||||||
bool operator<(const Zoom &other) const
|
bool operator<(const Zoom &other) const
|
||||||
{return scaleDenominator > other.scaleDenominator;}
|
{return _scaleDenominator > other._scaleDenominator;}
|
||||||
|
|
||||||
|
const QString &id() const {return _id;}
|
||||||
|
qreal scaleDenominator() const {return _scaleDenominator;}
|
||||||
|
const QPointF &topLeft() const {return _topLeft;}
|
||||||
|
const QSize &tile() const {return _tile;}
|
||||||
|
const QSize &matrix() const {return _matrix;}
|
||||||
|
const QRect &limits() const {return _limits;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _id;
|
||||||
|
qreal _scaleDenominator;
|
||||||
|
QPointF _topLeft;
|
||||||
|
QSize _tile;
|
||||||
|
QSize _matrix;
|
||||||
|
QRect _limits;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool load(const QString &path, const Setup &setup);
|
|
||||||
const QString &errorString() const {return _errorString;}
|
WMTS(const QString &path, const Setup &setup);
|
||||||
|
|
||||||
const RectC &bounds() const {return _bounds;}
|
const RectC &bounds() const {return _bounds;}
|
||||||
QList<Zoom> zooms() const;
|
QList<Zoom> zooms() const;
|
||||||
const Projection &projection() const {return _projection;}
|
const Projection &projection() const {return _projection;}
|
||||||
QString tileUrl() const {return _tileUrl;}
|
const QString &tileUrl() const {return _tileUrl;}
|
||||||
|
|
||||||
|
bool isValid() const {return _valid;}
|
||||||
|
const QString &errorString() const {return _errorString;}
|
||||||
|
|
||||||
static Downloader *downloader() {return _downloader;}
|
|
||||||
static void setDownloader(Downloader *downloader)
|
static void setDownloader(Downloader *downloader)
|
||||||
{_downloader = downloader;}
|
{_downloader = downloader;}
|
||||||
|
|
||||||
@ -113,7 +137,6 @@ private:
|
|||||||
void capabilities(QXmlStreamReader &reader, CTX &ctx);
|
void capabilities(QXmlStreamReader &reader, CTX &ctx);
|
||||||
bool parseCapabilities(const QString &path, const Setup &setup);
|
bool parseCapabilities(const QString &path, const Setup &setup);
|
||||||
bool getCapabilities(const QString &url, const QString &file);
|
bool getCapabilities(const QString &url, const QString &file);
|
||||||
bool createProjection(const QString &crs);
|
|
||||||
|
|
||||||
QSet<TileMatrix> _matrixes;
|
QSet<TileMatrix> _matrixes;
|
||||||
QSet<MatrixLimits> _limits;
|
QSet<MatrixLimits> _limits;
|
||||||
@ -121,6 +144,7 @@ private:
|
|||||||
Projection _projection;
|
Projection _projection;
|
||||||
QString _tileUrl;
|
QString _tileUrl;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
QString _errorString;
|
QString _errorString;
|
||||||
|
|
||||||
static Downloader *_downloader;
|
static Downloader *_downloader;
|
||||||
|
@ -10,30 +10,50 @@
|
|||||||
|
|
||||||
#define CAPABILITIES_FILE "capabilities.xml"
|
#define CAPABILITIES_FILE "capabilities.xml"
|
||||||
|
|
||||||
WMTSMap::WMTSMap(const QString &name, const WMTS::Setup &setup, QObject *parent)
|
bool WMTSMap::loadWMTS()
|
||||||
: Map(parent), _name(name), _setup(setup), _zoom(0), _valid(false)
|
|
||||||
{
|
{
|
||||||
QString dir(TILES_DIR + "/" + _name);
|
QString file = tilesDir() + "/" + CAPABILITIES_FILE;
|
||||||
QString file = dir + "/" + CAPABILITIES_FILE;
|
|
||||||
|
|
||||||
if (!QDir().mkpath(dir)) {
|
WMTS wmts(file, _setup);
|
||||||
|
if (!wmts.isValid()) {
|
||||||
|
_errorString = wmts.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bounds = wmts.bounds();
|
||||||
|
_zooms = wmts.zooms();
|
||||||
|
_projection = wmts.projection();
|
||||||
|
_tileLoader = TileLoader(wmts.tileUrl(), tilesDir());
|
||||||
|
|
||||||
|
updateTransform();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WMTSMap::WMTSMap(const QString &name, const WMTS::Setup &setup, QObject *parent)
|
||||||
|
: Map(parent), _name(name), _setup(setup), _zoom(0), _block(false),
|
||||||
|
_valid(false)
|
||||||
|
{
|
||||||
|
if (!QDir().mkpath(tilesDir())) {
|
||||||
_errorString = "Error creating tiles dir";
|
_errorString = "Error creating tiles dir";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WMTS wmts;
|
_valid = loadWMTS();
|
||||||
if (!wmts.load(file, _setup)) {
|
}
|
||||||
_errorString = wmts.errorString();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_bounds = wmts.bounds();
|
|
||||||
_zooms = wmts.zooms();
|
|
||||||
_projection = wmts.projection();
|
|
||||||
_tileLoader = TileLoader(wmts.tileUrl(), dir);
|
|
||||||
updateTransform();
|
|
||||||
|
|
||||||
_block = false;
|
void WMTSMap::clearCache()
|
||||||
_valid = true;
|
{
|
||||||
|
_tileLoader.clearCache();
|
||||||
|
_zoom = 0;
|
||||||
|
|
||||||
|
if (!loadWMTS())
|
||||||
|
qWarning("%s: %s\n", qPrintable(_name), qPrintable(_errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WMTSMap::tilesDir() const
|
||||||
|
{
|
||||||
|
return QString(TILES_DIR + "/" + _name);
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal WMTSMap::sd2res(qreal scaleDenominator) const
|
qreal WMTSMap::sd2res(qreal scaleDenominator) const
|
||||||
@ -46,17 +66,17 @@ void WMTSMap::updateTransform()
|
|||||||
const WMTS::Zoom &z = _zooms.at(_zoom);
|
const WMTS::Zoom &z = _zooms.at(_zoom);
|
||||||
ReferencePoint tl, br;
|
ReferencePoint tl, br;
|
||||||
|
|
||||||
qreal pixelSpan = sd2res(z.scaleDenominator);
|
qreal pixelSpan = sd2res(z.scaleDenominator());
|
||||||
if (_projection.isGeographic())
|
if (_projection.isGeographic())
|
||||||
pixelSpan /= deg2rad(WGS84_RADIUS);
|
pixelSpan /= deg2rad(WGS84_RADIUS);
|
||||||
QPointF tileSpan(z.tile.width() * pixelSpan, z.tile.height() * pixelSpan);
|
QPointF tileSpan(z.tile().width() * pixelSpan, z.tile().height() * pixelSpan);
|
||||||
QPointF bottomRight(z.topLeft.x() + tileSpan.x() * z.matrix.width(),
|
QPointF bottomRight(z.topLeft().x() + tileSpan.x() * z.matrix().width(),
|
||||||
z.topLeft.y() - tileSpan.y() * z.matrix.height());
|
z.topLeft().y() - tileSpan.y() * z.matrix().height());
|
||||||
|
|
||||||
tl.xy = QPoint(0, 0);
|
tl.xy = QPoint(0, 0);
|
||||||
tl.pp = z.topLeft;
|
tl.pp = z.topLeft();
|
||||||
br.xy = QPoint(z.tile.width() * z.matrix.width(),
|
br.xy = QPoint(z.tile().width() * z.matrix().width(),
|
||||||
z.tile.height() * z.matrix.height());
|
z.tile().height() * z.matrix().height());
|
||||||
br.pp = bottomRight;
|
br.pp = bottomRight;
|
||||||
|
|
||||||
QList<ReferencePoint> points;
|
QList<ReferencePoint> points;
|
||||||
@ -76,26 +96,6 @@ void WMTSMap::unload()
|
|||||||
SLOT(emitLoaded()));
|
SLOT(emitLoaded()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WMTSMap::clearCache()
|
|
||||||
{
|
|
||||||
QString dir(TILES_DIR + "/" + _name);
|
|
||||||
QString file = dir + "/" + CAPABILITIES_FILE;
|
|
||||||
|
|
||||||
_tileLoader.clearCache();
|
|
||||||
|
|
||||||
WMTS wmts;
|
|
||||||
if (!wmts.load(file, _setup))
|
|
||||||
return;
|
|
||||||
_bounds = wmts.bounds();
|
|
||||||
_zooms = wmts.zooms();
|
|
||||||
_projection = wmts.projection();
|
|
||||||
_tileLoader = TileLoader(wmts.tileUrl(), dir);
|
|
||||||
|
|
||||||
if (_zoom >= _zooms.size())
|
|
||||||
_zoom = _zooms.size() - 1;
|
|
||||||
updateTransform();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WMTSMap::emitLoaded()
|
void WMTSMap::emitLoaded()
|
||||||
{
|
{
|
||||||
emit loaded();
|
emit loaded();
|
||||||
@ -106,11 +106,12 @@ QRectF WMTSMap::bounds() const
|
|||||||
const WMTS::Zoom &z = _zooms.at(_zoom);
|
const WMTS::Zoom &z = _zooms.at(_zoom);
|
||||||
QRectF tileBounds, bounds;
|
QRectF tileBounds, bounds;
|
||||||
|
|
||||||
tileBounds = (z.limits.isNull()) ?
|
tileBounds = (z.limits().isNull()) ?
|
||||||
QRectF(QPointF(0, 0), QSize(z.tile.width() * z.matrix.width(),
|
QRectF(QPointF(0, 0), QSize(z.tile().width() * z.matrix().width(),
|
||||||
z.tile.height() * z.matrix.height())) : QRectF(QPointF(z.limits.left()
|
z.tile().height() * z.matrix().height()))
|
||||||
* z.tile.width(), z.limits.top() * z.tile.height()), QSize(z.tile.width()
|
: QRectF(QPointF(z.limits().left() * z.tile().width(), z.limits().top()
|
||||||
* z.limits.width(), z.tile.height() * z.limits.height()));
|
* z.tile().height()), QSize(z.tile().width() * z.limits().width(),
|
||||||
|
z.tile().height() * z.limits().height()));
|
||||||
|
|
||||||
bounds = _bounds.isValid() ? QRectF(ll2xy(_bounds.topLeft()),
|
bounds = _bounds.isValid() ? QRectF(ll2xy(_bounds.topLeft()),
|
||||||
ll2xy(_bounds.bottomRight())) : QRectF();
|
ll2xy(_bounds.bottomRight())) : QRectF();
|
||||||
@ -130,7 +131,7 @@ int WMTSMap::zoomFit(const QSize &size, const RectC &br)
|
|||||||
|
|
||||||
_zoom = 0;
|
_zoom = 0;
|
||||||
for (int i = 0; i < _zooms.size(); i++) {
|
for (int i = 0; i < _zooms.size(); i++) {
|
||||||
if (sd2res(_zooms.at(i).scaleDenominator) < resolution)
|
if (sd2res(_zooms.at(i).scaleDenominator()) < resolution)
|
||||||
break;
|
break;
|
||||||
_zoom = i;
|
_zoom = i;
|
||||||
}
|
}
|
||||||
@ -169,15 +170,15 @@ int WMTSMap::zoomOut()
|
|||||||
void WMTSMap::draw(QPainter *painter, const QRectF &rect)
|
void WMTSMap::draw(QPainter *painter, const QRectF &rect)
|
||||||
{
|
{
|
||||||
const WMTS::Zoom &z = _zooms.at(_zoom);
|
const WMTS::Zoom &z = _zooms.at(_zoom);
|
||||||
QPoint tl = QPoint((int)floor(rect.left() / (qreal)z.tile.width()),
|
QPoint tl = QPoint((int)floor(rect.left() / (qreal)z.tile().width()),
|
||||||
(int)floor(rect.top() / (qreal)z.tile.height()));
|
(int)floor(rect.top() / (qreal)z.tile().height()));
|
||||||
QPoint br = QPoint((int)ceil(rect.right() / (qreal)z.tile.width()),
|
QPoint br = QPoint((int)ceil(rect.right() / (qreal)z.tile().width()),
|
||||||
(int)ceil(rect.bottom() / (qreal)z.tile.height()));
|
(int)ceil(rect.bottom() / (qreal)z.tile().height()));
|
||||||
|
|
||||||
QList<Tile> tiles;
|
QList<Tile> tiles;
|
||||||
for (int i = tl.x(); i < br.x(); i++)
|
for (int i = tl.x(); i < br.x(); i++)
|
||||||
for (int j = tl.y(); j < br.y(); j++)
|
for (int j = tl.y(); j < br.y(); j++)
|
||||||
tiles.append(Tile(QPoint(i, j), z.id));
|
tiles.append(Tile(QPoint(i, j), z.id()));
|
||||||
|
|
||||||
if (_block)
|
if (_block)
|
||||||
_tileLoader.loadTilesSync(tiles);
|
_tileLoader.loadTilesSync(tiles);
|
||||||
@ -186,9 +187,9 @@ void WMTSMap::draw(QPainter *painter, const QRectF &rect)
|
|||||||
|
|
||||||
for (int i = 0; i < tiles.count(); i++) {
|
for (int i = 0; i < tiles.count(); i++) {
|
||||||
Tile &t = tiles[i];
|
Tile &t = tiles[i];
|
||||||
QPoint tp(t.xy().x() * z.tile.width(), t.xy().y() * z.tile.height());
|
QPoint tp(t.xy().x() * z.tile().width(), t.xy().y() * z.tile().height());
|
||||||
if (t.pixmap().isNull())
|
if (t.pixmap().isNull())
|
||||||
painter->fillRect(QRect(tp, z.tile), _backgroundColor);
|
painter->fillRect(QRect(tp, z.tile()), _backgroundColor);
|
||||||
else
|
else
|
||||||
painter->drawPixmap(tp, t.pixmap());
|
painter->drawPixmap(tp, t.pixmap());
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,9 @@ private slots:
|
|||||||
void emitLoaded();
|
void emitLoaded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool loadWMTS();
|
||||||
qreal sd2res(qreal scaleDenominator) const;
|
qreal sd2res(qreal scaleDenominator) const;
|
||||||
|
QString tilesDir() const;
|
||||||
void updateTransform();
|
void updateTransform();
|
||||||
|
|
||||||
QPointF ll2xy(const Coordinates &c) const;
|
QPointF ll2xy(const Coordinates &c) const;
|
||||||
|
Loading…
Reference in New Issue
Block a user