QString GeneralConfig::getConfigData(const QString& filePath, const QString& item, const QString& field) {
/* -returns data from config file stored under 'item' and in 'field' */
    QFile configFile( filePath );
    if ( ! configFile.exists() ) return QString("");
    configFile.open(QIODevice::ReadOnly | QIODevice::ReadWrite);
    QTextStream configData(&configFile);
    QRegularExpressionValidator commentExp( QRegularExpression("^#.*") );
    QRegularExpressionValidator itemExp( QRegularExpression(QString("^\\[%1\\]").arg(item)) );
    QRegularExpressionValidator otherItemExp( QRegularExpression(QString("^\\[.*\\]")) );
    QRegularExpressionValidator fieldExp( QRegularExpression(QString("^%1:.*").arg(field)) );
    bool itemFound = false;
    int pos = 0;
    while ( ! configData.atEnd() ) {
        QString line = configData.readLine();
        if ( commentExp.validate(line,pos) == QValidator::Acceptable) continue;
        else if ( itemExp.validate(line,pos) == QValidator::Acceptable && itemFound == false ) itemFound = true;
        else if ( itemFound == true && otherItemExp.validate(line,pos) == QValidator::Acceptable) return QString();
        else if ( itemFound == true && fieldExp.validate(line,pos) == QValidator::Acceptable) {
            configFile.close();
            return line.remove(0, field.length() + 1).trimmed();
        }
    }
    configFile.close();
    return QString("");
}
Beispiel #2
0
bool MapnikRendererClient::load()
{
	QString filename = fileInDirectory( m_directory, "Mapnik Renderer" );
	FileStream configData( filename );
	if ( !configData.open( QIODevice::ReadOnly ) )
		return false;

	quint32 zoomLevels, size;
	configData >> size >> zoomLevels;
	m_tileSize = size;
	for ( int i = 0; i < ( int ) zoomLevels; i++ )
	{
		quint32 zoom, minX, maxX, minY, maxY;
		configData >> zoom >> minX >> maxX >> minY >> maxY;
		if ( configData.status() == QDataStream::ReadPastEnd )
			return false;
		Box box;
		box.minX = minX;
		box.maxX = maxX;
		box.minY = minY;
		box.maxY = maxY;
		m_boxes.resize( zoom + 1 );
		m_boxes[zoom] = box;
		m_zoomLevels.push_back( zoom );
	}

	m_fileZoom = -1;
	m_indexFile = new QFile( this );
	m_tileFile = new QFile( this );

	return true;
}
Beispiel #3
0
std::string ConfigFile::ExportXML()
{
    TiXmlDocument doc( m_strFilename.c_str() );

    TiXmlElement configData( m_strIdentifier.c_str() );
    configData.SetAttribute( "version", "1.0");

    //Loop through all the configurations
    std::map<std::string, ConfigAttribute>::iterator configIter;
    for(configIter = m_mapConfig.begin(); configIter != m_mapConfig.end(); configIter++)
    {
        ConfigAttribute item = configIter->second;
        std::string firstItem = item.strAttribute;

        //replace whitespace with underscore
        std::replace(firstItem.begin(), firstItem.end(), ' ', '_');
        TiXmlElement elementConfigItem (firstItem.c_str() );
        elementConfigItem.SetAttribute( "value", item.strValue.c_str());

        //Add array sub items to the xml
        if(item.bIsArray)
        {
            for(std::vector<std::string>::iterator iter = item.listValues.begin(); iter != item.listValues.end(); iter++)
            {
                std::string strTemp = *iter;
                TiXmlElement elementArrayItem (firstItem.c_str() );
                elementArrayItem.SetAttribute( "value", strTemp.c_str());
                elementConfigItem.LinkEndChild(elementArrayItem.Clone());
            }
        }
        configData.LinkEndChild(elementConfigItem.Clone());
    }
	doc.LinkEndChild(configData.Clone());

    std::string strReturnXML;
    std::ostringstream strStream;
    
    strStream << doc;
    strReturnXML = strStream.str();
    
	//Save the config file
	return strReturnXML;
}
AddContentWindow::AddContentWindow(const ScenePtr &dest, QWidget *parent) :
    QWidget(parent),
    framework(dest->GetFramework()),
    scene(dest),
    position(float3::zero),
    uploadProgressStep(0),
    numFailedUploads(0),
    numSuccessfulUploads(0),
    numTotalUploads(0)
{
    setWindowModality(Qt::ApplicationModal/*Qt::WindowModal*/);
    setAttribute(Qt::WA_DeleteOnClose);
    setWindowTitle(tr("Entity And Asset Import"));

    QPixmap nullIcon(16,16); // do a null icon to hide the default ugly one
    nullIcon.fill(Qt::transparent);
    setWindowIcon(nullIcon);

    setStyleSheet(
        "QProgressBar {"
            "border: 1px solid grey; border-radius: 0px; text-align: center; background-color: rgb(244, 244, 244);"
        "}"
        "QProgressBar::chunk {"
            "background-color: qlineargradient(spread:pad, x1:0.028, y1:1, x2:0.972, y2:1, stop:0 rgba(194, 194, 194, 100), stop:1 rgba(115, 115, 115, 100));"
        "}"
    );

    QFont titleFont("Arial", 11);
    titleFont.setBold(true);

    QVBoxLayout *layout = new QVBoxLayout();
    layout->setContentsMargins(5, 5, 5, 5);
    setLayout(layout);

    // Entities ui
    entityView = new QWidget(this);
    QVBoxLayout *entitiesLayout = new QVBoxLayout();
    entitiesLayout->setContentsMargins(0, 0, 0, 0);
    entityView->setLayout(entitiesLayout);

    QLabel *entityLabel = new QLabel(tr("Entities"), this);
    entityLabel->setFont(titleFont);
    entityLabel->setStyleSheet("color: rgb(63, 63, 63);");

    entityTreeWidget = new EntityAndAssetTreeWidget(this);
    entityTreeWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
    entityTreeWidget->setColumnCount(3);
    entityTreeWidget->setHeaderLabels(QStringList(QStringList() << tr("Create") << tr("ID") << tr("Name")));
    entityTreeWidget->header()->setResizeMode(QHeaderView::ResizeToContents);

    QPushButton *selectAllEntitiesButton = new QPushButton(tr("Select All"));
    QPushButton *deselectAllEntitiesButton = new QPushButton(tr("Deselect All"));
    QHBoxLayout *entityButtonsLayout = new QHBoxLayout;
    QSpacerItem *entityButtonSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
    QSpacerItem *middleSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Fixed);

    QVBoxLayout *entitiesProgressLayout = new QVBoxLayout();
    entityStatusLabel = new QLabel(this);
    entityProgressBar = new QProgressBar(this);
    entityProgressBar->setFixedHeight(20);

    entitiesLayout->addWidget(entityLabel);
    entitiesLayout->addLayout(entitiesProgressLayout);
    entitiesLayout->addWidget(entityTreeWidget);
    entitiesProgressLayout->addWidget(entityStatusLabel);
    entitiesProgressLayout->addWidget(entityProgressBar);
    entityButtonsLayout->addWidget(selectAllEntitiesButton);
    entityButtonsLayout->addWidget(deselectAllEntitiesButton);
    entityButtonsLayout->addSpacerItem(entityButtonSpacer);
    //entitiesLayout->insertLayout(-1, entitiesProgressLayout);
    entitiesLayout->insertLayout(-1, entityButtonsLayout);
    entitiesLayout->insertSpacerItem(-1, middleSpacer);
    layout->addWidget(entityView);

    // Assets ui
    assetView = new QWidget(this);
    QVBoxLayout *assetsLayout = new QVBoxLayout();
    assetsLayout->setContentsMargins(0, 0, 0, 0);
    assetView->setLayout(assetsLayout);

    QLabel *assetLabel = new QLabel(tr("Assets"), this);
    assetLabel->setFont(titleFont);
    assetLabel->setStyleSheet("color: rgb(63, 63, 63);");

    assetTreeWidget = new EntityAndAssetTreeWidget(this);
    assetTreeWidget->setColumnCount(5);
    QStringList labels;
    labels << tr("Upload") << tr("Type") << tr("Source name") << tr("Source subname") << tr("Destination name");
    assetTreeWidget->setHeaderLabels(labels);

    QPushButton *selectAllAssetsButton = new QPushButton(tr("Select All"), this);
    QPushButton *deselectAllAssetsButton = new QPushButton(tr("Deselect All"), this);
    QHBoxLayout *assetButtonsLayout = new QHBoxLayout(this);
    QSpacerItem *assetButtonSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
    QLabel *storageLabel = new QLabel(tr("Asset storage:"), this);
    storageComboBox = new QComboBox(this);

    QVBoxLayout *uploadLayout = new QVBoxLayout();
    uploadStatusLabel = new QLabel(this);
    uploadProgressBar = new QProgressBar(this);
    uploadProgressBar->setFixedHeight(20);

    GenerateStorageComboBoxContents();

    uploadLayout->addWidget(uploadStatusLabel);
    uploadLayout->addWidget(uploadProgressBar);

    assetsLayout->addWidget(assetLabel);
    assetsLayout->addLayout(uploadLayout);
    assetsLayout->addWidget(assetTreeWidget);

    assetButtonsLayout->addWidget(selectAllAssetsButton);
    assetButtonsLayout->addWidget(deselectAllAssetsButton);
    assetButtonsLayout->addSpacerItem(assetButtonSpacer);
    assetButtonsLayout->addWidget(storageLabel);
    assetButtonsLayout->addWidget(storageComboBox);
    assetsLayout->insertLayout(-1, assetButtonsLayout);
    layout->addWidget(assetView);

    uploadStatusLabel->hide();
    uploadProgressBar->hide();
    entityStatusLabel->hide();
    entityProgressBar->hide();

    // General controls
    addContentButton = new QPushButton(tr("Add content"), this);
    cancelButton = new QPushButton(tr("Cancel"), this);

    QHBoxLayout *buttonsLayout = new QHBoxLayout();
    QSpacerItem *buttonSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
    buttonsLayout->addSpacerItem(buttonSpacer);
    buttonsLayout->addWidget(addContentButton);
    buttonsLayout->addWidget(cancelButton);
    layout->insertLayout(-1, buttonsLayout);

    resize(800, 400);

    connect(addContentButton, SIGNAL(clicked()), SLOT(AddContent()));
    connect(cancelButton, SIGNAL(clicked()), SLOT(close()));
    connect(selectAllEntitiesButton, SIGNAL(clicked()), SLOT(SelectAllEntities()));
    connect(deselectAllEntitiesButton, SIGNAL(clicked()), SLOT(DeselectAllEntities()));
    connect(selectAllAssetsButton, SIGNAL(clicked()), SLOT(SelectAllAssets()));
    connect(deselectAllAssetsButton, SIGNAL(clicked()), SLOT(DeselectAllAssets()));
    connect(assetTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), SLOT(CheckIfColumnIsEditable(QTreeWidgetItem *, int)));
    connect(storageComboBox, SIGNAL(currentIndexChanged(int)), SLOT(RewriteDestinationNames()));

    // If we saved recently used storage, and the storage exists in the current combo box, set it as current index.
    ConfigData configData(ConfigAPI::FILE_FRAMEWORK, cAddContentDialogSetting, cRecentStorageSetting, "", "");
    QString storageName = framework->Config()->Get(configData).toString();
    if (!storageName.isEmpty())
        for(int i = 0; i < storageComboBox->count(); ++i)
            if (storageComboBox->itemData(i) == storageName)
            {
                storageComboBox->setCurrentIndex(i);
                break;
            }

    connect(this, SIGNAL(AssetUploadCompleted(const AssetStoragePtr &, int, int)), SLOT(CreateEntities()));
}
Beispiel #5
0
QString SlaveConfig::configData(const QString &protocol, const QString &host, const QString &key)
{
   return configData(protocol, host)[key];
}
GlassWidget::GlassWidget(const QString &instance_name,QWidget *parent)
    : QFrame(parent)
{
    gw_process=NULL;
    gw_auto_start=false;

    setFrameStyle(QFrame::Box|QFrame::Raised);
    setMidLineWidth(2);

    //
    // Fonts
    //
    QFont bold_font(font().family(),font().pointSize(),QFont::Bold);

    //
    // Dialogs
    //
    gw_server_dialog=new ServerDialog(this);
    gw_codec_dialog=new CodecDialog(this);
    gw_source_dialog=new SourceDialog(this);
    connect(gw_source_dialog,SIGNAL(updated()),this,SLOT(checkArgs()));
    gw_stream_dialog=new StreamDialog(this);
    gw_config_dialog=new ConfigDialog(instance_name,gw_server_dialog,
                                      gw_codec_dialog,gw_stream_dialog,
                                      gw_source_dialog,this);
    connect(gw_server_dialog,SIGNAL(typeChanged(Connector::ServerType,bool)),
            this,SLOT(serverTypeChangedData(Connector::ServerType,bool)));
    connect(gw_server_dialog,SIGNAL(settingsChanged()),this,SLOT(checkArgs()));

    for(int i=0; i<2; i++) {
        gw_meters[i]=new PlayMeter(SegMeter::Right,this);
        gw_meters[i]->setRange(-3000,0);
        gw_meters[i]->setHighThreshold(-800);
        gw_meters[i]->setClipThreshold(-100);
        gw_meters[i]->setMode(SegMeter::Peak);
    }
    gw_meters[0]->setLabel(tr("L"));
    gw_meters[1]->setLabel(tr("R"));


    gw_status_frame_widget=new QLabel(this);
    gw_status_frame_widget->setFrameStyle(QFrame::Box|QFrame::Raised);
    gw_status_widget=new StatusWidget(this);

    gw_name_label=new QLabel(instance_name,this);
    gw_name_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
    gw_name_label->setFont(bold_font);

    gw_message_widget=new MessageWidget(this);
    gw_message_widget->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);

    gw_start_button=new QPushButton(tr("Start"),this);
    gw_start_button->setFont(bold_font);
    connect(gw_start_button,SIGNAL(clicked()),this,SLOT(startEncodingData()));

    gw_config_button=new QPushButton(tr("Settings"),this);
    gw_config_button->setFont(bold_font);
    connect(gw_config_button,SIGNAL(clicked()),this,SLOT(configData()));

    gw_insert_button=new QPushButton(tr("Insert"),this);
    gw_insert_button->setFont(bold_font);
    gw_insert_button->setStyleSheet("background-color: yellow");
    connect(gw_insert_button,SIGNAL(clicked()),this,SLOT(insertData()));
    gw_insert_button->hide();

    gw_remove_button=new QPushButton(tr("Remove"),this);
    gw_remove_button->setFont(bold_font);
    gw_remove_button->setStyleSheet("background-color: violet");
    connect(gw_remove_button,SIGNAL(clicked()),this,SLOT(removeData()));
    gw_remove_button->hide();

    //
    // Kill Timer
    //
    gw_kill_timer=new QTimer(this);
    gw_kill_timer->setSingleShot(true);
    connect(gw_kill_timer,SIGNAL(timeout()),this,SLOT(killData()));
}
Beispiel #7
0
bool MapnikRenderer::Preprocess( IImporter* importer, QString dir )
{
	QString filename = fileInDirectory( dir, "Mapnik Renderer" );

	try {
		IImporter::BoundingBox box;
		if ( !importer->GetBoundingBox( &box ) )
			return false;
		std::vector< IImporter::RoutingEdge > inputEdges;
		std::vector< IImporter::RoutingNode > inputNodes;
		std::vector< IImporter::RoutingNode > inputPaths;
		if ( m_settings.deleteTiles ) {
			if ( !importer->GetRoutingEdges( &inputEdges ) ) {
				qCritical() << "Mapnik Renderer: failed to read routing edges";
				return false;
			}
			if ( !importer->GetRoutingNodes( &inputNodes ) ) {
				qCritical() << "Mapnik Renderer: failed to read routing nodes";
				return false;
			}
			if ( !importer->GetRoutingEdgePaths( &inputPaths ) ) {
				qCritical() << "Mapnik Renderer: failed to read routing paths";
			}
		}

		Timer time;

		mapnik::datasource_cache::instance().register_datasources( m_settings.plugins.toLatin1().constData() );
		QDir fonts( m_settings.fonts );
		mapnik::projection projection( "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +over" );
		mapnik::freetype_engine::register_font( fonts.filePath( "DejaVuSans.ttf" ).toLatin1().constData() );
		mapnik::freetype_engine::register_font( fonts.filePath( "DejaVuSans-Bold.ttf" ).toLatin1().constData() );
		mapnik::freetype_engine::register_font( fonts.filePath( "DejaVuSans-Oblique.ttf" ).toLatin1().constData() );
		mapnik::freetype_engine::register_font( fonts.filePath( "DejaVuSans-BoldOblique.ttf" ).toLatin1().constData() );

		qDebug() << "Mapnik Renderer: initialized mapnik connection:" << time.restart() << "ms";

		int numThreads = omp_get_max_threads();
		qDebug() << "Mapnik Renderer: using" << numThreads << "threads";

		qDebug() << "Mapnik Renderer: x: " << box.min.x << "-" << box.max.x;
		qDebug() << "Mapnik Renderer: y: " << box.min.y << "-" << box.max.y;

		FileStream configData( filename );
		if ( !configData.open( QIODevice::WriteOnly ) )
			return false;

		configData << quint32( m_settings.tileSize ) << quint32( m_settings.zoomLevels.size() );

		long long tilesSkipped = 0;
		long long tiles = 0;
		long long metaTilesRendered = 0;
		long long pngcrushSaved = 0;

		std::vector< ZoomInfo > zoomInfo( m_settings.zoomLevels.size() );
		std::vector< MetaTile > tasks;

		for ( int zoomLevel = 0; zoomLevel < ( int ) m_settings.zoomLevels.size(); zoomLevel++ ) {
			ZoomInfo& info = zoomInfo[zoomLevel];
			int zoom = m_settings.zoomLevels[zoomLevel];

			info.minX = box.min.GetTileX( zoom );
			info.maxX = box.max.GetTileX( zoom ) + 1;
			info.minY = box.min.GetTileY( zoom );
			info.maxY = box.max.GetTileY( zoom ) + 1;

			if ( zoom <= m_settings.fullZoom ) {
				info.minX = info.minY = 0;
				info.maxX = info.maxY = 1 << zoom;
			} else {
				info.minX = std::max( 0 , info.minX - m_settings.tileMargin );
				info.maxX = std::min ( 1 << zoom, info.maxX + m_settings.tileMargin );
				info.minY = std::max( 0, info.minY - m_settings.tileMargin );
				info.maxY = std::min ( 1 << zoom, info.maxY + m_settings.tileMargin );
			}

			tiles += ( info.maxX - info.minX ) * ( info.maxY - info.minY );
			qDebug() << "Mapnik Renderer: [" << zoom << "] x:" << info.minX << "-" << info.maxX << "; y:" << info.minY << "-" << info.maxY;
			configData << quint32( zoom ) << quint32( info.minX ) << quint32( info.maxX ) << quint32( info.minY ) << quint32( info.maxY );

			int numberOfTiles = ( info.maxX - info.minX ) * ( info.maxY - info.minY );
			IndexElement dummyIndex;
			dummyIndex.start = dummyIndex.size = 0;
			info.index.resize( numberOfTiles, dummyIndex );

			std::vector< UnsignedCoordinate > path;
			for ( std::vector< IImporter::RoutingEdge >::const_iterator i = inputEdges.begin(), e = inputEdges.end(); i != e; ++i ) {
				path.push_back( inputNodes[i->source].coordinate );
				for ( int pathID = 0; pathID < i->pathLength; pathID++ )
					path.push_back( inputPaths[pathID + i->pathID].coordinate );
				path.push_back( inputNodes[i->target].coordinate );

				for ( unsigned edge = 0; edge < path.size(); edge++ ) {
					int sourceX = path[edge].GetTileX( zoom );
					int sourceY = path[edge].GetTileY( zoom );
					int targetX = path[edge].GetTileX( zoom );
					int targetY = path[edge].GetTileY( zoom );
					if ( sourceX > targetX )
						std::swap( sourceX, targetX );
					if ( sourceY > targetY )
						std::swap( sourceY, targetY );
					sourceX = std::max( sourceX, info.minX );
					sourceX = std::min( sourceX, info.maxX - 1 );
					sourceY = std::max( sourceY, info.minY );
					sourceY = std::min( sourceY, info.maxY - 1 );
					targetX = std::max( targetX, info.minX );
					targetX = std::min( targetX, info.maxX - 1 );
					targetY = std::max( targetY, info.minY );
					targetY = std::min( targetY, info.maxY - 1 );
					for ( int x = sourceX; x <= targetX; ++x )
						for ( int y = sourceY; y <= targetY; ++y )
							info.index[( x - info.minX ) + ( y - info.minY ) * ( info.maxX - info.minX )].size = 1;
				}

				path.clear();
			}

			info.tilesFile = new QFile( filename + QString( "_%1_tiles" ).arg( zoom ) );
			if ( !openQFile( info.tilesFile, QIODevice::WriteOnly ) )
				return false;

			for ( int x = info.minX; x < info.maxX; x+= m_settings.metaTileSize ) {
				int metaTileSizeX = std::min( m_settings.metaTileSize, info.maxX - x );
				for ( int y = info.minY; y < info.maxY; y+= m_settings.metaTileSize ) {
					int metaTileSizeY = std::min( m_settings.metaTileSize, info.maxY - y );
					MetaTile tile;
					tile.zoom = zoomLevel;
					tile.x = x;
					tile.y = y;
					tile.metaTileSizeX = metaTileSizeX;
					tile.metaTileSizeY = metaTileSizeY;
					tasks.push_back( tile );
				}
			}
		}


#pragma omp parallel
		{
			int threadID = omp_get_thread_num();
			const int metaTileSize = m_settings.metaTileSize * m_settings.tileSize + 2 * m_settings.margin;

			mapnik::Map map;
			mapnik::image_32 image( metaTileSize, metaTileSize );
			QTemporaryFile tempOut;
			QTemporaryFile tempIn;
			mapnik::load_map( map, m_settings.theme.toLocal8Bit().constData() );

#pragma omp for schedule( dynamic )
			for ( int i = 0; i < ( int ) tasks.size(); i++ ) {

				int metaTileSizeX = tasks[i].metaTileSizeX;
				int metaTileSizeY = tasks[i].metaTileSizeY;
				int x = tasks[i].x;
				int y = tasks[i].y;
				int zoomLevel = tasks[i].zoom;
				int zoom = m_settings.zoomLevels[zoomLevel];
				ZoomInfo& info = zoomInfo[zoomLevel];

				map.resize( metaTileSizeX * m_settings.tileSize + 2 * m_settings.margin, metaTileSizeY * m_settings.tileSize + 2 * m_settings.margin );

				ProjectedCoordinate drawTopLeft( x - 1.0 * m_settings.margin / m_settings.tileSize, y - 1.0 * m_settings.margin / m_settings.tileSize, zoom );
				ProjectedCoordinate drawBottomRight( x + metaTileSizeX + 1.0 * m_settings.margin / m_settings.tileSize, y + metaTileSizeY + 1.0 * m_settings.margin / m_settings.tileSize, zoom );
				GPSCoordinate drawTopLeftGPS = drawTopLeft.ToGPSCoordinate();
				GPSCoordinate drawBottomRightGPS = drawBottomRight.ToGPSCoordinate();
				projection.forward( drawTopLeftGPS.longitude, drawBottomRightGPS.latitude );
				projection.forward( drawBottomRightGPS.longitude, drawTopLeftGPS.latitude );
				mapnik::box2d<double> boundingBox( drawTopLeftGPS.longitude, drawTopLeftGPS.latitude, drawBottomRightGPS.longitude, drawBottomRightGPS.latitude );
				map.zoom_to_box( boundingBox );
				mapnik::agg_renderer<mapnik::image_32> renderer( map, image );
				renderer.apply();

				std::string data;
				int skipped = 0;
				int saved = 0;
				for ( int subX = 0; subX < metaTileSizeX; ++subX ) {
					for ( int subY = 0; subY < metaTileSizeY; ++subY ) {
						int indexNumber = ( y + subY - info.minY ) * ( info.maxX - info.minX ) + x + subX - info.minX;
						mapnik::image_view<mapnik::image_data_32> view = image.get_view( subX * m_settings.tileSize + m_settings.margin, subY * m_settings.tileSize + m_settings.margin, m_settings.tileSize, m_settings.tileSize );
						std::string result;
						if ( !m_settings.deleteTiles || info.index[( x + subX - info.minX ) + ( y + subY - info.minY ) * ( info.maxX - info.minX )].size == 1 ) {
							if ( m_settings.reduceColors )
								result = mapnik::save_to_string( view, "png256" );
							else
								result = mapnik::save_to_string( view, "png" );

							if ( m_settings.pngcrush ) {
								tempOut.open();
								tempOut.write( result.data(), result.size() );
								tempOut.flush();
								tempIn.open();
								pclose( popen( ( "pngcrush " + tempOut.fileName() + " " + tempIn.fileName() ).toUtf8().constData(), "r" ) );
								QByteArray buffer = tempIn.readAll();
								tempIn.close();
								tempOut.close();
								if ( buffer.size() != 0 && buffer.size() < ( int ) result.size() ) {
									saved += result.size() - buffer.size();
									result.assign( buffer.constData(), buffer.size() );
								}
							}
						}

						info.index[indexNumber].start = data.size();
						info.index[indexNumber].size = result.size();
						data += result;
					}
				}

				qint64 position;
#pragma omp critical
				{
					position = info.tilesFile->pos();
					info.tilesFile->write( data.data(), data.size() );

					metaTilesRendered++;
					tilesSkipped += skipped;
					pngcrushSaved += saved;
					qDebug() << "Mapnik Renderer: [" << zoom << "], thread" << threadID << ", metatiles:" << metaTilesRendered << "/" << tasks.size();
				}

				for ( int subX = 0; subX < metaTileSizeX; ++subX ) {
					for ( int subY = 0; subY < metaTileSizeY; ++subY ) {
						int indexNumber = ( y + subY - info.minY ) * ( info.maxX - info.minX ) + x + subX - info.minX;
						info.index[indexNumber].start += position;
					}
				}
			}
		}

		for ( int zoomLevel = 0; zoomLevel < ( int ) m_settings.zoomLevels.size(); zoomLevel++ ) {
			const ZoomInfo& info = zoomInfo[zoomLevel];
			int zoom = m_settings.zoomLevels[zoomLevel];
			QFile indexFile( filename + QString( "_%1_index" ).arg( zoom ) );
			if ( !openQFile( &indexFile, QIODevice::WriteOnly ) )
				return false;
			for ( int i = 0; i < ( int ) info.index.size(); i++ ) {
				indexFile.write( ( const char* ) &info.index[i].start, sizeof( info.index[i].start ) );
				indexFile.write( ( const char* ) &info.index[i].size, sizeof( info.index[i].size ) );
			}
			delete info.tilesFile;
		}

		if ( m_settings.deleteTiles )
			qDebug() << "Mapnik Renderer: removed" << tilesSkipped << "tiles";
		if ( m_settings.pngcrush )
			qDebug() << "Mapnik Renderer: PNGcrush saved" << pngcrushSaved / 1024 / 1024 << "MB";

		qDebug() << "Mapnik Renderer: finished:" << time.restart() << "ms";

	} catch ( const mapnik::config_error & ex ) {
		qCritical( "Mapnik Renderer: ### Configuration error: %s", ex.what() );
		return false;
	} catch ( const std::exception & ex ) {
		qCritical( "Mapnik Renderer: ### STD error: %s", ex.what() );
		return false;
	} catch ( ... ) {
		qCritical( "Mapnik Renderer: ### Unknown error" );
		return false;
	}
	return true;
}