diff --git a/src/lib/GeoDataTreeModel.cpp b/src/lib/GeoDataTreeModel.cpp index 783fc9bd8..bd71da60a 100644 --- a/src/lib/GeoDataTreeModel.cpp +++ b/src/lib/GeoDataTreeModel.cpp @@ -1,631 +1,631 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010 Thibaut Gridel // // Own #include "GeoDataTreeModel.h" // Qt #include #include #include #include // Marble #include "GeoDataObject.h" #include "GeoDataDocument.h" #include "GeoDataContainer.h" #include "GeoDataExtendedData.h" #include "GeoDataPlacemark.h" #include "GeoDataStyle.h" #include "GeoDataTypes.h" #include "FileManager.h" #include "MarbleDebug.h" #include "MarblePlacemarkModel.h" using namespace Marble; class GeoDataTreeModel::Private { public: Private(); ~Private(); GeoDataDocument* m_rootDocument; bool m_ownsRootDocument; }; GeoDataTreeModel::Private::Private() : m_rootDocument( new GeoDataDocument ), m_ownsRootDocument( true ) { // nothing to do } GeoDataTreeModel::Private::~Private() { if ( m_ownsRootDocument ) { delete m_rootDocument; } } GeoDataTreeModel::GeoDataTreeModel( QObject *parent ) : QAbstractItemModel( parent ), d( new Private ) { } GeoDataTreeModel::~GeoDataTreeModel() { delete d; } bool GeoDataTreeModel::hasChildren( const QModelIndex &parent ) const { GeoDataObject *parentItem; if ( parent.column() > 0 ) { return false; } if ( !parent.isValid() ) { parentItem = d->m_rootDocument; } else { parentItem = static_cast( parent.internalPointer() ); } if ( !parentItem ) { return false; } if ( parentItem->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( parentItem ); return dynamic_cast( placemark->geometry() ); } if ( parentItem->nodeType() == GeoDataTypes::GeoDataFolderType || parentItem->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataContainer *container = static_cast( parentItem ); return container->size(); } if ( parentItem->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) { GeoDataMultiGeometry *geometry = static_cast( parentItem ); return geometry->size(); } return false; } int GeoDataTreeModel::rowCount( const QModelIndex &parent ) const { // mDebug() << "rowCount"; GeoDataObject *parentItem; if ( parent.column() > 0 ) { // mDebug() << "rowCount bad column"; return 0; } if ( !parent.isValid() ) { // mDebug() << "rowCount root parent"; parentItem = d->m_rootDocument; } else { parentItem = static_cast( parent.internalPointer() ); } if ( !parentItem ) { // mDebug() << "rowCount bad parent"; return 0; } if ( parentItem->nodeType() == GeoDataTypes::GeoDataFolderType || parentItem->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataContainer *container = static_cast( parentItem ); // mDebug() << "rowCount " << type << "(" << parentItem << ") =" << container->size(); return container->size(); // } else { // mDebug() << "rowCount bad container " << container; } if ( parentItem->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( parentItem ); if ( dynamic_cast( placemark->geometry() ) ) { // mDebug() << "rowCount " << type << "(" << parentItem << ") = 1"; return 1; } } if ( parentItem->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) { GeoDataMultiGeometry *geometry = static_cast( parentItem ); // mDebug() << "rowCount " << parent << " " << type << " " << geometry->size(); return geometry->size(); // } else { // mDebug() << "rowCount bad geometry " << geometry; } // mDebug() << "rowcount end"; return 0;//parentItem->childCount(); } QVariant GeoDataTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( role == Qt::DisplayRole && orientation == Qt::Horizontal ) { switch ( section ) { case 0: return QString("Name"); break; case 1: return QString("Type"); break; case 2: return QString("Popularity"); break; case 3: return QString("PopIndex"); break; } } return QVariant(); } QVariant GeoDataTreeModel::data( const QModelIndex &index, int role ) const { // mDebug() << "data"; if ( !index.isValid() ) return QVariant(); GeoDataObject *object = static_cast( index.internalPointer() ); if ( role == Qt::DisplayRole ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( object ); if ( index.column() == 0 ){ return QVariant( placemark->name() ); } else if ( index.column() == 1 ){ return QVariant( placemark->nodeType() ); } else if ( index.column() == 2 ){ return QVariant( placemark->popularity() ); } else if ( index.column() == 3 ){ return QVariant( placemark->popularityIndex() ); } } if ( object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataFeature *feature = static_cast( object ); if ( index.column() == 0 ){ return QVariant( feature->name() ); } else if ( index.column() == 1 ){ return QVariant( feature->nodeType() ); } } GeoDataGeometry *geometry = dynamic_cast( object ); if ( geometry && index.column() == 1 ){ return QVariant( geometry->nodeType() ); } GeoDataObject *item = dynamic_cast( object ); if ( item && index.column() == 1 ){ return QVariant( item->nodeType() ); } } else if ( role == Qt::CheckStateRole && index.column() == 0 ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *feature = static_cast( object ); const char* type = feature->geometry()->nodeType(); if ( type == GeoDataTypes::GeoDataLineStringType || type == GeoDataTypes::GeoDataPolygonType || type == GeoDataTypes::GeoDataLinearRingType || type == GeoDataTypes::GeoDataMultiGeometryType || type == GeoDataTypes::GeoDataTrackType ) { if ( feature->isGloballyVisible() ) { return QVariant( Qt::Checked ); } else if ( feature->isVisible() ) { return QVariant( Qt::PartiallyChecked ); } else { return QVariant( Qt::Unchecked ); } } } else if ( object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataFeature *feature = static_cast( object ); if ( feature->isGloballyVisible() ) { return QVariant( Qt::Checked ); } else if ( feature->isVisible() ) { return QVariant( Qt::PartiallyChecked ); } else { return QVariant( Qt::Unchecked ); } } } else if ( role == Qt::DecorationRole && index.column() == 0 ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType || object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataFeature *feature = static_cast( object ); return QVariant(feature->style()->iconStyle().icon()); } } else if ( role == Qt::ToolTipRole && index.column() == 0 ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType || object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataFeature *feature = static_cast( object ); return QVariant( feature->description() ); } } else if ( role == MarblePlacemarkModel::ObjectPointerRole ) { return qVariantFromValue( object ); } else if ( role == MarblePlacemarkModel::PopularityIndexRole ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( object ); return QVariant( placemark->popularityIndex() ); } } else if ( role == MarblePlacemarkModel::PopularityRole ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( object ); return QVariant( placemark->popularity() ); } } else if ( role == MarblePlacemarkModel::CoordinateRole ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( object ); return qVariantFromValue( placemark->coordinate() ); } } return QVariant(); } QModelIndex GeoDataTreeModel::index( int row, int column, const QModelIndex &parent ) const { // mDebug() << "index"; if ( !hasIndex( row, column, parent ) ) { // mDebug() << "index bad index"; return QModelIndex(); } GeoDataObject *parentItem; if ( !parent.isValid() ) parentItem = d->m_rootDocument; else parentItem = static_cast( parent.internalPointer() ); if ( !parentItem ) { // mDebug() << "index bad parent"; return QModelIndex(); } GeoDataObject *childItem = 0; if ( parentItem->nodeType() == GeoDataTypes::GeoDataFolderType || parentItem->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataContainer *container = static_cast( parentItem ); childItem = container->child( row ); return createIndex( row, column, childItem ); } if ( parentItem->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *placemark = static_cast( parentItem ); childItem = placemark->geometry(); if ( dynamic_cast( childItem ) ) { return createIndex( row, column, childItem ); } } if ( parentItem->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) { GeoDataMultiGeometry *geometry = static_cast( parentItem ); childItem = geometry->child( row ); return createIndex( row, column, childItem ); } return QModelIndex(); } QModelIndex GeoDataTreeModel::parent( const QModelIndex &index ) const { // mDebug() << "parent"; if ( !index.isValid() ) { // mDebug() << "parent bad index"; return QModelIndex(); } GeoDataObject *childObject = static_cast( index.internalPointer() ); if ( childObject ) { /// parentObject can be a container, placemark or multigeometry GeoDataObject *parentObject = childObject->parent(); if ( parentObject == d->m_rootDocument ) { return QModelIndex(); } GeoDataObject *greatParentObject = parentObject->parent(); - // FIXME ANDER SOMETIMES NULL GREATPARENT + // Avoid crashing when there is no grandparent if ( greatParentObject == 0 ) { return QModelIndex(); } // greatParent can be a container if ( greatParentObject->nodeType() == GeoDataTypes::GeoDataFolderType || greatParentObject->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataContainer *greatparentContainer = static_cast( greatParentObject ); GeoDataFeature *parentFeature = static_cast( parentObject ); // mDebug() << "parent " << childObject->nodeType() << "(" << childObject << ") = " // << parentObject->nodeType() << "[" << greatparentContainer->childPosition( parentFeature ) << "](" << parentObject << ")"; return createIndex( greatparentContainer->childPosition( parentFeature ), 0, parentObject ); } // greatParent can be a placemark if ( greatParentObject->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { // GeoDataPlacemark *greatparentPlacemark = static_cast( greatParentObject ); // mDebug() << "parent " << childObject->nodeType() << "(" << childObject << ") = " // << parentObject->nodeType() << "[0](" << parentObject << ")"; return createIndex( 0, 0, parentObject ); } // greatParent can be a multigeometry if ( greatParentObject->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) { GeoDataMultiGeometry *greatparentMultiGeo = static_cast( greatParentObject ); GeoDataGeometry *parentGeometry = static_cast( parentObject ); // mDebug() << "parent " << childObject->nodeType() << "(" << childObject << ") = " // << parentObject->nodeType() << "[" << greatParentItem->childPosition( parentGeometry ) << "](" << parentObject << ")"; return createIndex( greatparentMultiGeo->childPosition( parentGeometry ), 0, parentObject ); } } // mDebug() << "parent unknown index"; return QModelIndex(); } int GeoDataTreeModel::columnCount( const QModelIndex & ) const { return 4; } bool GeoDataTreeModel::setData ( const QModelIndex & index, const QVariant & value, int role ) { if ( !index.isValid() ) return false; GeoDataObject *object = static_cast( index.internalPointer() ); if ( role == Qt::CheckStateRole ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType || object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataFeature *feature = static_cast( object ); feature->setVisible( value.toBool() ); mDebug() << "setData " << feature->name() << " " << value.toBool(); emit dataChanged( index, index ); return true; } } else if ( role == Qt::EditRole ) { if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType || object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataFeature *feature = static_cast( object ); feature->setName( value.toString() ); mDebug() << "setData " << feature->name() << " " << value.toString(); emit dataChanged( index, index ); return true; } } return false; } Qt::ItemFlags GeoDataTreeModel::flags ( const QModelIndex & index ) const { if ( !index.isValid() ) return Qt::NoItemFlags; GeoDataObject *object = static_cast( index.internalPointer() ); if ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType || object->nodeType() == GeoDataTypes::GeoDataFolderType || object->nodeType() == GeoDataTypes::GeoDataDocumentType ) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable; } return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QModelIndex GeoDataTreeModel::index( GeoDataObject *object ) { //It first runs bottom-top, storing every ancestor of the object, and //then goes top-down retrieving the QModelIndex of every ancestor until reaching the //index of the requested object. //The TreeModel contains: Documents, Folders, Placemarks, MultiGeometries //and Geometries that are children of MultiGeometries //You can not call this function with an element that does not belong to the tree Q_ASSERT( ( object->nodeType() == GeoDataTypes::GeoDataFolderType ) || ( object->nodeType() == GeoDataTypes::GeoDataDocumentType ) || ( object->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) || ( ( object->nodeType() == GeoDataTypes::GeoDataLineStringType ) && ( object->parent()->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) ) || ( ( object->nodeType() == GeoDataTypes::GeoDataLinearRingType ) && ( object->parent()->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) ) || ( ( object->nodeType() == GeoDataTypes::GeoDataPointType ) && ( object->parent()->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) ) || ( ( object->nodeType() == GeoDataTypes::GeoDataPolygonType ) && ( object->parent()->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) ) || ( object->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) ); QList< GeoDataObject* > ancestors; GeoDataObject *itup = object; //Iterator to reach the top of the GeoDataDocument (bottom-up) while ( itup && ( itup != d->m_rootDocument ) ) {//We reach up to the rootDocument ancestors.append( itup ); itup = itup->parent() ; } QModelIndex itdown; if ( !ancestors.isEmpty() ) { itdown = index( d->m_rootDocument->childPosition( static_cast( ancestors.last() ) ),0,QModelIndex());//Iterator to go top down GeoDataObject *parent; while ( ( ancestors.size() > 1 ) ) { parent = static_cast( ancestors.last() ); if ( ( parent->nodeType() == GeoDataTypes::GeoDataFolderType ) || ( parent->nodeType() == GeoDataTypes::GeoDataDocumentType ) ) { ancestors.removeLast(); itdown = index( static_cast(parent)->childPosition( static_cast( ancestors.last() ) ) , 0, itdown ); } else if ( ( parent->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) ) { //The only child of the model is a Geometry or MultiGeometry object //If it is a geometry object, we should be on the bottom of the list ancestors.removeLast(); if( ancestors.last()->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) itdown = index( 0 , 0, itdown ); else itdown = QModelIndex(); } else if ( ( parent->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) ) { //The child is one of the geometry children of MultiGeometry ancestors.removeLast(); itdown = index( static_cast(parent)->childPosition( static_cast(ancestors.last()) ) , 0, itdown ); } else { //If the element is not found on the tree, it will be added under m_rootDocument itdown = QModelIndex(); break; } } } return itdown; } int GeoDataTreeModel::addFeature( GeoDataContainer *parent, GeoDataFeature *feature ) { int row = -1; if ( parent && feature ) { QModelIndex modelindex = index( parent ); //index(GeoDataObject*) returns QModelIndex() if parent == m_rootDocument //or if parent is not found on the tree. //We must check that we are in top of the tree (then QModelIndex() is //the right parent to insert the child object) or that we have a valid QModelIndex if( ( parent == d->m_rootDocument ) || modelindex.isValid() ) { row = parent->size(); beginInsertRows( modelindex , row , row ); parent->append( feature ); endInsertRows(); emit added(feature); } else mDebug() << "GeoDataTreeModel::addFeature (parent " << parent << " - feature" << feature << ") : parent not found on the TreeModel"; } else mDebug() << "Null pointer in call to GeoDataTreeModel::addFeature (parent " << parent << " - feature" << feature << ")"; return row; //-1 if it failed, the relative index otherwise. } int GeoDataTreeModel::addDocument( GeoDataDocument *document ) { return addFeature( d->m_rootDocument, document ); } bool GeoDataTreeModel::removeFeature( GeoDataContainer *parent, int row ) { if ( rowsize() ) { beginRemoveRows( index( parent ), row , row ); parent->remove( row ); endRemoveRows(); return true; } return false; //Tried to remove a row that is not contained in the parent. } bool GeoDataTreeModel::removeFeature( GeoDataFeature *feature ) { if ( feature && ( feature!=d->m_rootDocument ) ) {//We check to see we are not removing the //top level element m_rootDocument GeoDataObject *parent = static_cast< GeoDataObject* >( feature->parent() ); if ( ( parent->nodeType() == GeoDataTypes::GeoDataFolderType ) || ( parent->nodeType() == GeoDataTypes::GeoDataDocumentType ) ) { int row = static_cast< GeoDataContainer* >( feature->parent() )->childPosition( feature ); if ( row != -1 ) { if ( removeFeature( static_cast< GeoDataContainer* >( feature->parent() ) , row ) ) { emit removed(feature); return true; } else return false; } else return false; //The feature is not contained in the parent it points to } } return false; //We can not remove the rootDocument } void GeoDataTreeModel::updateFeature( GeoDataFeature *feature ) { QModelIndex featureIndex = index( feature ); if ( featureIndex.isValid() ) { emit dataChanged( featureIndex, featureIndex ); } } void GeoDataTreeModel::removeDocument( int index ) { removeFeature( d->m_rootDocument, index ); } void GeoDataTreeModel::removeDocument( GeoDataDocument *document ) { removeFeature( document ); } void GeoDataTreeModel::update() { // mDebug() << "updating GeoDataTreeModel"; reset(); } void GeoDataTreeModel::setRootDocument( GeoDataDocument* document ) { beginResetModel(); if ( d->m_ownsRootDocument ) { delete d->m_rootDocument; } d->m_ownsRootDocument = ( document == 0 ); d->m_rootDocument = document ? document : new GeoDataDocument; endResetModel(); } GeoDataDocument * GeoDataTreeModel::rootDocument() { return d->m_rootDocument; } #include "GeoDataTreeModel.moc" diff --git a/src/lib/StackedTile.cpp b/src/lib/StackedTile.cpp index 35623d15a..e51592584 100644 --- a/src/lib/StackedTile.cpp +++ b/src/lib/StackedTile.cpp @@ -1,309 +1,310 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2010 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2008-2010 Jens-Michael Hoffmann // #include "StackedTile.h" #include "StackedTile_p.h" #include #include "MarbleDebug.h" #include "Tile.h" using namespace Marble; static const uint **jumpTableFromQImage32( const QImage &img ) { if ( img.depth() != 48 && img.depth() != 32 ) return 0; const int height = img.height(); const int bpl = img.bytesPerLine() / 4; const uint *data = reinterpret_cast(img.bits()); const uint **jumpTable = new const uint*[height]; for ( int y = 0; y < height; ++y ) { jumpTable[ y ] = data; data += bpl; } return jumpTable; } static const uchar **jumpTableFromQImage8( const QImage &img ) { if ( img.depth() != 8 && img.depth() != 1 ) return 0; const int height = img.height(); const int bpl = img.bytesPerLine(); const uchar *data = img.bits(); const uchar **jumpTable = new const uchar*[height]; for ( int y = 0; y < height; ++y ) { jumpTable[ y ] = data; data += bpl; } return jumpTable; } StackedTilePrivate::StackedTilePrivate( const TileId &id, const QImage &resultImage, const GeoDataDocument &resultVector, QVector > const &tiles ) : m_id( id ), m_resultImage( resultImage ), m_resultVector( resultVector ), m_depth( resultImage.depth() ), m_isGrayscale( resultImage.isGrayscale() ), m_tiles( tiles ), jumpTable8( jumpTableFromQImage8( m_resultImage ) ), jumpTable32( jumpTableFromQImage32( m_resultImage ) ), m_byteCount( calcByteCount( resultImage, tiles ) ), m_isUsed( false ) { } StackedTilePrivate::~StackedTilePrivate() { delete [] jumpTable32; delete [] jumpTable8; } uint StackedTilePrivate::pixel( int x, int y ) const { - // FIXME ANDER, IMAGE CAN BE NULL + // Image can be null if it is a VectorTile + // VectorTile shouldnt call this method if ( m_resultImage.isNull() ) return 0; if ( m_depth == 8 ) { if ( m_isGrayscale ) return (jumpTable8)[y][x]; else return m_resultImage.color( (jumpTable8)[y][x] ); } if ( m_depth == 32 ) return (jumpTable32)[y][x]; if ( m_depth == 1 && !m_isGrayscale ) return m_resultImage.color((jumpTable8)[y][x/8] >> 7); return m_resultImage.pixel( x, y ); } uint StackedTilePrivate::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) const { // Bilinear interpolation to determine the color of a subpixel int iX = (int)(x); int iY = (int)(y); qreal fY = y - iY; // Interpolation in y-direction if ( ( iY + 1 ) < m_resultImage.height() ) { QRgb bottomLeftValue = pixel( iX, iY + 1 ); // #define CHEAPHIGH #ifdef CHEAPHIGH QRgb leftValue; if ( fY < 0.33 ) leftValue = topLeftValue; else if ( fY < 0.66 ) leftValue = (((bottomLeftValue ^ topLeftValue) & 0xfefefefeUL) >> 1) + (bottomLeftValue & topLeftValue); else leftValue = bottomLeftValue; #else // blending the color values of the top left and bottom left point qreal ml_red = ( 1.0 - fY ) * qRed ( topLeftValue ) + fY * qRed ( bottomLeftValue ); qreal ml_green = ( 1.0 - fY ) * qGreen( topLeftValue ) + fY * qGreen( bottomLeftValue ); qreal ml_blue = ( 1.0 - fY ) * qBlue ( topLeftValue ) + fY * qBlue ( bottomLeftValue ); #endif // Interpolation in x-direction if ( iX + 1 < m_resultImage.width() ) { qreal fX = x - iX; QRgb topRightValue = pixel( iX + 1, iY ); QRgb bottomRightValue = pixel( iX + 1, iY + 1 ); #ifdef CHEAPHIGH QRgb rightValue; if ( fY < 0.33 ) rightValue = topRightValue; else if ( fY < 0.66 ) rightValue = (((bottomRightValue ^ topRightValue) & 0xfefefefeUL) >> 1) + (bottomRightValue & topRightValue); else rightValue = bottomRightValue; QRgb averageValue; if ( fX < 0.33 ) averageValue = leftValue; else if ( fX < 0.66 ) averageValue = (((leftValue ^ rightValue) & 0xfefefefeUL) >> 1) + (leftValue & rightValue); else averageValue = rightValue; return averageValue; #else // blending the color values of the top right and bottom right point qreal mr_red = ( 1.0 - fY ) * qRed ( topRightValue ) + fY * qRed ( bottomRightValue ); qreal mr_green = ( 1.0 - fY ) * qGreen( topRightValue ) + fY * qGreen( bottomRightValue ); qreal mr_blue = ( 1.0 - fY ) * qBlue ( topRightValue ) + fY * qBlue ( bottomRightValue ); // blending the color values of the resulting middle left // and middle right points int mm_red = (int)( ( 1.0 - fX ) * ml_red + fX * mr_red ); int mm_green = (int)( ( 1.0 - fX ) * ml_green + fX * mr_green ); int mm_blue = (int)( ( 1.0 - fX ) * ml_blue + fX * mr_blue ); return qRgb( mm_red, mm_green, mm_blue ); #endif } else { #ifdef CHEAPHIGH return leftValue; #else return qRgb( ml_red, ml_green, ml_blue ); #endif } } else { // Interpolation in x-direction if ( iX + 1 < m_resultImage.width() ) { qreal fX = x - iX; if ( fX == 0.0 ) return topLeftValue; QRgb topRightValue = pixel( iX + 1, iY ); #ifdef CHEAPHIGH QRgb topValue; if ( fX < 0.33 ) topValue = topLeftValue; else if ( fX < 0.66 ) topValue = (((topLeftValue ^ topRightValue) & 0xfefefefeUL) >> 1) + (topLeftValue & topRightValue); else topValue = topRightValue; return topValue; #else // blending the color values of the top left and top right point int tm_red = (int)( ( 1.0 - fX ) * qRed ( topLeftValue ) + fX * qRed ( topRightValue ) ); int tm_green = (int)( ( 1.0 - fX ) * qGreen( topLeftValue ) + fX * qGreen( topRightValue ) ); int tm_blue = (int)( ( 1.0 - fX ) * qBlue ( topLeftValue ) + fX * qBlue ( topRightValue ) ); return qRgb( tm_red, tm_green, tm_blue ); #endif } } return topLeftValue; } int StackedTilePrivate::calcByteCount( const QImage &resultImage, const QVector > &tiles ) { int byteCount = resultImage.numBytes(); QVector >::const_iterator pos = tiles.constBegin(); QVector >::const_iterator const end = tiles.constEnd(); for (; pos != end; ++pos ) byteCount += (*pos)->byteCount(); return byteCount; } StackedTile::StackedTile( TileId const &id, QImage const &resultImage, const GeoDataDocument &resultVector, QVector > const &tiles ) : d( new StackedTilePrivate( id, resultImage, resultVector, tiles ) ) { Q_ASSERT( !tiles.isEmpty() ); if ( d->m_resultImage.isNull() && d->m_resultVector.size() == 0 ) { qWarning() << "A tile has no image and no vector data. Please rerun the application."; return; } if ( d->jumpTable32 == 0 && d->jumpTable8 == 0 ) { qWarning() << "Color depth" << d->m_depth << " is not supported."; } } StackedTile::~StackedTile() { delete d; } TileId const& StackedTile::id() const { return d->m_id; } void StackedTile::setUsed( bool used ) { d->m_isUsed = used; } bool StackedTile::used() const { return d->m_isUsed; } uint StackedTile::pixel( int x, int y ) const { return d->pixel( x, y ); } uint StackedTile::pixelF( qreal x, qreal y ) const { int iX = (int)(x); int iY = (int)(y); QRgb topLeftValue = pixel( iX, iY ); return d->pixelF( x, y, topLeftValue ); } uint StackedTile::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) const { return d->pixelF( x, y, topLeftValue ); } int StackedTile::depth() const { return d->m_depth; } int StackedTile::numBytes() const { return d->m_byteCount; } QVector > StackedTile::tiles() const { return d->m_tiles; } QImage const * StackedTile::resultImage() const { return &d->m_resultImage; } -const GeoDataDocument *StackedTile::resultVectorData() const +GeoDataDocument const * StackedTile::resultVectorData() const { return &d->m_resultVector; } diff --git a/src/lib/StackedTile.h b/src/lib/StackedTile.h index 5311db264..942b2b3ef 100644 --- a/src/lib/StackedTile.h +++ b/src/lib/StackedTile.h @@ -1,128 +1,128 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2007-2010 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2010 Jens-Michael Hoffmann // #ifndef MARBLE_STACKEDTILE_H #define MARBLE_STACKEDTILE_H #include #include #include #include "MarbleGlobal.h" #include "GeoDataContainer.h" class QImage; namespace Marble { class StackedTilePrivate; class Tile; class TileId; /*! \class StackedTile \brief A single tile that consists of a stack of Tile layers. The StackedTile is a tile container that covers a certain area and is used for a particular zoom level. It consists of a stack of several individual thematic Tiles that cover the very same area and are used for the very same zoom level: This stack of Tiles is built up from the ground: The first Tile at the bottom usually represents the ground surface. Optionally there might be a range of other Tiles stacked on top which cover e.g. relief, streets and clouds. For rendering the whole stack of tiles gets merged and blended into a single QImage. This merging/blending operation is usually only performed once the stack of tiles changes visually. As a result access to the visual composition of all TextureTile layers is very fast since it is reduced to a single QImage that also consumes very little memory. The whole mechanism is comparable to layers in applications like Gimp or Photoshop (TM) which can be blended on top of each other via so called filters and can be merged into a single layer if required. Restrictions: The Tiles that are part of the stack need to be of the same size and need to cover the same area at the same zoom level using the very same projection. */ class StackedTile { friend class StackedTileLoader; public: explicit StackedTile( TileId const &id, QImage const &resultImage, const GeoDataDocument &resultVector, QVector > const &tiles ); virtual ~StackedTile(); /*! \brief Returns a unique ID for the tile. \return A TileId object that encodes zoom level, position and map theme. */ TileId const& id() const; void setUsed( bool used ); bool used() const; int depth() const; int numBytes() const; /*! \brief Returns the stack of Tiles \return A container of Tile objects. */ QVector > tiles() const; /*! \brief Returns the QImage that describes the merged stack of Tiles \return A non-zero pointer to the resulting QImage */ QImage const * resultImage() const; /*! - \brief Returns the QImage that describes the merged stack of Tiles - \return A non-zero pointer to the resulting QImage + \brief Returns the GeoDataDocument that describes the merged stack of Tiles + \return A non-zero pointer to the resulting GeoDataDocument */ - const GeoDataDocument *resultVectorData() const; + GeoDataDocument const * resultVectorData() const; /*! \brief Returns the color value of the result tile at the given integer position. \return The uint that describes the color value of the given pixel Note: for gray scale images the color value of a single pixel is described via a uchar (1 byte) while for RGB(A) images uint (4 bytes) are used. */ uint pixel( int x, int y ) const; /*! \brief Returns the color value of the result tile at a given floating point position. \return The uint that describes the color value of the given pixel Subpixel calculation is done via bilinear interpolation. Note: for gray scale images the color value of a single pixel is described via a uchar (1 byte) while for RGB(A) images uint (4 bytes) are used. */ uint pixelF( qreal x, qreal y ) const; // This method passes the top left pixel (if known already) for better performance uint pixelF( qreal x, qreal y, const QRgb& pixel ) const; private: Q_DISABLE_COPY( StackedTile ) StackedTilePrivate *d; }; } #endif diff --git a/src/lib/Tile.cpp b/src/lib/Tile.cpp index 7bf6a2951..56f3c1c49 100644 --- a/src/lib/Tile.cpp +++ b/src/lib/Tile.cpp @@ -1,48 +1,46 @@ // Copyright 2010 Jens-Michael Hoffmann // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library. If not, see . #include "Tile.h" #include #include namespace Marble { Tile::Tile( TileId const & tileId, QString const &format, const Blending * blending ) : m_id( tileId ), m_blending( blending ), m_format( format ) { - // With vectortiles it might occur not having image - //FIXME ANDER Q_ASSERT( !image()->isNull() ); } Tile::~Tile() { } const char* Tile::nodeType() const { return "Tile"; } QString Tile::type() { return "tile"; } } diff --git a/src/lib/TileLoader.cpp b/src/lib/TileLoader.cpp index 3e54d4122..55bf16455 100644 --- a/src/lib/TileLoader.cpp +++ b/src/lib/TileLoader.cpp @@ -1,293 +1,291 @@ /* Copyright 2010 Jens-Michael Hoffmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "TileLoader.h" #include #include #include #include #include "MarbleRunnerManager.h" #include "QTreeView" #include "GeoSceneTiled.h" #include "GeoDataContainer.h" #include "HttpDownloadManager.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "TileLoaderHelper.h" Q_DECLARE_METATYPE( Marble::DownloadUsage ) namespace Marble { TileLoader::TileLoader(HttpDownloadManager * const downloadManager, const PluginManager *pluginManager) : m_pluginManager( pluginManager ) { qRegisterMetaType( "DownloadUsage" ); connect( this, SIGNAL( downloadTile( QUrl, QString, QString, DownloadUsage )), downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage ))); connect( downloadManager, SIGNAL( downloadComplete( QByteArray, QString )), SLOT( updateTile( QByteArray, QString ))); } void TileLoader::setTextureLayers( const QVector &textureLayers ) { foreach ( const GeoSceneTiled *texture, textureLayers ) { const uint hash = qHash( texture->sourceDir() ); m_textureLayers.insert( hash, texture ); } } // If the tile image file is locally available: // - if not expired: create ImageTile, set state to "uptodate", return it => done // - if expired: create TextureTile, state is set to Expired by default, trigger dl, QImage TileLoader::loadTileImage( TileId const & tileId, DownloadUsage const usage ) { GeoSceneTiled const * const textureLayer = findTextureLayer( tileId ); QString const fileName = tileFileName( textureLayer, tileId ); TileStatus status = tileStatus( tileId ); if ( status != Missing ) { // check if an update should be triggered if ( status == Available ) { mDebug() << "TileLoader::loadTileImage" << tileId.toString() << "StateUptodate"; } else { Q_ASSERT( status == Expired ); mDebug() << "TileLoader::loadTileImage" << tileId.toString() << "StateExpired"; triggerDownload( tileId, usage ); } QImage const image( fileName ); if ( !image.isNull() ) { // file is there, so create and return a tile object in any case return image; } } // tile was not locally available => trigger download and look for tiles in other levels // for scaling QImage replacementTile = scaledLowerLevelTile( tileId ); Q_ASSERT( !replacementTile.isNull() ); triggerDownload( tileId, usage ); return replacementTile; } GeoDataDocument *TileLoader::loadTileVectorData( TileId const & tileId, DownloadUsage const usage, QString const &format ) { GeoSceneTiled const * const textureLayer = findTextureLayer( tileId ); QString const fileName = tileFileName( textureLayer, tileId ); TileStatus status = tileStatus( tileId ); if ( status != Missing ) { // check if an update should be triggered if ( status == Available ) { mDebug() << "TileLoader::loadTileVectorData" << tileId.toString() << "StateUptodate"; } else { Q_ASSERT( status == Expired ); mDebug() << "TileLoader::loadTileVectorData" << tileId.toString() << "StateExpired"; triggerDownload( tileId, usage ); } QFile file ( fileName ); if ( file.exists() ) { // File is ready, so parse and return the vector data in any case MarbleRunnerManager* man = new MarbleRunnerManager( m_pluginManager ); GeoDataDocument* document = man->openFile( fileName ); - // FIXME ANDER sometimes the parser doesnt work - // maybe Q_ASSERT? if (document){ emit tileCompleted( tileId, document, format ); return document; } } } // tile was not locally available => trigger download triggerDownload( tileId, usage ); return new GeoDataDocument; } // This method triggers a download of the given tile (without checking // expiration). It is called by upper layer (StackedTileLoader) when the tile // that should be reloaded is currently loaded in memory. // // post condition // - download is triggered void TileLoader::reloadTile( TileId const &tileId, DownloadUsage const usage ) { triggerDownload( tileId, usage ); } void TileLoader::downloadTile( TileId const & tileId ) { triggerDownload( tileId, DownloadBulk ); } int TileLoader::maximumTileLevel( GeoSceneTiled const & texture ) { // if maximum tile level is configured in the DGML files, // then use it, otherwise use old detection code. if ( texture.maximumTileLevel() >= 0 ) { return texture.maximumTileLevel(); } int maximumTileLevel = -1; QString tilepath = MarbleDirs::path( texture.themeStr() ); // mDebug() << "StackedTileLoader::maxPartialTileLevel tilepath" << tilepath; QStringList leveldirs = QDir( tilepath ).entryList( QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot ); QStringList::const_iterator it = leveldirs.constBegin(); QStringList::const_iterator const end = leveldirs.constEnd(); for (; it != end; ++it ) { bool ok = true; const int value = (*it).toInt( &ok, 10 ); if ( ok && value > maximumTileLevel ) maximumTileLevel = value; } // mDebug() << "Detected maximum tile level that contains data: " // << maxtilelevel; return maximumTileLevel + 1; } bool TileLoader::baseTilesAvailable( GeoSceneTiled const & texture ) { const int levelZeroColumns = texture.levelZeroColumns(); const int levelZeroRows = texture.levelZeroRows(); bool result = true; // Check whether the tiles from the lowest texture level are available // for ( int column = 0; result && column < levelZeroColumns; ++column ) { for ( int row = 0; result && row < levelZeroRows; ++row ) { const TileId id( texture.sourceDir(), 0, column, row ); const QString tilepath = tileFileName( &texture, id ); result &= QFile::exists( tilepath ); } } return result; } TileLoader::TileStatus TileLoader::tileStatus( const TileId &tileId ) const { GeoSceneTiled const * const textureLayer = findTextureLayer( tileId ); QString const fileName = tileFileName( textureLayer, tileId ); QFileInfo fileInfo( fileName ); if ( !fileInfo.exists() ) { return Missing; } const QDateTime lastModified = fileInfo.lastModified(); const int expireSecs = textureLayer->expire(); const bool isExpired = lastModified.secsTo( QDateTime::currentDateTime() ) >= expireSecs; return isExpired ? Expired : Available; } void TileLoader::updateTile( QByteArray const & data, QString const & tileId ) { TileId const id = TileId::fromString( tileId ); QImage const tileImage = QImage::fromData( data ); if ( tileImage.isNull() ) return; emit tileCompleted( id, tileImage ); } inline GeoSceneTiled const * TileLoader::findTextureLayer( TileId const & id ) const { GeoSceneTiled const * const textureLayer = m_textureLayers.value( id.mapThemeIdHash(), 0 ); Q_ASSERT( textureLayer ); return textureLayer; } QString TileLoader::tileFileName( GeoSceneTiled const * textureLayer, TileId const & tileId ) { QString const fileName = textureLayer->relativeTileFileName( tileId ); QFileInfo const dirInfo( fileName ); return dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName ); } void TileLoader::triggerDownload( TileId const & id, DownloadUsage const usage ) { GeoSceneTiled const * const textureLayer = findTextureLayer( id ); QUrl const sourceUrl = textureLayer->downloadUrl( id ); QString const destFileName = textureLayer->relativeTileFileName( id ); emit downloadTile( sourceUrl, destFileName, id.toString(), usage ); } QImage TileLoader::scaledLowerLevelTile( TileId const & id ) const { mDebug() << "TileLoader::scaledLowerLevelTile" << id.toString(); GeoSceneTiled const * const textureLayer = findTextureLayer( id ); for ( int level = qMax( 0, id.zoomLevel() - 1 ); level >= 0; --level ) { int const deltaLevel = id.zoomLevel() - level; TileId const replacementTileId( id.mapThemeIdHash(), level, id.x() >> deltaLevel, id.y() >> deltaLevel ); QString const fileName = tileFileName( textureLayer, replacementTileId ); mDebug() << "TileLoader::scaledLowerLevelTile" << "trying" << fileName; QImage toScale( fileName ); if ( level == 0 && toScale.isNull() ) { mDebug() << "No level zero tile installed in map theme dir. Falling back to a transparent image for now."; GeoSceneTiled const * const textureLayer = findTextureLayer( replacementTileId ); QSize tileSize = textureLayer->tileSize(); Q_ASSERT( !tileSize.isEmpty() ); // assured by textureLayer toScale = QImage( tileSize, QImage::Format_ARGB32_Premultiplied ); toScale.fill( qRgba( 0, 0, 0, 0 ) ); } if ( !toScale.isNull() ) { // which rect to scale? int const restTileX = id.x() % ( 1 << deltaLevel ); int const restTileY = id.y() % ( 1 << deltaLevel ); int const partWidth = toScale.width() >> deltaLevel; int const partHeight = toScale.height() >> deltaLevel; int const startX = restTileX * partWidth; int const startY = restTileY * partHeight; mDebug() << "QImage::copy:" << startX << startY << partWidth << partHeight; QImage const part = toScale.copy( startX, startY, partWidth, partHeight ); mDebug() << "QImage::scaled:" << toScale.size(); return part.scaled( toScale.size() ); } } Q_ASSERT_X( false, "scaled image", "level zero image missing" ); // not reached return QImage(); } } #include "TileLoader.moc" diff --git a/src/lib/VectorTileMapper.cpp b/src/lib/VectorTileMapper.cpp index 92c5e932a..f66a702d6 100644 --- a/src/lib/VectorTileMapper.cpp +++ b/src/lib/VectorTileMapper.cpp @@ -1,150 +1,147 @@ -// -// This file is part of the Marble Virtual Globe. -// -// This program is free software licensed under the GNU LGPL. You can -// find a copy of this license in LICENSE.txt in the top directory of -// the source code. -// -// Copyright 2007 Torsten Rahn -// Copyright 2011 Bernhard Beschow -// +/* + This file is part of the Marble Virtual Globe. + This program is free software licensed under the GNU LGPL. You can + find a copy of this license in LICENSE.txt in the top directory of + the source code. + + Copyright 2007 Torsten Rahn + Copyright 2011 Bernhard Beschow +*/ #include "VectorTileMapper.h" #include #include #include "MarbleGlobal.h" #include "GeoPainter.h" #include "GeoDataPolygon.h" #include "GeoDataDocument.h" #include "MarbleDebug.h" #include "Quaternion.h" #include "ScanlineTextureMapperContext.h" #include "StackedTileLoader.h" #include "StackedTile.h" #include "TextureColorizer.h" #include "ViewportParams.h" #include "MathHelper.h" using namespace Marble; class VectorTileMapper::RenderJob : public QRunnable { public: RenderJob( StackedTileLoader *tileLoader, int tileLevel, const ViewportParams *viewport ); virtual void run(); int lon2tilex(double lon, int z); int lat2tiley(double lat, int z); private: StackedTileLoader *const m_tileLoader; const int m_tileLevel; const ViewportParams *const m_viewport; }; VectorTileMapper::RenderJob::RenderJob( StackedTileLoader *tileLoader, int tileLevel, const ViewportParams *viewport ) : m_tileLoader( tileLoader ), m_tileLevel( tileLevel ), m_viewport( viewport ) { } VectorTileMapper::VectorTileMapper( StackedTileLoader *tileLoader ) : TextureMapperInterface() , m_tileLoader( tileLoader ) , m_repaintNeeded( true ) , m_radius( 0 ) , m_threadPool() { } void VectorTileMapper::mapTexture( GeoPainter *painter, const ViewportParams *viewport, const QRect &dirtyRect, TextureColorizer *texColorizer ) { if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() ) { const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport ); if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat ) { m_canvasImage = QImage( viewport->size(), optimalFormat ); } if ( !viewport->mapCoversViewport() ) { m_canvasImage.fill( 0 ); } m_radius = viewport->radius(); m_repaintNeeded = true; } if ( m_repaintNeeded ) { mapTexture( viewport, painter->mapQuality() ); m_repaintNeeded = false; } const int radius = (int)(1.05 * (qreal)(viewport->radius())); QRect rect( viewport->width() / 2 - radius, viewport->height() / 2 - radius, 2 * radius, 2 * radius); rect = rect.intersect( dirtyRect ); painter->drawImage( rect, m_canvasImage, rect ); } void VectorTileMapper::setRepaintNeeded() { m_repaintNeeded = true; } void VectorTileMapper::mapTexture( const ViewportParams *viewport, MapQuality mapQuality ) { Q_UNUSED( mapQuality); // Reset backend m_tileLoader->resetTilehash(); QRunnable *const job = new RenderJob( m_tileLoader, tileZoomLevel(), viewport ); m_threadPool.start( job ); // FIXME ANDER COMMENTING THE LINE AT MARBLEMODEL WE DON'T NEED TO WAIT FOR FINISHING // m_threadPool.waitForDone(); m_tileLoader->cleanupTilehash(); } void VectorTileMapper::RenderJob::run() { /** FIXME ANDER TEST LOGIC FOR DOWNLOADING ALL THE TILES THAT ARE IN THE CURRENT ZOOM LEVEL AND INSIDE THE SCREEN **/ - // More info: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Subtiles - int minTileX = lon2tilex( m_viewport->viewLatLonAltBox().west(GeoDataCoordinates::Degree), m_tileLevel ); int minTileY = lat2tiley( m_viewport->viewLatLonAltBox().north(GeoDataCoordinates::Degree), m_tileLevel ); int maxTileX = lon2tilex( m_viewport->viewLatLonAltBox().east(GeoDataCoordinates::Degree), m_tileLevel ); int maxTileY = lat2tiley( m_viewport->viewLatLonAltBox().south(GeoDataCoordinates::Degree), m_tileLevel ); for (int x = minTileX; x < maxTileX; x++) for (int y = minTileY; y < maxTileY; y++) if ( x >= 0 && y >= 0) m_tileLoader->loadTile(TileId( 0, m_tileLevel, x, y )); } int VectorTileMapper::RenderJob::lon2tilex(double lon, int z) { return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); } int VectorTileMapper::RenderJob::lat2tiley(double lat, int z) { return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); } diff --git a/src/lib/geodata/handlers/dgml/DgmlVectortileTagHandler.cpp b/src/lib/geodata/handlers/dgml/DgmlVectortileTagHandler.cpp index 05910afc6..6b4d0488b 100644 --- a/src/lib/geodata/handlers/dgml/DgmlVectortileTagHandler.cpp +++ b/src/lib/geodata/handlers/dgml/DgmlVectortileTagHandler.cpp @@ -1,73 +1,76 @@ /* Copyright (C) 2007 Nikolas Zimmermann This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "DgmlVectortileTagHandler.h" #include #include "MarbleDebug.h" #include "DgmlElementDictionary.h" #include "DgmlAttributeDictionary.h" #include "DgmlAuxillaryDictionary.h" #include "GeoParser.h" #include "GeoSceneLayer.h" #include "GeoSceneVectorTile.h" namespace Marble { namespace dgml { DGML_DEFINE_TAG_HANDLER(Vectortile) GeoNode* DgmlVectortileTagHandler::parse(GeoParser& parser) const { // Check whether the tag is valid Q_ASSERT(parser.isStartElement() && parser.isValidElement(dgmlTag_Vectortile)); const QString name = parser.attribute(dgmlAttr_name).trimmed(); const QString expireStr = parser.attribute(dgmlAttr_expire).trimmed(); int expire = std::numeric_limits::max(); if ( !expireStr.isEmpty() ) expire = expireStr.toInt(); GeoSceneTiled *texture = 0; // Checking for parent item GeoStackItem parentItem = parser.parentElement(); // Check parent type and make sure that the dataSet type // matches the backend of the parent layer if ( parentItem.represents(dgmlTag_Layer) - && parentItem.nodeAs()->backend() == dgmlValue_vectortile ) { //FIXME ANDER REALLY NEEDED? + && parentItem.nodeAs()->backend() == dgmlValue_vectortile ) { + + //FIXME ANDER Using backend tag here and in other layers to decide which type of layer + // looks unnecesary because we have already parsed tag texture = new GeoSceneVectorTile( name ); texture->setExpire( expire ); parentItem.nodeAs()->addDataset( texture ); } return texture; } } } diff --git a/src/lib/graphicsview/MarbleGraphicsItem.cpp b/src/lib/graphicsview/MarbleGraphicsItem.cpp index 9da825d5f..9b3a07066 100644 --- a/src/lib/graphicsview/MarbleGraphicsItem.cpp +++ b/src/lib/graphicsview/MarbleGraphicsItem.cpp @@ -1,318 +1,314 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009-2010 Bastian Holst // #include "MarbleGraphicsItem.h" #include "MarbleGraphicsItem_p.h" // Marble #include "GeoPainter.h" #include "MarbleDebug.h" #include "ViewportParams.h" // Qt #include #include #include #include #include using namespace Marble; MarbleGraphicsItem::MarbleGraphicsItem( MarbleGraphicsItem *parent ) : d( new MarbleGraphicsItemPrivate( this, parent ) ) { } MarbleGraphicsItem::MarbleGraphicsItem( MarbleGraphicsItemPrivate *d_ptr ) : d( d_ptr ) { } MarbleGraphicsItem::~MarbleGraphicsItem() { delete d; } bool MarbleGraphicsItem::paintEvent( GeoPainter *painter, ViewportParams *viewport, const QString& renderPos, GeoSceneLayer *layer ) { if ( !p()->m_visibility ) { return true; } p()->updateLabelPositions(); p()->setProjection( viewport, painter ); // Remove the pixmap if it has been requested. This prevents QPixmapCache from being used // outside the ui thread. if ( p()->m_removeCachedPixmap ) { p()->m_removeCachedPixmap = false; QPixmapCache::remove( p()->m_cacheKey ); } if ( p()->positions().size() == 0 ) { return true; } bool successful = true; // At the moment, as GraphicsItems can't be zoomed or rotated ItemCoordinateCache // and DeviceCoordianteCache is exactly the same if ( ItemCoordinateCache == cacheMode() || DeviceCoordinateCache == cacheMode() ) { p()->ensureValidCacheKey(); QPixmap cachePixmap; #if QT_VERSION < 0x040600 bool pixmapAvailable = QPixmapCache::find( p()->m_cacheKey, cachePixmap ); #else bool pixmapAvailable = QPixmapCache::find( p()->m_cacheKey, &cachePixmap ); #endif if ( !pixmapAvailable ) { QSize neededPixmapSize = size().toSize() + QSize( 1, 1 ); // adding a pixel for rounding errors if ( cachePixmap.size() != neededPixmapSize ) { if ( size().isValid() && !size().isNull() ) { cachePixmap = QPixmap( neededPixmapSize ).copy(); } else { mDebug() << "Warning: Invalid pixmap size suggested: " << d->m_size; } } cachePixmap.fill( Qt::transparent ); GeoPainter pixmapPainter( &( cachePixmap ), viewport, NormalQuality ); // We paint in best quality here, as we only have to paint once. pixmapPainter.setRenderHint( QPainter::Antialiasing, true ); // The cache image will get a 0.5 pixel bounding to save antialiasing effects. pixmapPainter.translate( 0.5, 0.5 ); paint( &pixmapPainter, viewport, renderPos, layer ); // Paint children foreach ( MarbleGraphicsItem *item, p()->m_children ) { item->paintEvent( &pixmapPainter, viewport, renderPos, layer ); } // Update the pixmap in cache #if QT_VERSION < 0x040600 QPixmapCache::insert( p()->m_cacheKey, cachePixmap ); #else p()->m_cacheKey = QPixmapCache::insert( cachePixmap ); #endif } foreach( const QPointF& position, p()->positions() ) { - painter->save(); - painter->drawPixmap( position, cachePixmap ); - - painter->restore(); } } else { foreach( const QPointF& position, p()->positions() ) { painter->save(); painter->translate( position ); paint( painter, viewport, renderPos, layer ); // Paint children foreach ( MarbleGraphicsItem *item, p()->m_children ) { item->paintEvent( painter, viewport, renderPos, layer ); } painter->restore(); } } return successful; } bool MarbleGraphicsItem::contains( const QPointF& point ) const { foreach( const QRectF& rect, d->boundingRects() ) { if( rect.contains( point ) ) return true; } return false; } QRectF MarbleGraphicsItem::containsRect( const QPointF& point ) const { foreach( const QRectF& rect, d->boundingRects() ) { if( rect.contains( point ) ) return rect; } return QRectF(); } QList MarbleGraphicsItem::boundingRects() const { return p()->boundingRects(); } QSizeF MarbleGraphicsItem::size() const { return p()->m_size; } qreal MarbleGraphicsItem::zValue() const { return p()->m_zValue; } void MarbleGraphicsItem::setZValue( qreal z ) { p()->m_zValue = z; update(); } AbstractMarbleGraphicsLayout *MarbleGraphicsItem::layout() const { return p()->m_layout; } void MarbleGraphicsItem::setLayout( AbstractMarbleGraphicsLayout *layout ) { // Deleting the old layout delete p()->m_layout; p()->m_layout = layout; update(); } MarbleGraphicsItem::CacheMode MarbleGraphicsItem::cacheMode() const { return p()->m_cacheMode; } void MarbleGraphicsItem::setCacheMode( CacheMode mode ) { p()->m_cacheMode = mode; if ( p()->m_cacheMode == NoCache ) { p()->m_removeCachedPixmap = true; } } void MarbleGraphicsItem::update() { p()->m_removeCachedPixmap = true; // Update the parent. if ( p()->m_parent ) { p()->m_parent->update(); } } bool MarbleGraphicsItem::visible() const { return p()->m_visibility; } void MarbleGraphicsItem::setVisible( bool visible ) { p()->m_visibility = visible; } void MarbleGraphicsItem::hide() { setVisible( false ); } void MarbleGraphicsItem::show() { setVisible( true ); } void MarbleGraphicsItem::setSize( const QSizeF& size ) { p()->m_size = size; update(); foreach ( MarbleGraphicsItem *item, p()->m_children ) { item->p()->setParentSize( size ); } } QSizeF MarbleGraphicsItem::contentSize() const { return size(); } void MarbleGraphicsItem::setContentSize( const QSizeF& size ) { setSize( size ); } QRectF MarbleGraphicsItem::contentRect() const { return QRectF( QPointF( 0, 0 ), contentSize() ); } QString MarbleGraphicsItem::toolTip() const { return p()->m_toolTip; } void MarbleGraphicsItem::setToolTip( const QString& toolTip ) { p()->m_toolTip = toolTip; } void MarbleGraphicsItem::paint( GeoPainter *painter, ViewportParams *viewport, const QString& renderPos, GeoSceneLayer * layer ) { Q_UNUSED( viewport ); Q_UNUSED( renderPos ); Q_UNUSED( layer ); Q_UNUSED( painter ); } bool MarbleGraphicsItem::eventFilter( QObject *object, QEvent *e ) { if ( ! ( e->type() == QEvent::MouseButtonDblClick || e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease ) ) { return false; } QMouseEvent *event = static_cast (e); if( !p()->m_children.isEmpty() ) { QList absolutePositions = p()->absolutePositions(); foreach( const QPointF& absolutePosition, absolutePositions ) { QPoint shiftedPos = event->pos() - absolutePosition.toPoint(); if ( QRect( QPoint( 0, 0 ), size().toSize() ).contains( shiftedPos ) ) { foreach( MarbleGraphicsItem *child, p()->m_children ) { QList childRects = child->boundingRects(); foreach( const QRectF& childRect, childRects ) { if( childRect.toRect().contains( shiftedPos ) ) { if( child->eventFilter( object, e ) ) { return true; } } } } } } } return false; } MarbleGraphicsItemPrivate *MarbleGraphicsItem::p() const { return d; } diff --git a/src/lib/layers/VectorTileLayer.cpp b/src/lib/layers/VectorTileLayer.cpp index 7436da68f..31ddb5b57 100644 --- a/src/lib/layers/VectorTileLayer.cpp +++ b/src/lib/layers/VectorTileLayer.cpp @@ -1,443 +1,443 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2008-2009 Patrick Spendrin // Copyright 2010 Thibaut Gridel // #include "VectorTileLayer.h" #include #include #include #include #include "VectorTileMapper.h" #include "GeoPainter.h" #include "GeoSceneGroup.h" #include "MergedLayerDecorator.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "StackedTile.h" #include "StackedTileLoader.h" #include "SunLocator.h" #include "TextureColorizer.h" #include "TileLoader.h" #include "VectorComposer.h" #include "ViewportParams.h" #include "GeoDataTreeModel.h" namespace Marble { const int REPAINT_SCHEDULING_INTERVAL = 1000; struct CacheDocument { /** The CacheDocument takes ownership of doc */ CacheDocument( GeoDataDocument* doc, GeoDataTreeModel* model ); /** Remove the document from the tree and delete the document */ ~CacheDocument(); GeoDataDocument* document; GeoDataTreeModel* owner; private: Q_DISABLE_COPY( CacheDocument ) }; class VectorTileLayer::Private { public: Private(HttpDownloadManager *downloadManager, const SunLocator *sunLocator, VectorComposer *veccomposer, const PluginManager *pluginManager, VectorTileLayer *parent, GeoDataTreeModel *treeModel); void mapChanged(); void updateTextureLayers(); public: VectorTileLayer *const m_parent; const SunLocator *const m_sunLocator; VectorComposer *const m_veccomposer; TileLoader m_loader; MergedLayerDecorator m_layerDecorator; StackedTileLoader m_tileLoader; TextureMapperInterface *m_texmapper; TextureColorizer *m_texcolorizer; QVector m_textures; GeoSceneGroup *m_textureLayerSettings; // For scheduling repaints QTimer m_repaintTimer; // TreeModel for storing GeoDataDocuments GeoDataTreeModel *m_treeModel; // GeoDataDocuments (each tile is one) cache QCache< TileId, CacheDocument> m_documents; }; CacheDocument::CacheDocument( GeoDataDocument* doc, GeoDataTreeModel* model ) : document( doc ), owner( model ) { // nothing to do } CacheDocument::~CacheDocument() { Q_ASSERT( owner ); owner->removeDocument( document ); delete document; } VectorTileLayer::Private::Private(HttpDownloadManager *downloadManager, const SunLocator *sunLocator, VectorComposer *veccomposer, const PluginManager *pluginManager, VectorTileLayer *parent, GeoDataTreeModel *treeModel) : m_parent( parent ) , m_sunLocator( sunLocator ) , m_veccomposer( veccomposer ) , m_loader( downloadManager, pluginManager ) , m_layerDecorator( &m_loader, sunLocator ) , m_tileLoader( &m_layerDecorator ) , m_texmapper( 0 ) , m_texcolorizer( 0 ) , m_textureLayerSettings( 0 ) , m_repaintTimer() , m_treeModel( treeModel ) { } void VectorTileLayer::Private::mapChanged() { if ( m_texmapper ) { m_texmapper->setRepaintNeeded(); } if ( !m_repaintTimer.isActive() ) { m_repaintTimer.start(); } } void VectorTileLayer::Private::updateTextureLayers() { QVector result; foreach ( const GeoSceneTiled *candidate, m_textures ) { // Check if the GeoSceneTiled is a TextureTile or VectorTile. // Only VectorTiles have to be used. if ( QString (candidate->nodeType()) == "GeoSceneVectorTile"){ bool enabled = true; if ( m_textureLayerSettings ) { const bool propertyExists = m_textureLayerSettings->propertyValue( candidate->name(), enabled ); enabled |= !propertyExists; // if property doesn't exist, enable texture nevertheless } if ( enabled ) { result.append( candidate ); mDebug() << "enabling texture" << candidate->name(); } else { mDebug() << "disabling texture" << candidate->name(); } } } if ( !result.isEmpty() ) { const GeoSceneTiled *const firstTexture = result.at( 0 ); m_layerDecorator.setLevelZeroLayout( firstTexture->levelZeroColumns(), firstTexture->levelZeroRows() ); m_layerDecorator.setThemeId( "maps/" + firstTexture->sourceDir() ); } m_tileLoader.setTextureLayers( result ); m_loader.setTextureLayers( result ); } VectorTileLayer::VectorTileLayer(HttpDownloadManager *downloadManager, const SunLocator *sunLocator, VectorComposer *veccomposer , const PluginManager *pluginManager, GeoDataTreeModel *treeModel ) : QObject() , d( new Private( downloadManager, sunLocator, veccomposer, pluginManager, this, treeModel ) ) { qRegisterMetaType( "TileId" ); qRegisterMetaType( "GeoDataDocument*" ); connect( &d->m_loader, SIGNAL( tileCompleted( TileId, GeoDataDocument*, QString ) ), this, SLOT( updateTile( TileId, GeoDataDocument*, QString ) ) ); // Repainting is too much load, we wont repaint. // Repaint timer // d->m_repaintTimer.setSingleShot( true ); // d->m_repaintTimer.setInterval( REPAINT_SCHEDULING_INTERVAL ); // connect( &d->m_repaintTimer, SIGNAL( timeout() ), // this, SIGNAL( repaintNeeded() ) ); -// connect( d->m_veccomposer, SIGNAL( datasetLoaded() ), -// this, SLOT( mapChanged() ) ); + connect( d->m_veccomposer, SIGNAL( datasetLoaded() ), + this, SLOT( mapChanged() ) ); } VectorTileLayer::~VectorTileLayer() { delete d->m_texmapper; delete d->m_texcolorizer; delete d; } QStringList VectorTileLayer::renderPosition() const { return QStringList() << "SURFACE"; } bool VectorTileLayer::showSunShading() const { return d->m_layerDecorator.showSunShading(); } bool VectorTileLayer::showCityLights() const { return d->m_layerDecorator.showCityLights(); } void VectorTileLayer::updateTile(TileId const & tileId, GeoDataDocument * document, QString const &format ) { Q_UNUSED( format ); d->m_treeModel->addDocument( document ); d->m_documents.insert( tileId, new CacheDocument( document, d->m_treeModel ) ); } bool VectorTileLayer::render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) { Q_UNUSED( renderPos ); Q_UNUSED( layer ); // Stop repaint timer if it is already running d->m_repaintTimer.stop(); if ( d->m_textures.isEmpty() ) return false; if ( !d->m_texmapper ) return false; // choose the smaller dimension for selecting the tile level, leading to higher-resolution results const int levelZeroWidth = d->m_tileLoader.tileSize().width() * d->m_tileLoader.tileColumnCount( 0 ); const int levelZeroHight = d->m_tileLoader.tileSize().height() * d->m_tileLoader.tileRowCount( 0 ); const int levelZeroMinDimension = qMin( levelZeroWidth, levelZeroHight ); qreal linearLevel = ( 4.0 * (qreal)( viewport->radius() ) / (qreal)( levelZeroMinDimension ) ); if ( linearLevel < 1.0 ) linearLevel = 1.0; // Dirty fix for invalid entry linearLevel // As our tile resolution doubles with each level we calculate // the tile level from tilesize and the globe radius via log(2) qreal tileLevelF = qLn( linearLevel ) / qLn( 2.0 ); int tileLevel = (int)( tileLevelF * 1.00001 ); // snap to the sharper tile level a tiny bit earlier // to work around rounding errors when the radius // roughly equals the global texture width // mDebug() << "tileLevelF: " << tileLevelF << " tileLevel: " << tileLevel; if ( tileLevel > d->m_tileLoader.maximumTileLevel() ) tileLevel = d->m_tileLoader.maximumTileLevel(); const bool changedTileLevel = tileLevel != d->m_texmapper->tileZoomLevel(); // mDebug() << "VectorTile Level was set to: " << tileLevel; d->m_texmapper->setTileLevel( tileLevel ); if ( changedTileLevel ) { d->m_documents.clear(); } const QRect dirtyRect = QRect( QPoint( 0, 0), viewport->size() ); d->m_texmapper->mapTexture( painter, viewport, dirtyRect, d->m_texcolorizer ); return true; } void VectorTileLayer::setShowRelief( bool show ) { if ( d->m_texcolorizer ) { d->m_texcolorizer->setShowRelief( show ); } } void VectorTileLayer::setShowSunShading( bool show ) { disconnect( d->m_sunLocator, SIGNAL( positionChanged( qreal, qreal ) ), this, SLOT( reset() ) ); if ( show ) { connect( d->m_sunLocator, SIGNAL( positionChanged( qreal, qreal ) ), this, SLOT( reset() ) ); } d->m_layerDecorator.setShowSunShading( show ); reset(); } void VectorTileLayer::setShowCityLights( bool show ) { d->m_layerDecorator.setShowCityLights( show ); reset(); } void VectorTileLayer::setShowTileId( bool show ) { d->m_layerDecorator.setShowTileId( show ); reset(); } void VectorTileLayer::setupTextureMapper( ) { if ( d->m_textures.isEmpty() ) return; // FIXME: replace this with an approach based on the factory method pattern. delete d->m_texmapper; d->m_texmapper = new VectorTileMapper( &d->m_tileLoader ); Q_ASSERT( d->m_texmapper ); } void VectorTileLayer::setNeedsUpdate() { if ( d->m_texmapper ) { d->m_texmapper->setRepaintNeeded(); } } void VectorTileLayer::setVolatileCacheLimit( quint64 kilobytes ) { d->m_tileLoader.setVolatileCacheLimit( kilobytes ); } void VectorTileLayer::reset() { mDebug() << Q_FUNC_INFO; d->m_tileLoader.clear(); d->mapChanged(); } void VectorTileLayer::reload() { d->m_tileLoader.reloadVisibleTiles(); } void VectorTileLayer::downloadTile( const TileId &tileId ) { d->m_tileLoader.downloadTile( tileId ); } void VectorTileLayer::setMapTheme( const QVector &textures, GeoSceneGroup *textureLayerSettings, const QString &seaFile, const QString &landFile ) { delete d->m_texcolorizer; d->m_texcolorizer = 0; if ( QFileInfo( seaFile ).isReadable() || QFileInfo( landFile ).isReadable() ) { d->m_texcolorizer = new TextureColorizer( seaFile, landFile, d->m_veccomposer ); } d->m_textures = textures; d->m_textureLayerSettings = textureLayerSettings; if ( d->m_textureLayerSettings ) { connect( d->m_textureLayerSettings, SIGNAL( valueChanged( QString, bool ) ), this, SLOT( updateTextureLayers() ) ); } d->updateTextureLayers(); } int VectorTileLayer::tileZoomLevel() const { if (!d->m_texmapper) return -1; return d->m_texmapper->tileZoomLevel(); } QSize VectorTileLayer::tileSize() const { return d->m_tileLoader.tileSize(); } GeoSceneTiled::Projection VectorTileLayer::tileProjection() const { return d->m_tileLoader.tileProjection(); } int VectorTileLayer::tileColumnCount( int level ) const { return d->m_tileLoader.tileColumnCount( level ); } int VectorTileLayer::tileRowCount( int level ) const { return d->m_tileLoader.tileRowCount( level ); } qint64 VectorTileLayer::volatileCacheLimit() const { return d->m_tileLoader.volatileCacheLimit(); } int VectorTileLayer::preferredRadiusCeil( int radius ) const { const int tileWidth = d->m_tileLoader.tileSize().width(); const int levelZeroColumns = d->m_tileLoader.tileColumnCount( 0 ); const qreal linearLevel = 4.0 * (qreal)( radius ) / (qreal)( tileWidth * levelZeroColumns ); const qreal tileLevelF = qLn( linearLevel ) / qLn( 2.0 ); const int tileLevel = qCeil( tileLevelF ); if ( tileLevel < 0 ) return ( tileWidth * levelZeroColumns / 4 ) >> (-tileLevel); return ( tileWidth * levelZeroColumns / 4 ) << tileLevel; } int VectorTileLayer::preferredRadiusFloor( int radius ) const { const int tileWidth = d->m_tileLoader.tileSize().width(); const int levelZeroColumns = d->m_tileLoader.tileColumnCount( 0 ); const qreal linearLevel = 4.0 * (qreal)( radius ) / (qreal)( tileWidth * levelZeroColumns ); const qreal tileLevelF = qLn( linearLevel ) / qLn( 2.0 ); const int tileLevel = qFloor( tileLevelF ); if ( tileLevel < 0 ) return ( tileWidth * levelZeroColumns / 4 ) >> (-tileLevel); return ( tileWidth * levelZeroColumns / 4 ) << tileLevel; } bool VectorTileLayer::isTileAvailable( const TileId &tileId ) const { return d->m_loader.tileStatus( tileId ) == TileLoader::Available; } } #include "VectorTileLayer.moc"