From f9a921e84f19c52d7ea7452a47f8aa0c0417852b Mon Sep 17 00:00:00 2001 From: juhvu Date: Tue, 14 Jun 2011 15:51:19 +1000 Subject: Map3D object visible in QML. --- src/imports/location/location.pro | 16 +- .../location/qdeclarative3dgraphicsgeomap.cpp | 141 +++++++++++-- .../location/qdeclarative3dgraphicsgeomap_p.h | 18 +- src/imports/location/tile.cpp | 27 +++ src/imports/location/tile.h | 25 +++ src/imports/location/tilecache.cpp | 142 +++++++++++++ src/imports/location/tilecache.h | 46 +++++ src/imports/location/tileprovider.cpp | 62 ++++++ src/imports/location/tileprovider.h | 34 ++++ src/imports/location/tilespec.cpp | 56 ++++++ src/imports/location/tilespec.h | 27 +++ src/imports/location/tilesphere.cpp | 222 +++++++++++++++++++++ src/imports/location/tilesphere.h | 58 ++++++ 13 files changed, 845 insertions(+), 29 deletions(-) create mode 100644 src/imports/location/tile.cpp create mode 100644 src/imports/location/tile.h create mode 100644 src/imports/location/tilecache.cpp create mode 100644 src/imports/location/tilecache.h create mode 100644 src/imports/location/tileprovider.cpp create mode 100644 src/imports/location/tileprovider.h create mode 100644 src/imports/location/tilespec.cpp create mode 100644 src/imports/location/tilespec.h create mode 100644 src/imports/location/tilesphere.cpp create mode 100644 src/imports/location/tilesphere.h diff --git a/src/imports/location/location.pro b/src/imports/location/location.pro index aa875d13..d625f368 100644 --- a/src/imports/location/location.pro +++ b/src/imports/location/location.pro @@ -7,6 +7,7 @@ TARGETPATH = Qt/location include(../qimportbase.pri) QT += declarative network location +CONFIG += qt3d DESTDIR = $$QT.location.imports/$$TARGETPATH target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH @@ -54,7 +55,12 @@ HEADERS += qdeclarativeposition_p.h \ qdeclarativegeomaneuver_p.h \ qdeclarativegeomaprouteobject_p.h \ qdeclarativegeoboundingcircle_p.h \ - qdeclarative3dgraphicsgeomap_p.h + qdeclarative3dgraphicsgeomap_p.h \ + tilecache.h \ + tile.h \ + tilesphere.h \ + tileprovider.h \ + tilespec.h SOURCES += qdeclarativeposition.cpp \ location.cpp \ @@ -88,8 +94,12 @@ SOURCES += qdeclarativeposition.cpp \ qdeclarativegeomaneuver.cpp \ qdeclarativegeomaprouteobject.cpp \ qdeclarativegeoboundingcircle.cpp \ - qdeclarative3dgraphicsgeomap.cpp - + qdeclarative3dgraphicsgeomap.cpp \ + tilecache.cpp \ + tile.cpp \ + tilesphere.cpp \ + tileprovider.cpp \ + tilespec.cpp # Tell qmake to create such makefile that qmldir and target (i.e. declarative_location) # are both copied to qt/imports/QtMobility/location -directory, diff --git a/src/imports/location/qdeclarative3dgraphicsgeomap.cpp b/src/imports/location/qdeclarative3dgraphicsgeomap.cpp index b3cf0202..f421a591 100644 --- a/src/imports/location/qdeclarative3dgraphicsgeomap.cpp +++ b/src/imports/location/qdeclarative3dgraphicsgeomap.cpp @@ -45,6 +45,15 @@ #include "qdeclarativecoordinate_p.h" #include "qdeclarativegeoserviceprovider_p.h" #include "qdeclarativelandmark_p.h" +#include "qglview.h" +#include "qglsubsurface.h" +#include "tilecache.h" +#include "tile.h" +#include "tilesphere.h" + +#include +#include +#include #include #include @@ -89,7 +98,7 @@ QTM_BEGIN_NAMESPACE Mouse handling is done by adding MapMouseArea items as children of either MapObjects or the Map item itself. - The Map element is part of the \bold{QtMobility.location 1.2} module. + The Map element is part of the \bold{Qt.location 5.0} module. */ QDeclarative3DGraphicsGeoMap::QDeclarative3DGraphicsGeoMap(QSGPaintedItem *parent) : QSGPaintedItem(parent), @@ -101,16 +110,32 @@ QDeclarative3DGraphicsGeoMap::QDeclarative3DGraphicsGeoMap(QSGPaintedItem *paren initialCoordinate(0), mapType_(NoMap), connectivityMode_(NoConnectivity), - componentCompleted_(false) + componentCompleted_(false), + activeMouseArea_(0), + tileCache_(0), + tileSphere_(0), + sceneNode_(0) { initialCoordinate = new QGeoCoordinate(-27.0, 153.0); zoomLevel_ = 8; size_ = QSizeF(100.0, 100.0); - // setAcceptsHoverEvents(true); - // setAcceptHoverEvents(true); - //setAcceptedMouseButtons(Qt::LeftButton | Qt::MidButton | Qt::RightButton); - //setFlag(QGraphicsItem::ItemHasNoContents, false); - //setFlag(QGraphicsItem::ItemAcceptsInputMethod); + setAcceptHoverEvents(true); + setAcceptedMouseButtons(Qt::LeftButton | Qt::MidButton | Qt::RightButton); + + setRenderTarget(QSGPaintedItem::FramebufferObject); + setFillColor(Qt::yellow); + + tileCache_ = new TileCache(); + tileSphere_ = new TileSphere(this, tileCache_, 4, 1.0); + connect(tileSphere_, + SIGNAL(sphereUpdated()), + this, + SLOT(update())); + sceneNode_ = tileSphere_->sphereSceneNode(); + tileSphere_->setRadius(3.0); + tileSphere_->setZoom(5); + tileSphere_->limitView(26, 16, 30, 20); + tileSphere_->update(); } QDeclarative3DGraphicsGeoMap::~QDeclarative3DGraphicsGeoMap() @@ -120,7 +145,6 @@ QDeclarative3DGraphicsGeoMap::~QDeclarative3DGraphicsGeoMap() // Remove map objects, we can't allow mapObject // to delete the objects because they are owned // by the declarative elements. - //QList objects = objectMap_.keys(); QList objects = mapObjects_; for (int i = 0; i < objects.size(); ++i) { mapData_->removeMapObject(objects.at(i)->mapObject()); @@ -129,7 +153,6 @@ QDeclarative3DGraphicsGeoMap::~QDeclarative3DGraphicsGeoMap() } if (serviceProvider_) delete serviceProvider_; - if (initialCoordinate) { delete initialCoordinate; } @@ -138,7 +161,6 @@ QDeclarative3DGraphicsGeoMap::~QDeclarative3DGraphicsGeoMap() void QDeclarative3DGraphicsGeoMap::componentComplete() { componentCompleted_ = true; - QSGPaintedItem::componentComplete(); populateMap(); } @@ -167,6 +189,7 @@ void QDeclarative3DGraphicsGeoMap::populateMap() QDeclarativeGeoMapMouseArea *mouseArea = qobject_cast(kids.at(i)); if (mouseArea) { + // TODO mouse areas won't work //mouseArea->setMap(this); //mouseAreas_.append(mouseArea); } @@ -180,22 +203,98 @@ void QDeclarative3DGraphicsGeoMap::setupMapView(QDeclarativeGeoMapObjectView *vi //view->repopulate(); } +class ViewportSubsurface : public QGLSubsurface +{ +public: + ViewportSubsurface(QGLAbstractSurface *surface, const QRect ®ion, + qreal adjust) + : QGLSubsurface(surface, region), m_adjust(adjust) {} + + qreal aspectRatio() const; + ~ViewportSubsurface() {} + +private: + qreal m_adjust; +}; -//void QDeclarative3DGraphicsGeoMap::paint(QPainter *painter, -// const QStyleOptionGraphicsItem *option, -// QWidget * /*widget*/) -//{ -// qDebug("-----------original paint called"); -// if (mapData_) { -// mapData_->paint(painter, option); -// } -//} +qreal ViewportSubsurface::aspectRatio() const +{ + return QGLSubsurface::aspectRatio() * m_adjust; +} void QDeclarative3DGraphicsGeoMap::paint(QPainter *painter) { qDebug() << __FUNCTION__ << "----------- Map3d, mapData_:" << mapData_; - if (mapData_) - mapData_->paint(painter, 0); + // to paint the old mercator projection: + //if (mapData_) + // mapData_->paint(painter, 0); + + QGLPainter glPainter; + if (!glPainter.begin(painter)) { + qWarning("GL graphics system is not active; cannot use 3D items"); + return; + } + // No stereo rendering, set the eye as neutral + glPainter.setEye(QGL::NoEye); + + // Currently applied transforms for this Map3D element + QTransform transform = painter->combinedTransform(); + // QSGItem::boundingRect() returns (top(0), left(0), width, height). + // Then we get the rectangle that is gotten by applying the QTransform on the rect + // --> this is the viewport for Map3D + QRect viewport = transform.mapRect(boundingRect()).toRect(); + qDebug() << "Viewport paramteres: " << viewport.width() << viewport.height() << viewport.top() << viewport.left(); + qreal adjust = 1.0f; + ViewportSubsurface surface(glPainter.currentSurface(), viewport, adjust); + qDebug() << "surface before: " << &surface; + glPainter.pushSurface(&surface); + qDebug() << "surface after: " << &surface; + + earlyDraw(&glPainter); + // From QGLView + QGLCamera defCamera; + glPainter.setCamera(&defCamera); + paintGL(&glPainter); + // Draw the children items + + + + glPainter.popSurface(); +} + +void QDeclarative3DGraphicsGeoMap::earlyDraw(QGLPainter *painter) +{ + // If we have a parent, then assume that the parent has painted + // the background and overpaint over the top of it. If we don't + // have a parent, then clear to black. + if (parentItem()) { + glClear(GL_DEPTH_BUFFER_BIT); + } else { + painter->setClearColor(Qt::black); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + // Force the effect to be updated. The GL paint engine + // has left the GL state in an unknown condition. + painter->disableEffect(); + +#ifdef GL_RESCALE_NORMAL + glEnable(GL_RESCALE_NORMAL); +#endif + + // Set the default effect for the scene. + painter->setStandardEffect(QGL::LitMaterial); + painter->setFaceColor(QGL::AllFaces, Qt::white); +} + + +// Private paint +void QDeclarative3DGraphicsGeoMap::paintGL(QGLPainter *painter) +{ + qDebug() << __FUNCTION__ << " of Map3D, sceneNode_: " << sceneNode_; + if (sceneNode_) + sceneNode_->draw(painter); + qDebug() << __FUNCTION__ << " after"; } void QDeclarative3DGraphicsGeoMap::geometryChanged(const QRectF &newGeometry, diff --git a/src/imports/location/qdeclarative3dgraphicsgeomap_p.h b/src/imports/location/qdeclarative3dgraphicsgeomap_p.h index 9ea2183e..b83b4cef 100644 --- a/src/imports/location/qdeclarative3dgraphicsgeomap_p.h +++ b/src/imports/location/qdeclarative3dgraphicsgeomap_p.h @@ -48,6 +48,13 @@ #include #include "qdeclarativegeomapobject_p.h" +class QGLPainter; +class QGLSceneNode; +class TileCache; +class Tile; +class TileSpec; +class TileSphere; + QTM_BEGIN_NAMESPACE class QGeoCoordinate; @@ -97,11 +104,6 @@ public: // From QDeclarativeParserStatus virtual void componentComplete(); - // From QGraphicsItem - //void paint(QPainter *painter, - // const QStyleOptionGraphicsItem *option, - // QWidget *widget); - // From QSGPaintedItem virtual void paint (QPainter *painter); void setPlugin(QDeclarativeGeoServiceProvider *plugin); @@ -201,6 +203,12 @@ private: QDeclarativeGeoMapMouseArea* activeMouseArea_; QList mouseAreas_; + void paintGL(QGLPainter *painter); + void earlyDraw(QGLPainter *painter); + TileCache *tileCache_; + TileSphere *tileSphere_; + QGLSceneNode *sceneNode_; + friend class QDeclarativeGeoMapObjectView; Q_DISABLE_COPY(QDeclarative3DGraphicsGeoMap) }; diff --git a/src/imports/location/tile.cpp b/src/imports/location/tile.cpp new file mode 100644 index 00000000..675fc7b5 --- /dev/null +++ b/src/imports/location/tile.cpp @@ -0,0 +1,27 @@ + +#include "tile.h" + +Tile::Tile() {} +Tile::Tile(const TileSpec &spec, const QPixmap &pixmap) + : spec_(spec), + pixmap_(pixmap) {} + +void Tile::setTileSpec(const TileSpec &spec) +{ + spec_ = spec; +} + +TileSpec Tile::tileSpec() const +{ + return spec_; +} + +void Tile::setPixmap(const QPixmap &pixmap) +{ + pixmap_ = pixmap; +} + +QPixmap Tile::pixmap() const +{ + return pixmap_; +} diff --git a/src/imports/location/tile.h b/src/imports/location/tile.h new file mode 100644 index 00000000..c63360cf --- /dev/null +++ b/src/imports/location/tile.h @@ -0,0 +1,25 @@ +#ifndef TILE_H +#define TILE_H + +#include "tilespec.h" + +#include + +class Tile +{ +public: + Tile(); + Tile(const TileSpec &spec, const QPixmap &pixmap); + + void setTileSpec(const TileSpec &spec); + TileSpec tileSpec() const; + + void setPixmap(const QPixmap &pixmap); + QPixmap pixmap() const; + +private: + TileSpec spec_; + QPixmap pixmap_; +}; + +#endif diff --git a/src/imports/location/tilecache.cpp b/src/imports/location/tilecache.cpp new file mode 100644 index 00000000..10e6ec10 --- /dev/null +++ b/src/imports/location/tilecache.cpp @@ -0,0 +1,142 @@ +#include "tilecache.h" + +#include +#include + +#include + +TileCache::TileCache(const QString &directory, + QObject *parent) + : QObject(parent), + directory_(directory), + waitingForPrefetchedTiles_(false), + tilesOutstanding_(0) +{ + if (directory_.isEmpty()) { + QDir home = QDir::home(); + if (!home.exists(".tilecache")) + home.mkdir(".tilecache"); + directory_ = home.filePath(".tilecache"); + } + + provider_ = new TileProvider(this); + connect(provider_, + SIGNAL(tileReady(Tile)), + this, + SLOT(insert(Tile))); + connect(provider_, + SIGNAL(tileError(TileSpec)), + this, + SLOT(handleError(TileSpec))); + loadTiles(); +} + +void TileCache::prefetch(const TileSpec &spec) +{ + if (!cache_.contains(spec)) { + provider_->tileRequest(spec); + ++tilesOutstanding_; + } +} + +void TileCache::waitForPrefetchedTiles() +{ + if (tilesOutstanding_ == 0) + emit prefetchingFinished(); + else + waitingForPrefetchedTiles_ = true; +} + +Tile TileCache::get(const TileSpec &spec) const +{ + return cache_.value(spec); +} + +void TileCache::insert(const Tile &tile) +{ + QString filename = tileSpecToFilename(tile.tileSpec()); + tile.pixmap().save(filename, "png"); + cache_.insert(tile.tileSpec(), tile); + + updateWaitingStatus(); +} + +void TileCache::handleError(const TileSpec &) +{ + updateWaitingStatus(); +} + +void TileCache::updateWaitingStatus() +{ + --tilesOutstanding_; + + if (waitingForPrefetchedTiles_ && (tilesOutstanding_ == 0)) { + emit prefetchingFinished(); + waitingForPrefetchedTiles_ = false; + } +} + +void TileCache::loadTiles() +{ + QStringList formats; + formats << "*.png"; + + QDir dir(directory_); + QStringList files = dir.entryList(formats); + + for (int i = 0; i < files.size(); ++i) { + TileSpec spec = filenameToTileSpec(files.at(i)); + if (spec.zoom() == -1) + continue; + + QPixmap pixmap; + pixmap.load(dir.filePath(files.at(i)), "png"); + Tile tile(spec, pixmap); + cache_.insert(tile.tileSpec(), tile); + } +} + +QString TileCache::tileSpecToFilename(const TileSpec &spec) const +{ + QString filename = QString::number(spec.zoom()); + filename += "-"; + filename += QString::number(spec.x()); + filename += "-"; + filename += QString::number(spec.y()); + filename += ".png"; + + QDir dir = QDir(directory_); + + return dir.filePath(filename); +} + +TileSpec TileCache::filenameToTileSpec(const QString &filename) const +{ + TileSpec spec; + QRegExp r("(\\d+)-(\\d+)-(\\d+).png"); + + int index = r.indexIn(filename); + if (index != -1) { + bool ok = false; + + int zoom = r.cap(1).toInt(&ok); + if (!ok) + return spec; + + ok = false; + int x = r.cap(2).toInt(&ok); + if (!ok) + return spec; + + ok = false; + int y = r.cap(3).toInt(&ok); + if (!ok) + return spec; + + spec.setZoom(zoom); + spec.setX(x); + spec.setY(y); + } + + return spec; +} diff --git a/src/imports/location/tilecache.h b/src/imports/location/tilecache.h new file mode 100644 index 00000000..6efefbfa --- /dev/null +++ b/src/imports/location/tilecache.h @@ -0,0 +1,46 @@ +#ifndef TILECACHE_H +#define TILECACHE_H + +#include +#include + +#include "tilespec.h" +#include "tile.h" +#include "tileprovider.h" + +class TileCache : public QObject +{ + Q_OBJECT +public: + TileCache(const QString &directory = QString(), QObject *parent = 0); + + void prefetch(const TileSpec &spec); + void waitForPrefetchedTiles(); + + Tile get(const TileSpec &spec) const; + +signals: + void prefetchingFinished(); + +private slots: + void insert(const Tile &tile); + void handleError(const TileSpec &spec); + +private: + void loadTiles(); + + void updateWaitingStatus(); + + QString tileSpecToFilename(const TileSpec &spec) const; + TileSpec filenameToTileSpec(const QString &filename) const; + + QString directory_; + TileProvider *provider_; + + bool waitingForPrefetchedTiles_; + int tilesOutstanding_; + + QMap cache_; +}; + +#endif // TILECACHE_H diff --git a/src/imports/location/tileprovider.cpp b/src/imports/location/tileprovider.cpp new file mode 100644 index 00000000..03552d6f --- /dev/null +++ b/src/imports/location/tileprovider.cpp @@ -0,0 +1,62 @@ +#include "tileprovider.h" + +#include "tile.h" +#include "tilespec.h" + +#include +#include +#include +#include +#include + +TileProvider::TileProvider(QObject *parent) + : QObject(parent) +{ + nam_ = new QNetworkAccessManager(this); + connect(nam_, + SIGNAL(finished(QNetworkReply*)), + this, + SLOT(finished(QNetworkReply*))); +} + +void TileProvider::tileRequest(const TileSpec &spec) +{ + QString url = "http://tile.openstreetmap.org/"; + url += QString::number(spec.zoom()); + url += "/"; + url += QString::number(spec.x()); + url += "/"; + url += QString::number(spec.y()); + url += ".png"; + + QNetworkRequest request = QNetworkRequest(QUrl(url)); + QNetworkReply *reply = nam_->get(request); + + map_.insert(reply, spec); +} + +void TileProvider::finished(QNetworkReply *reply) +{ + if (!map_.contains(reply)) { + reply->deleteLater(); + return; + } + + TileSpec spec = map_.value(reply); + + if (reply->error() == QNetworkReply::NoError) { + QByteArray bytes = reply->readAll(); + + QPixmap pixmap; + bool result = pixmap.loadFromData(bytes, "png"); + + if (result) { + Tile tile(spec, pixmap); + emit tileReady(tile); + } + } else { + emit tileError(spec); + } + + reply->deleteLater(); +} diff --git a/src/imports/location/tileprovider.h b/src/imports/location/tileprovider.h new file mode 100644 index 00000000..460b425b --- /dev/null +++ b/src/imports/location/tileprovider.h @@ -0,0 +1,34 @@ +#ifndef TILEPROVIDER_H +#define TILEPROVIDER_H + +#include +#include + +#include "tilespec.h" + +class Tile; +class QNetworkAccessManager; +class QNetworkReply; + +class TileProvider : public QObject +{ + Q_OBJECT +public: + TileProvider(QObject *parent = 0); + +public slots: + void tileRequest(const TileSpec &spec); + +private slots: + void finished(QNetworkReply *reply); + +signals: + void tileReady(const Tile &tile); + void tileError(const TileSpec &spec); + +private: + QNetworkAccessManager *nam_; + QHash map_; +}; + +#endif // TILEPROVIDER_H diff --git a/src/imports/location/tilespec.cpp b/src/imports/location/tilespec.cpp new file mode 100644 index 00000000..5123d844 --- /dev/null +++ b/src/imports/location/tilespec.cpp @@ -0,0 +1,56 @@ + +#include "tilespec.h" + +TileSpec::TileSpec() + : zoom_(-1), + x_(-1), + y_(-1) {} + +TileSpec::TileSpec(int zoom, int x, int y) + : zoom_(zoom), + x_(x), + y_(y) {} + +void TileSpec::setZoom(int zoom) +{ + zoom_ = zoom; +} + +int TileSpec::zoom() const +{ + return zoom_; +} + +void TileSpec::setX(int x) +{ + x_ = x; +} + +int TileSpec::x() const +{ + return x_; +} + +void TileSpec::setY(int y) +{ + y_ = y; +} + +int TileSpec::y() const +{ + return y_; +} + +bool TileSpec::operator < (const TileSpec &rhs) const +{ + if (zoom_ < rhs.zoom_) + return true; + if (zoom_ > rhs.zoom_) + return false; + if (x_ < rhs.x_) + return true; + if (x_ > rhs.x_) + return false; + return (y_ < rhs.y_); + +} diff --git a/src/imports/location/tilespec.h b/src/imports/location/tilespec.h new file mode 100644 index 00000000..bbc1edf0 --- /dev/null +++ b/src/imports/location/tilespec.h @@ -0,0 +1,27 @@ +#ifndef TILESPEC_H +#define TILESPEC_H + +class TileSpec +{ +public: + TileSpec(); + TileSpec(int zoom, int x, int y); + + void setZoom(int zoom); + int zoom() const; + + void setX(int x); + int x() const; + + void setY(int y); + int y() const; + + bool operator < (const TileSpec &rhs) const; + +private: + int zoom_; + int x_; + int y_; +}; + +#endif diff --git a/src/imports/location/tilesphere.cpp b/src/imports/location/tilesphere.cpp new file mode 100644 index 00000000..79eca575 --- /dev/null +++ b/src/imports/location/tilesphere.cpp @@ -0,0 +1,222 @@ +#include "tilesphere.h" + +#include "tilecache.h" +#include "tile.h" + +#include +#include +#include +#include + +#include + +TileSphere::TileSphere(QObject *parent, TileCache *tileCache, int zoom, double radius) + : QObject(parent), + tileCache_(tileCache), + zoom_(zoom), + radius_(radius), + viewLimited_(false) +{ + viewX1_ = 0; + viewY1_ = 0; + viewX2_ = (1 << zoom_) - 1; + viewY2_ = (1 << zoom_) - 1; + + sphereNode_ = new QGLSceneNode(this); + connect(tileCache_, + SIGNAL(prefetchingFinished()), + this, + SLOT(prefetchingFinished())); +} + +void TileSphere::setZoom(int zoom) +{ + if (zoom_ == zoom) + return; + + zoom_ = zoom; +} + +int TileSphere::zoom() const +{ + return zoom_; +} + +void TileSphere::setRadius(double radius) +{ + if (radius_ == radius) + return; + + radius_ = radius; +} + +double TileSphere::radius() const +{ + return radius_; +} + +void TileSphere::limitView(int x1, int y1, int x2, int y2) +{ + viewLimited_ = true; + viewX1_ = x1; + viewY1_ = y1; + viewX2_ = x2; + viewY2_ = y2; +} + +void TileSphere::clearLimit() +{ + if (!viewLimited_) + return; + + viewLimited_ = false; + viewX1_ = 0; + viewY1_ = 0; + viewX2_ = (1 << zoom_) - 1; + viewY2_ = (1 << zoom_) - 1; +} + +QGLSceneNode* TileSphere::sphereSceneNode() const +{ + return sphereNode_; +} + +void TileSphere::update() +{ + prefetchSphere(); + tileCache_->waitForPrefetchedTiles(); +} + +void TileSphere::prefetchingFinished() +{ + qDebug() << __FUNCTION__ << "prefetching is finished"; + buildSphere(); + emit sphereUpdated(); +} + +QGeometryData TileSphere::tileSpecToSphereQuad(int x, int y) const +{ + QGeometryData data; + + const double pi = 3.14159265358; + + double z = 1 << zoom_; + + double lon1 = 2.0 * pi * x / z; + double lon2 = 2.0 * pi * (x + 1) / z; + double lat1 = pi * y / z; + double lat2 = pi * (y + 1) / z; + + double cosLat1 = cos(lat1); + double cosLat2 = cos(lat2); + double cosLon1 = cos(pi/2 - lon1); + double cosLon2 = cos(pi/2 - lon2); + + double sinLat1 = sin(lat1); + double sinLat2 = sin(lat2); + double sinLon1 = sin(pi/2 - lon1); + double sinLon2 = sin(pi/2 - lon2); + + // y and z are flipped here to move from + // "earth" geometry to GL geometry + QVector3D v11 = QVector3D(cosLon1 * sinLat1, + cosLat1, + sinLon1 * sinLat1); + + QVector3D v12 = QVector3D(cosLon1 * sinLat2, + cosLat2, + sinLon1 * sinLat2); + + QVector3D v21 = QVector3D(cosLon2 * sinLat1, + cosLat1, + sinLon2 * sinLat1); + + QVector3D v22 = QVector3D(cosLon2 * sinLat2, + cosLat2, + sinLon2 * sinLat2); + + + data.appendVertex(radius_ * v11); + data.appendNormal(v11); + data.appendTexCoord(QVector2D(0, 1)); + + data.appendVertex(radius_ * v12); + data.appendNormal(v12); + data.appendTexCoord(QVector2D(0, 0)); + + data.appendVertex(radius_ * v22); + data.appendNormal(v22); + data.appendTexCoord(QVector2D(1, 0)); + + data.appendVertex(radius_ * v21); + data.appendNormal(v21); + data.appendTexCoord(QVector2D(1, 1)); + + return data; +} + +QGLSceneNode* TileSphere::tileToSceneNode(const Tile &tile) const +{ + if (tile.tileSpec().zoom() == -1) + return 0; + + QGLBuilder builder; + builder.addQuads(tileSpecToSphereQuad(tile.tileSpec().x(), tile.tileSpec().y())); + + QGLSceneNode *node = builder.finalizedSceneNode(); + + QGLMaterial *mat = new QGLMaterial(node); + QGLTexture2D *tex = new QGLTexture2D(mat); + tex->setPixmap(tile.pixmap()); + mat->setTexture(tex); + + node->setMaterial(mat); + node->setEffect(QGL::LitDecalTexture2D); + return node; +} + +void TileSphere::prefetchSphere() const +{ + int z = 1 << zoom_; + + int x1 = viewX1_; + int x2 = viewX2_; + if (x2 < x1) + x2 += z; + + int y1 = viewY1_; + int y2 = viewY2_; + if (y2 < y1) + y2 += z; + + for (int x = x1; x <= x2; ++x) { + for (int y = y1; y <= y2; ++y) { + tileCache_->prefetch(TileSpec(zoom_, x % z, y % z)); + } + } +} + +void TileSphere::buildSphere() const +{ + sphereNode_->removeNodes(sphereNode_->children()); + + int z = 1 << zoom_; + + int x1 = viewX1_; + int x2 = viewX2_; + if (x2 < x1) + x2 += z; + + int y1 = viewY1_; + int y2 = viewY2_; + if (y2 < y1) + y2 += z; + + for (int x = x1; x <= x2; ++x) { + for (int y = y1; y <= y2; ++y) { + QGLSceneNode *node = tileToSceneNode(tileCache_->get(TileSpec(zoom_, x % z, y % z))); + if (node) + sphereNode_->addNode(node); + } + } +} diff --git a/src/imports/location/tilesphere.h b/src/imports/location/tilesphere.h new file mode 100644 index 00000000..e3a7647a --- /dev/null +++ b/src/imports/location/tilesphere.h @@ -0,0 +1,58 @@ +#ifndef TILESPHERE_H +#define TILESPHERE_H + +#include + +class QGLSceneNode; +class QGeometryData; + +class Tile; +class TileCache; + +class TileSphere : public QObject +{ + Q_OBJECT +public: + TileSphere(QObject *parent, TileCache *tileCache, int zoom = 4, double radius = 1.0); + + void setZoom(int zoom); + int zoom() const; + + void setRadius(double radius); + double radius() const; + + void limitView(int x1, int y1, int x2, int y2); + void clearLimit(); + + QGLSceneNode* sphereSceneNode() const; + +public slots: + void update(); + +private slots: + void prefetchingFinished(); + +signals: + void sphereUpdated(); + +private: + QGeometryData tileSpecToSphereQuad(int x, int y) const; + QGLSceneNode* tileToSceneNode(const Tile &tile) const; + + void prefetchSphere() const; + void buildSphere() const; + + TileCache *tileCache_; + int zoom_; + double radius_; + + bool viewLimited_; + int viewX1_; + int viewY1_; + int viewX2_; + int viewY2_; + + QGLSceneNode* sphereNode_; +}; + +#endif // TILESPHERE_H -- cgit v1.2.1