1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 07:13:21 +02:00
GPXSee/src/map/wms.cpp

307 lines
8.2 KiB
C++
Raw Normal View History

#include <cmath>
#include <QFileInfo>
#include <QEventLoop>
#include <QXmlStreamReader>
2018-04-02 23:33:10 +02:00
#include <QStringList>
#include "downloader.h"
#include "crs.h"
#include "wms.h"
static inline double hint2denominator(double h)
{
/* Some WMS 1.1.1 servers use a 72dpi resolution by default. Using the usual
90dpi (0.28mm) resolution known from later standards (WMS 1.3, WMTS) does
make them return emty images in the "max" scale level. */
return h / (M_SQRT2 * 0.36e-3);
}
2018-04-02 19:59:52 +02:00
WMS::CTX::CTX(const Setup &setup) : setup(setup), formatSupported(false)
{
QStringList ll = setup.layer().split(',');
if (setup.style().isEmpty()) {
for (int i = 0; i < ll.size(); i++)
layers.append(Layer(ll.at(i)));
} else {
QStringList sl = setup.style().split(',');
if (ll.size() != sl.size())
return;
2018-04-02 19:59:52 +02:00
for (int i = 0; i < ll.size(); i++)
layers.append(Layer(ll.at(i), sl.at(i)));
}
2018-04-02 19:59:52 +02:00
}
void WMS::getMap(QXmlStreamReader &reader, CTX &ctx)
{
while (reader.readNextStartElement()) {
if (reader.name() == "Format") {
QString format(reader.readElementText());
if (format.left(format.indexOf(';')) == ctx.setup.format())
2018-04-02 19:59:52 +02:00
ctx.formatSupported = 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;
}
RectC WMS::geographicBoundingBox(QXmlStreamReader &reader)
{
double left = NAN, top = NAN, right = NAN, bottom = NAN;
while (reader.readNextStartElement()) {
if (reader.name() == "westBoundLongitude")
left = reader.readElementText().toDouble();
else if (reader.name() == "eastBoundLongitude")
right = reader.readElementText().toDouble();
else if (reader.name() == "northBoundLatitude")
top = reader.readElementText().toDouble();
else if (reader.name() == "southBoundLatitude")
bottom = reader.readElementText().toDouble();
else
reader.skipCurrentElement();
}
return RectC(Coordinates(left, top), Coordinates(right, bottom));
}
void WMS::layer(QXmlStreamReader &reader, CTX &ctx,
const QList<QString> &pCRSs, const QList<QString> &pStyles,
RangeF &pScaleDenominator, RectC &pBoundingBox)
{
QString name;
QList<QString> CRSs(pCRSs);
QList<QString> styles(pStyles);
RangeF scaleDenominator(pScaleDenominator);
RectC boundingBox(pBoundingBox);
2018-04-02 19:59:52 +02:00
int index;
while (reader.readNextStartElement()) {
if (reader.name() == "Name")
name = reader.readElementText();
else if (reader.name() == "CRS" || reader.name() == "SRS")
CRSs.append(reader.readElementText());
else if (reader.name() == "Style")
styles.append(style(reader));
else if (reader.name() == "ScaleHint") {
QXmlStreamAttributes attr = reader.attributes();
double minHint = attr.value("min").toString().toDouble();
double maxHint = attr.value("max").toString().toDouble();
2020-02-29 21:40:13 +01:00
if (minHint > 0)
scaleDenominator.setMin(hint2denominator(minHint));
if (maxHint > 0)
scaleDenominator.setMax(hint2denominator(maxHint));
reader.skipCurrentElement();
} else if (reader.name() == "MinScaleDenominator") {
double sd = reader.readElementText().toDouble();
if (sd > 0)
scaleDenominator.setMin(sd);
} else if (reader.name() == "MaxScaleDenominator") {
double sd = reader.readElementText().toDouble();
if (sd > 0)
scaleDenominator.setMax(sd);
} else if (reader.name() == "LatLonBoundingBox") {
QXmlStreamAttributes attr = reader.attributes();
boundingBox = RectC(Coordinates(
attr.value("minx").toString().toDouble(),
attr.value("maxy").toString().toDouble()),
Coordinates(attr.value("maxx").toString().toDouble(),
attr.value("miny").toString().toDouble()));
reader.skipCurrentElement();
} else if (reader.name() == "EX_GeographicBoundingBox")
boundingBox = geographicBoundingBox(reader);
else if (reader.name() == "Layer")
layer(reader, ctx, CRSs, styles, scaleDenominator, boundingBox);
else
reader.skipCurrentElement();
}
2018-04-02 19:59:52 +02:00
if ((index = ctx.layers.indexOf(name)) >= 0) {
Layer &layer = ctx.layers[index];
layer.scaleDenominator = scaleDenominator;
layer.boundingBox = boundingBox;
layer.isDefined = true;
layer.hasStyle = styles.contains(layer.style) || layer.style.isEmpty();
2018-04-02 19:59:52 +02:00
layer.hasCRS = CRSs.contains(ctx.setup.crs());
}
}
void WMS::capability(QXmlStreamReader &reader, CTX &ctx)
{
QList<QString> CRSs;
QList<QString> styles;
RangeF scaleDenominator(133.295598991, 559082264.0287178);
RectC boundingBox;
while (reader.readNextStartElement()) {
if (reader.name() == "Layer")
layer(reader, ctx, CRSs, styles, scaleDenominator, boundingBox);
else if (reader.name() == "Request")
request(reader, ctx);
else
reader.skipCurrentElement();
}
}
void WMS::capabilities(QXmlStreamReader &reader, CTX &ctx)
{
2018-03-31 11:27:01 +02:00
_version = reader.attributes().value("version").toString();
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;
2018-04-02 19:59:52 +02:00
if (ctx.layers.isEmpty()) {
_errorString = "Invalid layers/styles list definition";
2018-04-02 19:59:52 +02:00
return false;
}
if (!file.open(QFile::ReadOnly | QFile::Text)) {
_errorString = file.errorString();
return false;
}
reader.setDevice(&file);
if (reader.readNextStartElement()) {
if (reader.name() == "WMS_Capabilities"
|| reader.name() == "WMT_MS_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;
}
2018-04-02 19:59:52 +02:00
if (!ctx.formatSupported) {
_errorString = ctx.setup.format() + ": format not provided";
return false;
}
2018-04-02 19:59:52 +02:00
for (int i = 0; i < ctx.layers.size(); i++) {
const Layer &layer = ctx.layers.at(i);
if (!layer.isDefined) {
2018-04-03 23:05:17 +02:00
_errorString = layer.name + ": layer not provided";
2018-04-02 19:59:52 +02:00
return false;
}
if (!layer.hasStyle) {
2018-04-03 23:05:17 +02:00
_errorString = layer.style + ": style not provided for layer "
2018-04-02 19:59:52 +02:00
+ layer.name;
return false;
}
if (!layer.hasCRS) {
_errorString = ctx.setup.crs() + ": CRS not provided for layer "
+ layer.name;
return false;
}
if (!layer.scaleDenominator.isValid()
|| layer.scaleDenominator.isNull()) {
_errorString = "Invalid scale denominator range for layer "
2018-04-02 19:59:52 +02:00
+ layer.name;
return false;
}
if (!layer.boundingBox.isValid()) {
_errorString = "Invalid/missing bounding box for layer "
2018-04-02 19:59:52 +02:00
+ layer.name;
return false;
}
}
2018-04-02 19:59:52 +02:00
_projection = CRS::projection(ctx.setup.crs());
2018-05-16 18:52:48 +02:00
if (!_projection.isValid()) {
_errorString = ctx.setup.crs() + ": unknown CRS";
return false;
}
_boundingBox = ctx.layers.first().boundingBox;
for (int i = 1; i < ctx.layers.size(); i++)
_boundingBox &= ctx.layers.at(i).boundingBox;
if (_boundingBox.isNull()) {
_errorString = "Empty layers bounding box join";
return false;
}
_scaleDenominator = ctx.layers.first().scaleDenominator;
for (int i = 1; i < ctx.layers.size(); i++)
_scaleDenominator &= ctx.layers.at(i).scaleDenominator;
if (_scaleDenominator.isNull()) {
_errorString = "Empty layers scale denominator range join";
return false;
}
return true;
}
bool WMS::getCapabilities(const QString &url, const QString &file,
const Authorization &authorization)
{
Downloader d;
QList<Download> dl;
dl.append(Download(url, file));
QEventLoop wait;
QObject::connect(&d, SIGNAL(finished()), &wait, SLOT(quit()));
if (d.get(dl, authorization))
wait.exec();
2019-03-05 23:05:50 +01:00
if (!QFileInfo(file).exists()) {
_errorString = "Error downloading capabilities XML file";
return false;
}
2019-03-05 22:34:50 +01:00
return true;
}
WMS::WMS(const QString &file, const WMS::Setup &setup) : _valid(false)
{
2018-09-16 12:05:11 +02:00
QString capaUrl = QString("%1%2service=WMS&request=GetCapabilities")
.arg(setup.url(), setup.url().contains('?') ? "&" : "?");
2019-03-05 23:05:50 +01:00
if (!QFileInfo(file).exists())
if (!getCapabilities(capaUrl, file, setup.authorization()))
return;
if (!parseCapabilities(file, setup))
return;
_valid = true;
}