Fixed WMS/WMTS URLs composing
[GPXSee.git] / src / map / wmts.cpp
blob4056de6517db8efda3b2d1ede7cba566e64b91f9
1 #include <QXmlStreamReader>
2 #include <QFile>
3 #include <QFileInfo>
4 #include <QEventLoop>
5 #include <QTextStream>
6 #include <QStringList>
7 #include <QtAlgorithms>
8 #include <QXmlStreamReader>
9 #include "downloader.h"
10 #include "pcs.h"
11 #include "crs.h"
12 #include "wmts.h"
15 WMTS::TileMatrix WMTS::tileMatrix(QXmlStreamReader &reader)
17 TileMatrix matrix;
19 while (reader.readNextStartElement()) {
20 if (reader.name() == "Identifier")
21 matrix.id = reader.readElementText();
22 else if (reader.name() == "ScaleDenominator")
23 matrix.scaleDenominator = reader.readElementText().toDouble();
24 else if (reader.name() == "TopLeftCorner") {
25 QString str = reader.readElementText();
26 QTextStream ts(&str);
27 ts >> matrix.topLeft.rx() >> matrix.topLeft.ry();
28 } else if (reader.name() == "TileWidth")
29 matrix.tile.setWidth(reader.readElementText().toInt());
30 else if (reader.name() == "TileHeight")
31 matrix.tile.setHeight(reader.readElementText().toInt());
32 else if (reader.name() == "MatrixWidth")
33 matrix.matrix.setWidth(reader.readElementText().toInt());
34 else if (reader.name() == "MatrixHeight")
35 matrix.matrix.setHeight(reader.readElementText().toInt());
36 else
37 reader.skipCurrentElement();
40 if (!matrix.isValid())
41 reader.raiseError("Invalid TileMatrix definition");
43 return matrix;
46 void WMTS::tileMatrixSet(QXmlStreamReader &reader, CTX &ctx)
48 QString id, crs;
49 QSet<TileMatrix> matrixes;
51 while (reader.readNextStartElement()) {
52 if (reader.name() == "Identifier")
53 id = reader.readElementText();
54 else if (reader.name() == "SupportedCRS")
55 crs = reader.readElementText();
56 else if (reader.name() == "TileMatrix")
57 matrixes.insert(tileMatrix(reader));
58 else
59 reader.skipCurrentElement();
62 if (id == ctx.setup.set()) {
63 ctx.crs = crs;
64 _matrixes = matrixes;
68 WMTS::MatrixLimits WMTS::tileMatrixLimits(QXmlStreamReader &reader)
70 MatrixLimits limits;
72 while (reader.readNextStartElement()) {
73 if (reader.name() == "TileMatrix")
74 limits.id = reader.readElementText();
75 else if (reader.name() == "MinTileRow")
76 limits.rect.setTop(reader.readElementText().toInt());
77 else if (reader.name() == "MaxTileRow")
78 limits.rect.setBottom(reader.readElementText().toInt());
79 else if (reader.name() == "MinTileCol")
80 limits.rect.setLeft(reader.readElementText().toInt());
81 else if (reader.name() == "MaxTileCol")
82 limits.rect.setRight(reader.readElementText().toInt());
83 else
84 reader.skipCurrentElement();
87 if (!limits.isValid())
88 reader.raiseError("Invalid TileMatrixLimits definition");
90 return limits;
93 QSet<WMTS::MatrixLimits> WMTS::tileMatrixSetLimits(QXmlStreamReader &reader)
95 QSet<MatrixLimits> limits;
97 while (reader.readNextStartElement()) {
98 if (reader.name() == "TileMatrixLimits")
99 limits.insert(tileMatrixLimits(reader));
100 else
101 reader.skipCurrentElement();
104 return limits;
107 void WMTS::tileMatrixSetLink(QXmlStreamReader &reader, CTX &ctx)
109 QString id;
110 QSet<MatrixLimits> limits;
112 while (reader.readNextStartElement()) {
113 if (reader.name() == "TileMatrixSet")
114 id = reader.readElementText();
115 else if (reader.name() == "TileMatrixSetLimits")
116 limits = tileMatrixSetLimits(reader);
117 else
118 reader.skipCurrentElement();
121 if (id == ctx.setup.set()) {
122 ctx.hasSet = true;
123 _limits = limits;
127 RectC WMTS::wgs84BoundingBox(QXmlStreamReader &reader)
129 Coordinates topLeft, bottomRight;
131 while (reader.readNextStartElement()) {
132 if (reader.name() == "LowerCorner") {
133 QString str = reader.readElementText();
134 QTextStream(&str) >> topLeft.rlon() >> bottomRight.rlat();
135 } else if (reader.name() == "UpperCorner") {
136 QString str = reader.readElementText();
137 QTextStream(&str) >> bottomRight.rlon() >> topLeft.rlat();
138 } else
139 reader.skipCurrentElement();
142 return RectC(topLeft, bottomRight);
145 QString WMTS::style(QXmlStreamReader &reader)
147 QString id;
149 while (reader.readNextStartElement()) {
150 if (reader.name() == "Identifier")
151 id = reader.readElementText();
152 else
153 reader.skipCurrentElement();
156 return id;
159 void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
161 QString id, tpl;
162 RectC bounds;
163 QStringList formats, styles;
165 while (reader.readNextStartElement()) {
166 if (reader.name() == "Identifier")
167 id = reader.readElementText();
168 else if (reader.name() == "TileMatrixSetLink")
169 tileMatrixSetLink(reader, ctx);
170 else if (reader.name() == "WGS84BoundingBox")
171 bounds = wgs84BoundingBox(reader);
172 else if (reader.name() == "ResourceURL") {
173 const QXmlStreamAttributes &attr = reader.attributes();
174 if (attr.value("resourceType") == "tile")
175 tpl = attr.value("template").toString();
176 reader.skipCurrentElement();
177 } else if (reader.name() == "Style")
178 styles.append(style(reader));
179 else if (reader.name() == "Format")
180 formats.append(reader.readElementText());
181 else
182 reader.skipCurrentElement();
185 if (id == ctx.setup.layer()) {
186 ctx.hasLayer = true;
187 _bounds = bounds;
188 if (ctx.setup.rest())
189 _tileUrl = tpl;
190 if (styles.contains(ctx.setup.style()) || ctx.setup.style().isEmpty())
191 ctx.hasStyle = true;
192 if (formats.contains(ctx.setup.format()))
193 ctx.hasFormat = true;
197 void WMTS::contents(QXmlStreamReader &reader, CTX &ctx)
199 while (reader.readNextStartElement()) {
200 if (reader.name() == "TileMatrixSet")
201 tileMatrixSet(reader, ctx);
202 else if (reader.name() == "Layer")
203 layer(reader, ctx);
204 else
205 reader.skipCurrentElement();
209 void WMTS::capabilities(QXmlStreamReader &reader, CTX &ctx)
211 while (reader.readNextStartElement()) {
212 if (reader.name() == "Contents")
213 contents(reader, ctx);
214 else
215 reader.skipCurrentElement();
219 bool WMTS::parseCapabilities(const QString &path, const Setup &setup)
221 QFile file(path);
222 CTX ctx(setup);
223 QXmlStreamReader reader;
225 if (!file.open(QFile::ReadOnly | QFile::Text)) {
226 _errorString = file.errorString();
227 return false;
230 reader.setDevice(&file);
231 if (reader.readNextStartElement()) {
232 if (reader.name() == "Capabilities")
233 capabilities(reader, ctx);
234 else
235 reader.raiseError("Not a Capabilities XML file");
237 if (reader.error()) {
238 _errorString = QString("%1:%2: %3").arg(path).arg(reader.lineNumber())
239 .arg(reader.errorString());
240 return false;
243 if (!ctx.hasLayer) {
244 _errorString = ctx.setup.layer() + ": layer not provided";
245 return false;
247 if (!ctx.hasStyle) {
248 _errorString = ctx.setup.style() + ": style not provided";
249 return false;
251 if (!ctx.setup.rest() && !ctx.hasFormat) {
252 _errorString = ctx.setup.format() + ": format not provided";
253 return false;
255 if (!ctx.hasSet) {
256 _errorString = ctx.setup.set() + ": set not provided";
257 return false;
259 if (ctx.crs.isNull()) {
260 _errorString = "Missing CRS definition";
261 return false;
263 _projection = CRS::projection(ctx.crs);
264 if (!_projection.isValid()) {
265 _errorString = ctx.crs + ": unknown CRS";
266 return false;
268 if (_matrixes.isEmpty()) {
269 _errorString = "No usable tile matrix found";
270 return false;
272 if (ctx.setup.rest() && _tileUrl.isNull()) {
273 _errorString = "Missing tile URL template";
274 return false;
277 return true;
280 bool WMTS::getCapabilities(const QString &url, const QString &file,
281 const Authorization &authorization)
283 Downloader d;
284 QList<Download> dl;
286 dl.append(Download(url, file));
288 QEventLoop wait;
289 QObject::connect(&d, SIGNAL(finished()), &wait, SLOT(quit()));
290 if (d.get(dl, authorization))
291 wait.exec();
293 if (QFileInfo(file).exists())
294 return true;
295 else {
296 _errorString = "Error downloading capabilities XML file";
297 return false;
301 WMTS::WMTS(const QString &file, const WMTS::Setup &setup) : _valid(false)
303 QString capaUrl = setup.rest() ? setup.url() :
304 QString("%1%2service=WMTS&Version=1.0.0&request=GetCapabilities")
305 .arg(setup.url(), setup.url().contains('?') ? "&" : "?");
307 if (!QFileInfo(file).exists())
308 if (!getCapabilities(capaUrl, file, setup.authorization()))
309 return;
310 if (!parseCapabilities(file, setup))
311 return;
313 QString style = setup.style().isEmpty() ? "default" : setup.style();
314 if (!setup.rest()) {
315 _tileUrl = QString("%1%2service=WMTS&Version=1.0.0&request=GetTile"
316 "&Format=%3&Layer=%4&Style=%5&TileMatrixSet=%6&TileMatrix=$z"
317 "&TileRow=$y&TileCol=$x").arg(setup.url(),
318 setup.url().contains('?') ? "&" : "?" , setup.format(),
319 setup.layer(), style, setup.set());
320 for (int i = 0; i < setup.dimensions().size(); i++) {
321 const QPair<QString, QString> &dim = setup.dimensions().at(i);
322 _tileUrl.append(QString("&%1=%2").arg(dim.first, dim.second));
324 } else {
325 _tileUrl.replace("{Style}", style, Qt::CaseInsensitive);
326 _tileUrl.replace("{TileMatrixSet}", setup.set(), Qt::CaseInsensitive);
327 _tileUrl.replace("{TileMatrix}", "$z", Qt::CaseInsensitive);
328 _tileUrl.replace("{TileRow}", "$y", Qt::CaseInsensitive);
329 _tileUrl.replace("{TileCol}", "$x", Qt::CaseInsensitive);
330 for (int i = 0; i < setup.dimensions().size(); i++) {
331 const QPair<QString, QString> &dim = setup.dimensions().at(i);
332 _tileUrl.replace(QString("{%1}").arg(dim.first), dim.second,
333 Qt::CaseInsensitive);
337 _valid = true;
340 QList<WMTS::Zoom> WMTS::zooms() const
342 QList<Zoom> zooms;
343 QSet<TileMatrix>::const_iterator mi;
344 QSet<MatrixLimits>::const_iterator li;
346 for (mi = _matrixes.constBegin(); mi != _matrixes.constEnd(); ++mi) {
347 if ((li = _limits.find(MatrixLimits(mi->id))) == _limits.constEnd())
348 zooms.append(Zoom(mi->id, mi->scaleDenominator, mi->topLeft,
349 mi->tile, mi->matrix, QRect()));
350 else
351 zooms.append(Zoom(mi->id, mi->scaleDenominator, mi->topLeft,
352 mi->tile, mi->matrix, li->rect));
355 qSort(zooms);
357 return zooms;
360 #ifndef QT_NO_DEBUG
361 QDebug operator<<(QDebug dbg, const WMTS::Setup &setup)
363 dbg.nospace() << "Setup(" << setup.url() << ", " << setup.layer() << ", "
364 << setup.set() << ", " << setup.style() << ", " << setup.format() << ", "
365 << setup.rest() << ")";
366 return dbg.space();
369 QDebug operator<<(QDebug dbg, const WMTS::Zoom &zoom)
371 dbg.nospace() << "Zoom(" << zoom.id() << ", " << zoom.scaleDenominator()
372 << ", " << zoom.topLeft() << ", " << zoom.tile() << ", " << zoom.matrix()
373 << ", " << zoom.limits() << ")";
374 return dbg.space();
376 #endif // QT_NO_DEBUG