Commit ee50337f authored by Dennis Nienhüser's avatar Dennis Nienhüser
Browse files

Allow using absolute source directories in texture layers in .dgml files.

Extend .dgml by a tileSize tag (width and height attribute), usable inside textureLayer tags. Such .dgml files are backwards compatible.
Handle 'base tile missing' case more gracefully and return an empty image instead of triggering an assertion.
Move the default tile size (675) to global.h and replace hardcoded default tile sizes with it.
CCBUG: 249633
CCBUG: 263010
REVIEW: 6334

svn path=/trunk/KDE/kdeedu/marble/; revision=1217541
parent 1addfa08
......@@ -40,6 +40,7 @@
</texture>
<texture name="clouds_data" expire="10800"><!-- type="texture"-->
<sourcedir format="JPG"> earth/clouds </sourcedir>
<tileSize width="675" height="675" />
<installmap> clouds.jpg </installmap>
<storageLayout maximumTileLevel="1"/>
<blending name="CloudsBlending" />
......
......@@ -24,6 +24,7 @@
or 604800 seconds -->
<texture name="mapnik_data" expire="604800" >
<sourcedir format="PNG"> earth/openstreetmap </sourcedir>
<tileSize width="256" height="256" />
<storageLayout levelZeroColumns="1" levelZeroRows="1" maximumTileLevel="18" mode="OpenStreetMap" />
<projection name="Mercator" />
<downloadUrl protocol="http" host="a.tile.openstreetmap.org" path="/" />
......@@ -34,6 +35,7 @@
</texture>
<texture name="hillshading" expire="31536000" >
<sourcedir format="PNG"> earth/hillshading </sourcedir>
<tileSize width="256" height="256" />
<storageLayout levelZeroColumns="1" levelZeroRows="1" maximumTileLevel="17" mode="OpenStreetMap" />
<projection name="Mercator" />
<downloadUrl protocol="http" host="toolserver.org" path="/~cmarqu/hill/" />
......
......@@ -48,7 +48,8 @@ bool FileStoragePolicy::fileExists( const QString &fileName ) const
bool FileStoragePolicy::updateFile( const QString &fileName, const QByteArray &data )
{
const QString fullName( m_dataDirectory + '/' + fileName );
QFileInfo const dirInfo( fileName );
QString const fullName = dirInfo.isAbsolute() ? fileName : m_dataDirectory + '/' + fileName;
// Create directory if it doesn't exist yet...
QFileInfo info( fullName );
......
......@@ -11,7 +11,7 @@
#include "ImageLoadThread.h"
#include <QtGui/QColor>
#include "MarbleDebug.h"
#include "global.h"
#include "TileId.h"
......@@ -34,7 +34,7 @@ QImage ImageLoadThread::take( const TileId &tileId )
{
Q_UNUSED( tileId )
QImage tile( 675, 675, QImage::Format_RGB32 );
QImage tile( c_defaultTileSize, c_defaultTileSize, QImage::Format_RGB32 );
tile.fill( QColor( 0,0,255,255 ).rgb() );
return tile;
}
......
......@@ -724,7 +724,7 @@ GeoSceneDocument* MapWizard::createDocument()
texture->setInstallMap( document->head()->theme() + "." + d->format );
texture->setProjection( GeoSceneTexture::Equirectangular );
int imageWidth = QImage( image ).width();
int tileSize = 675;
int tileSize = c_defaultTileSize;
float approxMaxTileLevel = log( imageWidth / ( 2.0 * tileSize ) ) / log( 2.0 );
int maxTileLevel = 0;
......
......@@ -57,10 +57,6 @@ class TileCreatorPrivate
};
// FIXME: This shouldn't be defined here, but centrally somewhere
const uint tileSize = 675;
TileCreator::TileCreator(const QString& sourceDir, const QString& installMap,
const QString& dem, const QString& targetDir)
: QThread(0),
......@@ -127,7 +123,7 @@ void TileCreator::run()
}
// Calculating Maximum Tile Level
float approxMaxTileLevel = std::log( imageWidth / ( 2.0 * tileSize ) ) / std::log( 2.0 );
float approxMaxTileLevel = std::log( imageWidth / ( 2.0 * c_defaultTileSize ) ) / std::log( 2.0 );
int maxTileLevel = 0;
if ( approxMaxTileLevel == int( approxMaxTileLevel ) )
......@@ -147,17 +143,17 @@ void TileCreator::run()
// If the image size of the image source does not match the expected
// geometry we need to smooth-scale the image in advance to match
// the required size
bool needsScaling = ( imageWidth != 2 * maxRows * (int)(tileSize)
|| imageHeight != maxRows * (int)(tileSize) );
bool needsScaling = ( imageWidth != 2 * maxRows * (int)( c_defaultTileSize )
|| imageHeight != maxRows * (int)( c_defaultTileSize ) );
if ( needsScaling )
mDebug() << "Image Size doesn't match 2*n*TILEWIDTH x n*TILEHEIGHT geometry. Scaling ...";
int stdImageWidth = 2 * maxRows * tileSize;
int stdImageWidth = 2 * maxRows * c_defaultTileSize;
if ( stdImageWidth == 0 )
stdImageWidth = 2 * tileSize;
stdImageWidth = 2 * c_defaultTileSize;
int stdImageHeight = maxRows * tileSize;
int stdImageHeight = maxRows * c_defaultTileSize;
if ( stdImageWidth != imageWidth ) {
mDebug() <<
QString( "TileCreator::createTiles() The size of the final image will measure %1 x %2 pixels").arg(stdImageWidth).arg(stdImageHeight);
......@@ -213,7 +209,7 @@ void TileCreator::run()
if ( needsScaling ) {
// Pick the current row and smooth scale it
// to make it match the expected size
QSize destSize( stdImageWidth, tileSize );
QSize destSize( stdImageWidth, c_defaultTileSize );
row = row.scaled( destSize,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation );
......@@ -229,7 +225,7 @@ void TileCreator::run()
if ( d->m_cancelled )
return;
QImage tile = row.copy( m * stdImageWidth / mmax, 0, tileSize, tileSize );
QImage tile = row.copy( m * stdImageWidth / mmax, 0, c_defaultTileSize, c_defaultTileSize );
tileName = d->m_targetDir + ( QString("%1/%2/%2_%3.jpg")
.arg( maxTileLevel )
......@@ -313,29 +309,29 @@ void TileCreator::run()
tile.setColorTable( grayScalePalette );
uchar* destLine;
for ( uint y = 0; y < tileSize / 2; ++y ) {
for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
destLine = tile.scanLine( y );
const uchar* srcLine = img_topleft.scanLine( 2 * y );
for ( uint x = 0; x < tileSize / 2; ++x )
for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
destLine[x] = srcLine[ 2*x ];
}
for ( uint y = 0; y < tileSize / 2; ++y ) {
for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
destLine = tile.scanLine( y );
const uchar* srcLine = img_topright.scanLine( 2 * y );
for ( uint x = tileSize / 2; x < tileSize; ++x )
destLine[x] = srcLine[ 2 * ( x - tileSize / 2 ) ];
for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
}
for ( uint y = tileSize / 2; y < tileSize; ++y ) {
for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
destLine = tile.scanLine( y );
const uchar* srcLine = img_bottomleft.scanLine( 2 * ( y - tileSize / 2 ) );
for ( uint x = 0; x < tileSize / 2; ++x )
const uchar* srcLine = img_bottomleft.scanLine( 2 * ( y - c_defaultTileSize / 2 ) );
for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
destLine[ x ] = srcLine[ 2 * x ];
}
for ( uint y = tileSize / 2; y < tileSize; ++y ) {
for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
destLine = tile.scanLine( y );
const uchar* srcLine = img_bottomright.scanLine( 2 * ( y - tileSize/2 ) );
for ( uint x = tileSize / 2; x < tileSize; ++x )
destLine[x] = srcLine[ 2 * ( x - tileSize / 2 ) ];
const uchar* srcLine = img_bottomright.scanLine( 2 * ( y - c_defaultTileSize/2 ) );
for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
}
}
else {
......@@ -343,29 +339,29 @@ void TileCreator::run()
QRgb* destLine;
for ( uint y = 0; y < tileSize / 2; ++y ) {
for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
destLine = (QRgb*) tile.scanLine( y );
const QRgb* srcLine = (QRgb*) img_topleft.scanLine( 2 * y );
for ( uint x = 0; x < tileSize / 2; ++x )
for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
destLine[x] = srcLine[ 2 * x ];
}
for ( uint y = 0; y < tileSize / 2; ++y ) {
for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
destLine = (QRgb*) tile.scanLine( y );
const QRgb* srcLine = (QRgb*) img_topright.scanLine( 2 * y );
for ( uint x = tileSize / 2; x < tileSize; ++x )
destLine[x] = srcLine[ 2 * ( x - tileSize / 2 ) ];
for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
}
for ( uint y = tileSize / 2; y < tileSize; ++y ) {
for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
destLine = (QRgb*) tile.scanLine( y );
const QRgb* srcLine = (QRgb*) img_bottomleft.scanLine( 2 * ( y-tileSize/2 ) );
for ( uint x = 0; x < tileSize / 2; ++x )
const QRgb* srcLine = (QRgb*) img_bottomleft.scanLine( 2 * ( y-c_defaultTileSize/2 ) );
for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
destLine[x] = srcLine[ 2 * x ];
}
for ( uint y = tileSize / 2; y < tileSize; ++y ) {
for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
destLine = (QRgb*) tile.scanLine( y );
const QRgb* srcLine = (QRgb*) img_bottomright.scanLine( 2 * ( y-tileSize / 2 ) );
for ( uint x = tileSize / 2; x < tileSize; ++x )
destLine[x] = srcLine[ 2*( x-tileSize / 2 ) ];
const QRgb* srcLine = (QRgb*) img_bottomright.scanLine( 2 * ( y-c_defaultTileSize / 2 ) );
for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
destLine[x] = srcLine[ 2*( x-c_defaultTileSize / 2 ) ];
}
}
......
......@@ -77,6 +77,8 @@ QSharedPointer<TextureTile> TileLoader::loadTile( TileId const & stackedTileId,
// tile was not locally available => trigger download and look for tiles in other levels
// for scaling
QImage * replacementTile = scaledLowerLevelTile( tileId );
Q_ASSERT( replacementTile && !replacementTile->isNull() );
QSharedPointer<TextureTile> const tile( new TextureTile( tileId, replacementTile ));
tile->setStackedTileId( stackedTileId );
......@@ -226,7 +228,9 @@ inline GeoSceneTexture const * TileLoader::findTextureLayer( TileId const & id )
QString TileLoader::tileFileName( TileId const & tileId ) const
{
GeoSceneTexture const * const textureLayer = findTextureLayer( tileId );
return MarbleDirs::path( textureLayer->relativeTileFileName( 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 )
......@@ -242,13 +246,23 @@ QImage * TileLoader::scaledLowerLevelTile( TileId const & id )
{
mDebug() << "TileLoader::scaledLowerLevelTile" << id.toString();
for ( int level = id.zoomLevel() - 1; level >= 0; --level ) {
for ( int level = qMax<int>( 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( replacementTileId );
mDebug() << "TileLoader::scaledLowerLevelTile" << "trying" << fileName;
QImage const toScale( 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.";
GeoSceneTexture 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?
QSize const size = toScale.size();
......@@ -266,7 +280,7 @@ QImage * TileLoader::scaledLowerLevelTile( TileId const & id )
}
Q_ASSERT_X( false, "scaled image", "level zero image missing" ); // not reached
return new QImage();
return 0;
}
}
......
......@@ -39,6 +39,7 @@ const char* dgmlAttr_connect = "connect";
const char* dgmlAttr_expire = "expire";
const char* dgmlAttr_feature = "feature";
const char* dgmlAttr_format = "format";
const char* dgmlAttr_height = "height";
const char* dgmlAttr_host = "host";
const char* dgmlAttr_labelColor = "labelColor";
const char* dgmlAttr_levelZeroColumns = "levelZeroColumns";
......
......@@ -41,6 +41,7 @@ namespace dgml
extern const char* dgmlAttr_expire;
extern const char* dgmlAttr_feature;
extern const char* dgmlAttr_format;
extern const char* dgmlAttr_height;
extern const char* dgmlAttr_host;
extern const char* dgmlAttr_labelColor;
extern const char* dgmlAttr_levelZeroColumns;
......
......@@ -71,6 +71,7 @@ const char* dgmlTag_Target = "target";
const char* dgmlTag_Text = "text";
const char* dgmlTag_Texture = "texture";
const char* dgmlTag_Theme = "theme";
const char* dgmlTag_TileSize = "tileSize";
const char* dgmlTag_Value = "value";
const char* dgmlTag_Vector = "vector";
const char* dgmlTag_Visible = "visible";
......
......@@ -73,6 +73,7 @@ namespace dgml
extern const char* dgmlTag_Target;
extern const char* dgmlTag_Text;
extern const char* dgmlTag_Texture;
extern const char* dgmlTag_TileSize;
extern const char* dgmlTag_Theme;
extern const char* dgmlTag_Value;
extern const char* dgmlTag_Vector;
......
// 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 2011 Dennis Nienhüser <earthwings@gentoo.org>
//
#include "DgmlTileSizeTagHandler.h"
#include "DgmlAttributeDictionary.h"
#include "DgmlElementDictionary.h"
#include "GeoParser.h"
#include "GeoSceneTexture.h"
namespace Marble
{
namespace dgml
{
DGML_DEFINE_TAG_HANDLER(TileSize)
GeoNode* DgmlTileSizeTagHandler::parse( GeoParser& parser ) const
{
// Check whether the tag is valid
Q_ASSERT( parser.isStartElement() && parser.isValidElement( dgmlTag_TileSize ));
// Checking for parent item
GeoStackItem parentItem = parser.parentElement();
if ( !parentItem.represents( dgmlTag_Texture ))
return 0;
int width = parser.attribute(dgmlAttr_width).toInt();
int height = parser.attribute(dgmlAttr_height).toInt();
QSize const size( width, height );
if ( !size.isEmpty() ) {
parentItem.nodeAs<GeoSceneTexture>()->setTileSize( size );
}
return 0;
}
}
}
// 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 2011 Dennis Nienhüser <earthwings@gentoo.org>
//
#ifndef MARBLE_DGML_TILESIZETAGHANDLER_H
#define MARBLE_DGML_TILESIZETAGHANDLER_H
#include "GeoTagHandler.h"
namespace Marble
{
namespace dgml
{
class DgmlTileSizeTagHandler : public GeoTagHandler
{
public:
virtual GeoNode* parse( GeoParser& ) const;
};
}
}
#endif
......@@ -133,20 +133,41 @@ QVector<QUrl> GeoSceneTexture::downloadUrls() const
const QSize GeoSceneTexture::tileSize() const
{
if ( !m_tileSize.isValid() ) {
if ( m_tileSize.isEmpty() ) {
const TileId id( sourceDir(), 0, 0, 0 );
const QString path = MarbleDirs::path( relativeTileFileName( id ));
QString const fileName = relativeTileFileName( id );
QFileInfo const dirInfo( fileName );
QString const path = dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName );
QImage testTile( path );
Q_ASSERT( !testTile.isNull() );
m_tileSize = testTile.size();
Q_ASSERT( !tileSize().isEmpty() );
if ( testTile.isNull() ) {
mDebug() << "Tile size is missing in dgml and no base tile found in " << themeStr();
mDebug() << "Using default tile size " << c_defaultTileSize;
m_tileSize = QSize( c_defaultTileSize, c_defaultTileSize );
} else {
m_tileSize = testTile.size();
}
if ( m_tileSize.isEmpty() ) {
mDebug() << "Tile width or height cannot be 0. Falling back to default tile size.";
m_tileSize = QSize( c_defaultTileSize, c_defaultTileSize );
}
}
Q_ASSERT( !m_tileSize.isEmpty() );
return m_tileSize;
}
void GeoSceneTexture::setTileSize( const QSize &tileSize )
{
if ( tileSize.isEmpty() ) {
mDebug() << "Ignoring invalid tile size " << tileSize;
} else {
m_tileSize = tileSize;
}
}
GeoSceneTexture::Projection GeoSceneTexture::projection() const
{
return m_projection;
......@@ -214,7 +235,8 @@ QString GeoSceneTexture::relativeTileFileName( const TileId &id ) const
QString GeoSceneTexture::themeStr() const
{
return "maps/" + sourceDir();
QFileInfo const dirInfo( sourceDir() );
return dirInfo.isAbsolute() ? sourceDir() : "maps/" + sourceDir();
}
QList<DownloadPolicy *> GeoSceneTexture::downloadPolicies() const
......
......@@ -78,6 +78,7 @@ class GeoSceneTexture : public GeoSceneAbstractDataset
QVector<QUrl> downloadUrls() const;
const QSize tileSize() const;
void setTileSize( const QSize &tileSize );
Projection projection() const;
void setProjection( const Projection );
......
......@@ -260,6 +260,9 @@ const qreal EARTH_RADIUS = 6378000.0;
// Maximum level of base tiles
const int maxBaseTileLevel = 4;
// Default size (width and height) of tiles
const unsigned int c_defaultTileSize = 675;
class MarbleGlobalPrivate;
class MarbleLocale;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment