diff options
Diffstat (limited to 'src/location/mapsgl/map2d/map2d_p.cpp')
-rw-r--r-- | src/location/mapsgl/map2d/map2d_p.cpp | 660 |
1 files changed, 660 insertions, 0 deletions
diff --git a/src/location/mapsgl/map2d/map2d_p.cpp b/src/location/mapsgl/map2d/map2d_p.cpp new file mode 100644 index 00000000..98e7fe0d --- /dev/null +++ b/src/location/mapsgl/map2d/map2d_p.cpp @@ -0,0 +1,660 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "map2d_p.h" +#include "projection2d_p.h" + +#include "tile.h" +#include "mapitem.h" + +#include <Qt3D/qgeometrydata.h> +#include <Qt3D/qglbuilder.h> +#include <Qt3D/qglcamera.h> + +#include <cmath> + +TileMap::TileMap(int minY, int maxY) + : size(0), + minY(minY), + maxY(maxY), + minX(maxY - minY + 1, -1), + maxX(maxY - minY + 1, -1) +{} + +void TileMap::adjust(int tileX, int tileY) +{ + int index = tileY - minY; + int min = minX.at(index); + int max = maxX.at(index); + + if (min == -1) { + min = tileX; + max = tileX; + minX[index] = min; + maxX[index] = max; + size += 1; + } else { + int oldSize = (max - min); + int min2 = qMin(min, tileX); + if (min2 != min) + minX[index] = min2; + int max2 = qMax(max, tileX); + if (max2 != max) + maxX[index] = max2; + int newSize = (max2 - min2); + size += (newSize - oldSize); + } +} + +IntersectGenerator::IntersectGenerator(const MapPrivate *mp, + double p1, + double p2, + int t1, + int t2, + IntersectGenerator::Axis axis, + int zoomLevel) + : mp_(mp), + axis_(axis), + zoomLevel_(zoomLevel) +{ + if (t1 == t2) { + hasNext_ = false; + return; + } + + bool inc = true; + if (axis_ == IntersectGenerator::XAxis) { + inc = (0 < (p2 - p1)); + } else { + inc = (0 < (p1 - p2)); + } + + step_ = 1; + adjust_ = 0; + if (!inc) { + step_ = -1; + adjust_ = -1; + } + + first_ = p1; + denom_ = p2 - p1; + + current_ = t1; + end_ = t2 + step_; + + hasNext_ = true; + + generateValue(); +} + +bool IntersectGenerator::hasNext() const +{ + return hasNext_; +} + +QPair<double, int> IntersectGenerator::value() const +{ + return value_; +} + +void IntersectGenerator::next() +{ + generateValue(); +} + +void IntersectGenerator::generateValue() +{ + while (current_ != end_) { + double alpha = 0.0; + + if (axis_ == IntersectGenerator::XAxis) { + double x = mp_->tileXIntersectToPoint(zoomLevel_, current_).x(); + alpha = (x - first_) / denom_; + } else { + double y = mp_->tileYIntersectToPoint(zoomLevel_, current_).y(); + alpha = (y - first_) / denom_; + } + + if ((0.0 < alpha) && (alpha < 1.0)) { + value_ = QPair<double,int>(alpha, current_ + adjust_); + current_ += step_; + return; + } + current_ += step_; + } + hasNext_ = false; +} + +Map2DPrivate::Map2DPrivate(Map *parent, TileCache *cache, int maxZoom, int tileSize) + : MapPrivate(parent, cache), + maxZoom_(maxZoom), + tileSize_(tileSize), + baseHeight_(100.0) +{ + sideLength_ = pow(2.0, 1.0 * maxZoom_); + + setProjection(QSharedPointer<Projection>(new Projection2D(baseHeight_, sideLength_))); + screenPoly_.resize(4); + screenPoly_[0] = QPointF(0.0, 0.0); + screenPoly_[1] = QPointF(0.0, sideLength_); + screenPoly_[2] = QPointF(sideLength_, sideLength_); + screenPoly_[3] = QPointF(sideLength_, 0.0); + + screenPolyLeft_.resize(4); + screenPolyLeft_[0] = QPointF(0.0, 0.0); + screenPolyLeft_[1] = QPointF(0.0, sideLength_); + screenPolyLeft_[2] = QPointF(sideLength_ / 2.0, sideLength_); + screenPolyLeft_[3] = QPointF(sideLength_ / 2.0, 0.0); + + screenPolyRight_.resize(4); + screenPolyRight_[0] = QPointF(sideLength_ / 2.0, 0.0); + screenPolyRight_[1] = QPointF(sideLength_ / 2.0, sideLength_); + screenPolyRight_[2] = QPointF(sideLength_, sideLength_); + screenPolyRight_[3] = QPointF(sideLength_, 0.0); +} + +Map2DPrivate::~Map2DPrivate() {} + +QGLSceneNode* Map2DPrivate::createTileNode(const Tile &tile) +{ + int geomZoom = tile.tileSpec().zoom(); + int tileZoom = geomZoom; + int x = tile.tileSpec().x(); + int y = tile.tileSpec().y(); + + QGLBuilder builder; + + int z = 1 << geomZoom; + + bool rightEdge = false; + + double x1 = x * 1.0 / z; + double x2 = ((x + 1) % z) * 1.0 / z; + if (x2 == 0.0) { + x2 = 1.0; + rightEdge = true; + } + double y1 = y * 1.0 / z; + double y2 = (y + 1) * 1.0 / z; + + QSharedPointer<Projection> p = projection(); + + QVector3D tl = p->mercatorToPoint(QVector2D(x1, y1)); + QVector3D tr = p->mercatorToPoint(QVector2D(x2, y1)); + QVector3D bl = p->mercatorToPoint(QVector2D(x1, y2)); + QVector3D br = p->mercatorToPoint(QVector2D(x2, y2)); + + if (rightEdge) { + tr.setX(sideLength_); + br.setX(sideLength_); + } + + int dz = 1 << (geomZoom - tileZoom); + + int tx1 = x % dz; + int ty1 = y % dz; + + ty1 = dz - ty1; + + int tx2 = (x + 1) % dz; + if (tx2 == 0) + tx2 = dz; + + int ty2 = (y + 1) % dz; + if (ty2 == 0) + ty2 = dz; + + ty2 = dz - ty2; + + QGeometryData g; + + QVector3D n = QVector3D(0, 0, 1); + + g.appendVertex(tl); + g.appendNormal(n); + g.appendTexCoord(QVector2D(tx1 * 1.0 / dz, ty1 * 1.0 / dz)); + + g.appendVertex(bl); + g.appendNormal(n); + g.appendTexCoord(QVector2D(tx1 * 1.0 / dz, ty2 * 1.0 / dz)); + + g.appendVertex(br); + g.appendNormal(n); + g.appendTexCoord(QVector2D(tx2 * 1.0 / dz, ty2 * 1.0 / dz)); + + g.appendVertex(tr); + g.appendNormal(n); + g.appendTexCoord(QVector2D(tx2 * 1.0 / dz, ty1 * 1.0 / dz)); + + builder.addQuads(g); + + QGLSceneNode *node = builder.finalizedSceneNode(); + + QGLMaterial *mat = new QGLMaterial(node); +// tile.texture()->bind(); + mat->setTexture(tile.texture()); + node->setEffect(QGL::LitDecalTexture2D); + + node->setMaterial(mat); + + return node; +} + +void Map2DPrivate::updateGlCamera(QGLCamera* glCamera) +{ + bool old = glCamera->blockSignals(true); + + CameraData camera = cameraData(); + + double f = 1.0 * qMin(width(), height()) / tileSize_; + + double altitude = sideLength_ * camera.distance() * f / 2.0; + + QSharedPointer<Projection> p = projection(); + + QGeoCoordinate coord = camera.center(); + coord.setAltitude(0.0); + QVector3D center = p->coordToPoint(coord); + coord.setAltitude(altitude); + QVector3D eye = p->coordToPoint(coord); + + QVector3D view = eye - center; + QVector3D side = QVector3D::normal(view, QVector3D(0.0, 1.0, 0.0)); + QVector3D up = QVector3D::normal(side, view); + + QMatrix4x4 mBearing; + mBearing.rotate(-1.0 * camera.bearing(), view); + up = mBearing * up; + + QVector3D side2 = QVector3D::normal(up, view); + QMatrix4x4 mTilt; + mTilt.rotate(camera.tilt(), side2); + eye = (mTilt * view) + center; + + view = eye - center; + side = QVector3D::normal(view, QVector3D(0.0, 1.0, 0.0)); + up = QVector3D::normal(view, side2); + + QMatrix4x4 mRoll; + mRoll.rotate(camera.roll(), view); + up = mRoll * up; + + double nearPlane = 1.0; + double farPlane = 2.0 * altitude; + + glCamera->setCenter(center); + glCamera->setEye(eye); + glCamera->setUpVector(up); + glCamera->setNearPlane(nearPlane); + glCamera->setFarPlane(farPlane); + + glCamera->blockSignals(old); + + // TODO fold into above code if this works for screen <-> coordinate conversions + viewSize_ = glCamera->viewSize(); + eye_ = eye; + projectionMatrix_ = glCamera->projectionMatrix(aspectRatio()) * glCamera->modelViewMatrix(); +} + +void Map2DPrivate::updateFrustum(Frustum &frustum) +{ + frustum.update(glCamera(), cameraData().aspectRatio()); +} + +QList<TileSpec> Map2DPrivate::updateVisibleTiles() +{ + Frustum f = frustum(); + + QPolygonF poly(4); + + QVector3D tln = f.topLeftNear(); + QVector3D tlf = f.topLeftFar(); + double tl = (tln.z() - baseHeight_) / (tln.z() - tlf.z()); + + poly[0] = ((1 - tl) * tln + tl * tlf).toPointF(); + + QVector3D bln = f.bottomLeftNear(); + QVector3D blf = f.bottomLeftFar(); + double bl = (bln.z() - baseHeight_) / (bln.z() - blf.z()); + + poly[1] = ((1 - bl) * bln + bl * blf).toPointF(); + + QVector3D brn = f.bottomRightNear(); + QVector3D brf = f.bottomRightFar(); + double br = (brn.z() - baseHeight_) / (brn.z() - brf.z()); + + poly[2] = ((1 - br) * brn + br * brf).toPointF(); + + QVector3D trn = f.topRightNear(); + QVector3D trf = f.topRightFar(); + double tr = (trn.z() - baseHeight_) / (trn.z() - trf.z()); + + poly[3] = ((1 - tr) * trn + tr * trf).toPointF(); + + bool widePoly = false; + bool clip = false; + for (int i = 0; i < poly.size(); ++i) { + QPointF p = poly.at(i); + if (p.x() >= sideLength_) { + widePoly = true; + break; + } + if ((p.x() < 0) + || (sideLength_ < p.x()) + || (p.y() < 0) + || (sideLength_ < p.y())) { + clip = true; + } + } + + if (!widePoly) { + if (!clip) { + QVector<QVector3D> points(poly.size()); + + for (int i = 0; i < poly.size(); ++i) + points[i] = QVector3D(poly.at(i)); + + return tilesFromPoints(points, false); + } else { + QPolygonF clipped = intersection(poly, screenPoly_); + + QList<TileSpec> tiles; + + if (!clipped.isEmpty()) { + QVector<QVector3D> points(clipped.size() - 1); + + for (int i = 0; i < clipped.size() - 1; ++i) + points[i] = QVector3D(clipped.at(i)); + + return tilesFromPoints(points, false); + } + + return tiles; + } + + } else { + QPolygonF clippedLeft = intersection(poly, screenPolyLeft_); + QPolygonF clippedRight = intersection(poly, screenPolyRight_); + + QList<TileSpec> tiles; + + if (!clippedLeft.isEmpty()) { + QVector<QVector3D> pointsLeft(clippedLeft.size() - 1); + + for (int i = 0; i < clippedLeft.size() - 1; ++i) + pointsLeft[i] = QVector3D(clippedLeft.at(i)); + + if (clippedRight.isEmpty()) + return tilesFromPoints(pointsLeft, false); + else + tiles.append(tilesFromPoints(pointsLeft, false)); + } + + if (!clippedRight.isEmpty()) { + QVector<QVector3D> pointsRight(clippedRight.size() - 1); + + for (int i = 0; i < clippedRight.size() - 1; ++i) + pointsRight[i] = QVector3D(clippedRight.at(i)); + + if (tiles.isEmpty()) + return tilesFromPoints(pointsRight, true); + else + tiles.append(tilesFromPoints(pointsRight, true)); + } + + return tiles; + } +} + +QPolygonF Map2DPrivate::intersection(const QPolygonF &p1, const QPolygonF &p2) const +{ + QVector<double> x(4); + QVector<double> y(4); + + x[0] = p1.at(0).x(); + x[1] = p1.at(2).x(); + x[2] = p2.at(0).x(); + x[3] = p2.at(2).x(); + + y[0] = p1.at(0).y(); + y[1] = p1.at(2).y(); + y[2] = p2.at(0).y(); + y[3] = p2.at(2).y(); + + qSort(x); + qSort(y); + + QPolygonF result(5); + + result[0] = QPointF(x.at(1), y.at(1)); + result[1] = QPointF(x.at(2), y.at(1)); + result[2] = QPointF(x.at(2), y.at(2)); + result[3] = QPointF(x.at(1), y.at(2)); + result[4] = QPointF(x.at(1), y.at(1)); + + return result; +} + +void Map2DPrivate::tilesFromLine(const QVector3D &p1, + const QVector3D &p2, + const QVector2D &t1, + const QVector2D &t2, + int zoomLevel, + TileMap &map) const +{ + IntersectGenerator xGen = IntersectGenerator(this, p1.x(), p2.x(), t1.x(), t2.x(), + IntersectGenerator::XAxis, zoomLevel); + IntersectGenerator yGen = IntersectGenerator(this, p1.y(), p2.y(), t1.y(), t2.y(), + IntersectGenerator::YAxis, zoomLevel); + + int tileX = t1.x(); + int tileY = t1.y(); + + map.adjust(tileX, tileY); + + while (xGen.hasNext() && yGen.hasNext()) { + QPair<double, int> x = xGen.value(); + QPair<double, int> y = yGen.value(); + if (x.first < y.first) { + tileX = x.second; + map.adjust(tileX, tileY); + xGen.next(); + } else if (x.first > y.first) { + tileY = y.second; + map.adjust(tileX, tileY); + yGen.next(); + } else { + map.adjust(tileX, y.second); + map.adjust(x.second, tileY); + tileX = x.second; + tileY = y.second; + map.adjust(tileX, tileY); + xGen.next(); + yGen.next(); + } + } + + while (xGen.hasNext()) { + tileX = xGen.value().second; + map.adjust(tileX, tileY); + xGen.next(); + } + + while (yGen.hasNext()) { + tileY = yGen.value().second; + map.adjust(tileX, tileY); + yGen.next(); + } +} + +QList<TileSpec> Map2DPrivate::tilesFromPoints(const QVector<QVector3D> &points, bool roundUp) const +{ + int numPoints = points.size(); + + if (numPoints == 0) + return QList<TileSpec>(); + + int zoomLevel = cameraData().zoomLevel(); + + int minY = -1; + int maxY = -1; + + QVector<QVector2D> tiles(points.size()); + for (int i = 0; i < numPoints; ++i) { + QVector2D t = pointToTile(points.at(i), zoomLevel, roundUp); + if (minY == -1) { + minY = t.y(); + maxY = t.y(); + } else { + minY = qMin(minY, static_cast<int>(t.y())); + maxY = qMax(maxY, static_cast<int>(t.y())); + } + tiles[i] = t; + } + + TileMap map(minY, maxY); + + for (int i1 = 0; i1 < numPoints; ++i1) { + int i2 = (i1 + 1) % numPoints; + tilesFromLine(points.at(i1), points.at(i2), tiles.at(i1), tiles.at(i2), zoomLevel, map); + } + + QList<TileSpec> results; + + results.reserve(map.size); + + int size = map.minX.size(); + for (int i = 0; i < size; ++i) { + int y = map.minY + i; + int minX = map.minX[i]; + int maxX = map.maxX[i]; + for (int x = minX; x <= maxX; ++x) + results << TileSpec(zoomLevel, x, y); + } + + return results; +} + +void Map2DPrivate::updateMapItemSceneNode(MapItem *item) +{ + double z = item->zoom(); + + if (z < 0) { + // TODO draw item on camera + return; + } + + double scale = sideLength_ / (tileSize_ * pow(2, z)); + QPointF anchor = item->anchor() * scale; + QSizeF size = item->size() * scale; + double alt = 0.0; + QVector3D point = projection()->coordToPoint(item->coordinate()); + QVector3D tl = QVector3D(point.x() + anchor.x(), point.y() - anchor.y(), alt); + QVector3D tr = QVector3D(tl.x() + size.width(), tl.y(), alt); + QVector3D bl = QVector3D(tl.x(), tl.y() - size.height(), alt); + QVector3D br = QVector3D(tl.x() + size.width(), tl.y() - size.height(), alt); + + QVector3D up(0.0, 0.0, 1.0); + + QGeometryData g; + + g.appendVertex(tl); + g.appendNormal(up); + g.appendTexCoord(QVector2D(0.0, 0.0)); + + g.appendVertex(bl); + g.appendNormal(up); + g.appendTexCoord(QVector2D(0.0, 1.0)); + + g.appendVertex(br); + g.appendNormal(up); + g.appendTexCoord(QVector2D(1.0, 1.0)); + + g.appendVertex(tr); + g.appendNormal(up); + g.appendTexCoord(QVector2D(1.0, 0.0)); + + QGLBuilder builder; + builder.addQuads(g); + QGLSceneNode *node = builder.finalizedSceneNode(); + + QGLMaterial *mat = new QGLMaterial(node); + QColor defaultItemColor(128, 128, 128, 128); + mat->setColor(defaultItemColor); + mat->setSpecularColor(defaultItemColor); + mat->setDiffuseColor(defaultItemColor); + mat->setShininess(1.0); + node->setEffect(QGL::LitMaterial); + + node->setMaterial(mat); + item->setSceneNode(node); +} + +QGeoCoordinate Map2DPrivate::screenPositionToCoordinate(const QPointF &pos) const +{ + double w = width() * 1.0 / viewSize_.width(); + double h = height() * 1.0 / viewSize_.height(); + double x = (pos.x() - w) / w; + double y = (pos.y() - h) / h; + + x = (x + 1.0) / 2.0; + y = (y + 1.0) / 2.0; + + QVector3D tl = frustum().topLeftFar(); + QVector3D tr = frustum().topRightFar(); + QVector3D bl = frustum().bottomLeftFar(); + + QVector3D n = (1 - x - y) * tl + x * tr + y * bl; + + if (eye_.z() == n.z()) + return QGeoCoordinate(); + + double alpha = eye_.z() / (eye_.z() - n.z()); + QVector3D c = (1 - alpha) * eye_ + alpha * n; + + return projection()->pointToCoord(c); +} + +QPointF Map2DPrivate::coordinateToScreenPosition(const QGeoCoordinate &coordinate) const +{ + QVector3D c = projection()->coordToPoint(coordinate); + QVector3D d = projectionMatrix_.map(c); + return QPointF((d.x() + 1.0) * width() / 2.0, (-d.y() + 1.0) * height() / 2.0); +} |