Optimization
[GPXSee.git] / src / map / wmsmap.cpp
blobb8b6bce7169eb6258d355bc2ba4daab316e2265c
1 #include <QtCore>
2 #include <QDir>
3 #include <QPainter>
4 #include "common/wgs84.h"
5 #include "common/rectc.h"
6 #include "common/programpaths.h"
7 #include "tileloader.h"
8 #include "wmsmap.h"
11 #define CAPABILITIES_FILE "capabilities.xml"
12 #define EPSILON 1e-6
14 double WMSMap::sd2res(double scaleDenominator) const
16 return scaleDenominator * _wms->projection().units().fromMeters(1.0)
17 * 0.28e-3;
20 QString WMSMap::tileUrl() const
22 const WMS::Setup &setup = _wms->setup();
24 QString url = QString("%1%2service=WMS&version=%3&request=GetMap&bbox=$bbox"
25 "&width=%4&height=%5&layers=%6&styles=%7&format=%8&transparent=true")
26 .arg(_wms->getMapUrl(), _wms->getMapUrl().contains('?') ? "&" : "?",
27 _wms->version(), QString::number(_tileSize), QString::number(_tileSize),
28 setup.layer(), setup.style(), setup.format());
30 if (_wms->version() >= "1.3.0")
31 url.append(QString("&CRS=%1").arg(setup.crs()));
32 else
33 url.append(QString("&SRS=%1").arg(setup.crs()));
35 for (int i = 0; i < setup.dimensions().size(); i++) {
36 const KV<QString, QString> &dim = setup.dimensions().at(i);
37 url.append(QString("&%1=%2").arg(dim.key(), dim.value()));
40 return url;
43 void WMSMap::computeZooms()
45 _zooms.clear();
47 const RangeF &sd = _wms->scaleDenominator();
48 if (sd.size() > 0) {
49 double ld = log2(sd.max() - EPSILON) - log2(sd.min() + EPSILON);
50 int cld = (int)ceil(ld);
51 double step = ld / (double)cld;
52 double lmax = log2(sd.max() - EPSILON);
53 for (int i = 0; i <= cld; i++)
54 _zooms.append(pow(2.0, lmax - i * step));
55 } else
56 _zooms.append(sd.min() + EPSILON);
59 void WMSMap::updateTransform()
61 double pixelSpan = sd2res(_zooms.at(_zoom));
62 if (_wms->projection().isGeographic())
63 pixelSpan /= deg2rad(WGS84_RADIUS);
64 _transform = Transform(ReferencePoint(PointD(0, 0),
65 _wms->projection().ll2xy(_wms->bbox().topLeft())),
66 PointD(pixelSpan, pixelSpan));
69 WMSMap::WMSMap(const QString &fileName, const QString &name,
70 const WMS::Setup &setup, int tileSize, QObject *parent)
71 : Map(fileName, parent), _name(name), _tileLoader(0), _zoom(0),
72 _tileSize(tileSize), _mapRatio(1.0)
74 QString tilesDir(QDir(ProgramPaths::tilesDir()).filePath(_name));
76 _tileLoader = new TileLoader(tilesDir, this);
77 _tileLoader->setHeaders(setup.headers());
78 connect(_tileLoader, &TileLoader::finished, this, &WMSMap::tilesLoaded);
80 _wms = new WMS(QDir(tilesDir).filePath(CAPABILITIES_FILE), setup, this);
81 connect(_wms, &WMS::downloadFinished, this, &WMSMap::wmsReady);
82 if (_wms->isReady())
83 init();
86 void WMSMap::init()
88 _tileLoader->setUrl(tileUrl());
89 _bounds = RectD(_wms->bbox(), _wms->projection());
90 computeZooms();
91 updateTransform();
94 void WMSMap::wmsReady()
96 if (_wms->isValid())
97 init();
99 emit mapLoaded();
102 void WMSMap::load(const Projection &in, const Projection &out,
103 qreal deviceRatio, bool hidpi)
105 Q_UNUSED(in);
106 Q_UNUSED(out);
108 _mapRatio = hidpi ? deviceRatio : 1.0;
111 void WMSMap::clearCache()
113 _tileLoader->clearCache();
116 QRectF WMSMap::bounds()
118 return QRectF(_transform.proj2img(_bounds.topLeft()) / _mapRatio,
119 _transform.proj2img(_bounds.bottomRight()) / _mapRatio);
122 int WMSMap::zoomFit(const QSize &size, const RectC &rect)
124 if (rect.isValid()) {
125 RectD prect(rect, _wms->projection());
126 PointD sc(prect.width() / size.width(), prect.height() / size.height());
127 double resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
128 if (_wms->projection().isGeographic())
129 resolution *= deg2rad(WGS84_RADIUS);
131 _zoom = 0;
132 for (int i = 0; i < _zooms.size(); i++) {
133 if (sd2res(_zooms.at(i)) < resolution / _mapRatio)
134 break;
135 _zoom = i;
137 } else
138 _zoom = _zooms.size() - 1;
140 updateTransform();
141 return _zoom;
144 void WMSMap::setZoom(int zoom)
146 _zoom = zoom;
147 updateTransform();
150 int WMSMap::zoomIn()
152 _zoom = qMin(_zoom + 1, _zooms.size() - 1);
153 updateTransform();
154 return _zoom;
157 int WMSMap::zoomOut()
159 _zoom = qMax(_zoom - 1, 0);
160 updateTransform();
161 return _zoom;
164 QPointF WMSMap::ll2xy(const Coordinates &c)
166 return _transform.proj2img(_wms->projection().ll2xy(c)) / _mapRatio;
169 Coordinates WMSMap::xy2ll(const QPointF &p)
171 return _wms->projection().xy2ll(_transform.img2proj(p * _mapRatio));
174 qreal WMSMap::tileSize() const
176 return (_tileSize / _mapRatio);
179 void WMSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
181 QPoint tl = QPoint(qFloor(rect.left() / tileSize()),
182 qFloor(rect.top() / tileSize()));
183 QPoint br = QPoint(qCeil(rect.right() / tileSize()),
184 qCeil(rect.bottom() / tileSize()));
186 QVector<FetchTile> tiles;
187 tiles.reserve((br.x() - tl.x()) * (br.y() - tl.y()));
188 for (int i = tl.x(); i < br.x(); i++) {
189 for (int j = tl.y(); j < br.y(); j++) {
190 PointD ttl(_transform.img2proj(QPointF(i * _tileSize,
191 j * _tileSize)));
192 PointD tbr(_transform.img2proj(QPointF(i * _tileSize + _tileSize,
193 j * _tileSize + _tileSize)));
194 RectD bbox = (_wms->cs().axisOrder() == CoordinateSystem::YX)
195 ? RectD(PointD(tbr.y(), tbr.x()), PointD(ttl.y(), ttl.x()))
196 : RectD(ttl, tbr);
198 tiles.append(FetchTile(QPoint(i, j), _zoom, bbox));
202 if (flags & Map::Block)
203 _tileLoader->loadTilesSync(tiles);
204 else
205 _tileLoader->loadTilesAsync(tiles);
207 for (int i = 0; i < tiles.count(); i++) {
208 FetchTile &t = tiles[i];
209 QPointF tp(t.xy().x() * tileSize(), t.xy().y() * tileSize());
210 if (!t.pixmap().isNull()) {
211 t.pixmap().setDevicePixelRatio(_mapRatio);
212 painter->drawPixmap(tp, t.pixmap());