Now using a strict horizontal map scale
[GPXSee.git] / src / map / wmsmap.cpp
blob1ffefeb5f0b779dbff25a57f78c0c715d1a41f9f
1 #include <QDir>
2 #include <QPainter>
3 #include "common/wgs84.h"
4 #include "common/rectc.h"
5 #include "config.h"
6 #include "downloader.h"
7 #include "tileloader.h"
8 #include "wmsmap.h"
11 #define CAPABILITIES_FILE "capabilities.xml"
12 #define TILE_SIZE 256
14 double WMSMap::sd2res(double scaleDenominator) const
16 return scaleDenominator * 0.28e-3 * _projection.units().fromMeters(1.0);
19 QString WMSMap::tileUrl(const QString &version) const
21 QString url;
23 url = QString("%1?version=%2&request=GetMap&bbox=$bbox"
24 "&width=%3&height=%4&layers=%5&styles=%6&format=%7&transparent=true")
25 .arg(_setup.url(), version, QString::number(TILE_SIZE),
26 QString::number(TILE_SIZE), _setup.layer(), _setup.style(),
27 _setup.format());
29 if (version >= "1.3.0")
30 url.append(QString("&CRS=%1").arg(_setup.crs()));
31 else
32 url.append(QString("&SRS=%1").arg(_setup.crs()));
34 for (int i = 0; i < _setup.dimensions().size(); i++) {
35 const QPair<QString, QString> &dim = _setup.dimensions().at(i);
36 url.append(QString("&%1=%2").arg(dim.first, dim.second));
39 return url;
42 QString WMSMap::tilesDir() const
44 return QString(TILES_DIR + "/" + _name);
47 void WMSMap::computeZooms(const RangeF &scaleDenominator)
49 _zooms.clear();
51 if (scaleDenominator.size() > 0) {
52 double ld = log2(scaleDenominator.max()) - log2(scaleDenominator.min());
53 int cld = ceil(ld);
54 double step = ld / (qreal)cld;
55 qreal lmax = log2(scaleDenominator.max());
56 for (int i = 0; i <= cld; i++)
57 _zooms.append(pow(2.0, lmax - i * step));
58 } else
59 _zooms.append(scaleDenominator.min());
62 void WMSMap::updateTransform()
64 double pixelSpan = sd2res(_zooms.at(_zoom));
65 if (_projection.isGeographic())
66 pixelSpan /= deg2rad(WGS84_RADIUS);
67 double sx = _bbox.width() / pixelSpan;
68 double sy = _bbox.height() / pixelSpan;
70 ReferencePoint tl(PointD(0, 0), _bbox.topLeft());
71 ReferencePoint br(PointD(sx, sy), _bbox.bottomRight());
72 _transform = Transform(tl, br);
75 bool WMSMap::loadWMS()
77 QString file = tilesDir() + "/" + CAPABILITIES_FILE;
79 WMS wms(file, _setup);
80 if (!wms.isValid()) {
81 _errorString = wms.errorString();
82 return false;
85 _projection = wms.projection();
86 _bbox = RectD(_projection.ll2xy(wms.boundingBox().topLeft()),
87 _projection.ll2xy(wms.boundingBox().bottomRight()));
88 _tileLoader->setUrl(tileUrl(wms.version()));
90 if (wms.version() >= "1.3.0") {
91 if (_setup.coordinateSystem().axisOrder() == CoordinateSystem::Unknown)
92 _cs = _projection.coordinateSystem();
93 else
94 _cs = _setup.coordinateSystem();
95 } else
96 _cs = CoordinateSystem::XY;
98 computeZooms(wms.scaleDenominator());
99 updateTransform();
101 return true;
104 WMSMap::WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent)
105 : Map(parent), _name(name), _setup(setup), _zoom(0), _valid(false)
107 if (!QDir().mkpath(tilesDir())) {
108 _errorString = "Error creating tiles dir";
109 return;
112 _tileLoader = new TileLoader(this);
113 _tileLoader->setDir(tilesDir());
114 _tileLoader->setAuthorization(_setup.authorization());
115 connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(loaded()));
117 _valid = loadWMS();
120 void WMSMap::clearCache()
122 _tileLoader->clearCache();
123 _zoom = 0;
125 if (!loadWMS())
126 qWarning("%s: %s\n", qPrintable(_name), qPrintable(_errorString));
129 QRectF WMSMap::bounds() const
131 return QRectF(_transform.proj2img(_bbox.topLeft()),
132 _transform.proj2img(_bbox.bottomRight()));
135 int WMSMap::zoomFit(const QSize &size, const RectC &rect)
137 if (rect.isValid()) {
138 PointD tl(_projection.ll2xy(rect.topLeft()));
139 PointD br(_projection.ll2xy(rect.bottomRight()));
140 PointD sc((br.x() - tl.x()) / size.width(), (tl.y() - br.y())
141 / size.height());
142 double resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
143 if (_projection.isGeographic())
144 resolution *= deg2rad(WGS84_RADIUS);
146 _zoom = 0;
147 for (int i = 0; i < _zooms.size(); i++) {
148 if (sd2res(_zooms.at(i)) < resolution)
149 break;
150 _zoom = i;
152 } else
153 _zoom = _zooms.size() - 1;
155 updateTransform();
156 return _zoom;
159 void WMSMap::setZoom(int zoom)
161 _zoom = zoom;
162 updateTransform();
165 int WMSMap::zoomIn()
167 _zoom = qMin(_zoom + 1, _zooms.size() - 1);
168 updateTransform();
169 return _zoom;
172 int WMSMap::zoomOut()
174 _zoom = qMax(_zoom - 1, 0);
175 updateTransform();
176 return _zoom;
179 QPointF WMSMap::ll2xy(const Coordinates &c)
181 return _transform.proj2img(_projection.ll2xy(c));
184 Coordinates WMSMap::xy2ll(const QPointF &p)
186 return _projection.xy2ll(_transform.img2proj(p));
189 void WMSMap::draw(QPainter *painter, const QRectF &rect, bool block)
191 QPoint tl = QPoint((int)floor(rect.left() / (qreal)TILE_SIZE),
192 (int)floor(rect.top() / (qreal)TILE_SIZE));
193 QPoint br = QPoint((int)ceil(rect.right() / (qreal)TILE_SIZE),
194 (int)ceil(rect.bottom() / (qreal)TILE_SIZE));
196 QList<Tile> tiles;
197 for (int i = tl.x(); i < br.x(); i++) {
198 for (int j = tl.y(); j < br.y(); j++) {
199 PointD ttl(_transform.img2proj(QPointF(i * TILE_SIZE,
200 j * TILE_SIZE)));
201 PointD tbr(_transform.img2proj(QPointF(i * TILE_SIZE + TILE_SIZE
202 - 1, j * TILE_SIZE + TILE_SIZE - 1)));
203 RectD bbox = (_cs.axisOrder() == CoordinateSystem::YX)
204 ? RectD(PointD(tbr.y(), tbr.x()), PointD(ttl.y(), ttl.x()))
205 : RectD(ttl, tbr);
207 tiles.append(Tile(QPoint(i, j), _zoom, bbox));
211 if (block)
212 _tileLoader->loadTilesSync(tiles);
213 else
214 _tileLoader->loadTilesAsync(tiles);
216 for (int i = 0; i < tiles.count(); i++) {
217 Tile &t = tiles[i];
218 QPoint tp(t.xy().x() * TILE_SIZE, t.xy().y() * TILE_SIZE);
219 if (!t.pixmap().isNull())
220 painter->drawPixmap(tp, t.pixmap());