//Copyright 2017 Ryan Wick
//This file is part of Bandage
//Bandage is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//Bandage 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 General Public License for more details.
//You should have received a copy of the GNU General Public License
//along with Bandage. If not, see .
#include "image.h"
#include "commoncommandlinefunctions.h"
#include "../program/globals.h"
#include "../ui/mygraphicsscene.h"
#include "../ui/mygraphicsview.h"
#include "../graph/assemblygraph.h"
#include
#include "../program/settings.h"
#include
#include
#include
#include "../blast/blastsearch.h"
int bandageImage(QStringList arguments)
{
QTextStream out(stdout);
QTextStream err(stderr);
if (checkForHelp(arguments))
{
printImageUsage(&out, false);
return 0;
}
if (checkForHelpAll(arguments))
{
printImageUsage(&out, true);
return 0;
}
if (arguments.size() < 2)
{
printImageUsage(&err, false);
return 1;
}
QString graphFilename = arguments.at(0);
arguments.pop_front();
if (!checkIfFileExists(graphFilename))
{
outputText("Bandage error: " + graphFilename + " does not exist", &err);
return 1;
}
QString imageSaveFilename = arguments.at(0);
arguments.pop_front();
QString imageFileExtension = imageSaveFilename.right(4);
bool pixelImage;
if (imageFileExtension == ".png" || imageFileExtension == ".jpg")
pixelImage = true;
else if (imageFileExtension == ".svg")
pixelImage = false;
else
{
outputText("Bandage error: the output filename must end in .png, .jpg or .svg", &err);
return 1;
}
QString error = checkForInvalidImageOptions(arguments);
if (error.length() > 0)
{
outputText("Bandage error: " + error, &err);
return 1;
}
bool loadSuccess = g_assemblyGraph->loadGraphFromFile(graphFilename);
if (!loadSuccess)
{
outputText("Bandage error: could not load " + graphFilename, &err);
return 1;
}
int width = 0;
int height = 0;
//Since frame rate performance doesn't matter for a fixed image, set the
//default node outline to a nonzero value.
g_settings->outlineThickness = 0.3;
parseImageOptions(arguments, &width, &height);
//For Bandage image, it is necessary to position node labels at the
//centre of the node, not the visible centre(s). This is because there
//is no viewport.
g_settings->positionTextNodeCentre = true;
//The zoom level needs to be set so rainbow-style BLAST hits are rendered
//properly.
g_absoluteZoom = 10.0;
bool blastUsed = isOptionPresent("--query", &arguments);
if (blastUsed)
{
if (!createBlastTempDirectory())
{
err << "Error creating temporary directory for BLAST files" << Qt::endl;
return 1;
}
QString blastError = g_blastSearch->doAutoBlastSearch();
if (blastError != "")
{
err << blastError << Qt::endl;
return 1;
}
}
QString errorTitle;
QString errorMessage;
std::vector startingNodes = g_assemblyGraph->getStartingNodes(&errorTitle, &errorMessage,
g_settings->doubleMode,
g_settings->startingNodes,
"all");
QString errormsg;
QStringList columns;
bool coloursLoaded = false;
QString csvPath = parseColorsOption(arguments);
if (csvPath != "")
{
if(!g_assemblyGraph->loadCSV(csvPath, &columns, &errormsg, &coloursLoaded))
{
err << errormsg << Qt::endl;
return 1;
}
if(coloursLoaded == false)
{
err << csvPath << " didn't contain color" << Qt::endl;
return 1;
}
g_settings->nodeColourScheme = CUSTOM_COLOURS;
}
if (errorMessage != "")
{
err << errorMessage << Qt::endl;
return 1;
}
g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance);
g_assemblyGraph->layoutGraph();
MyGraphicsScene scene;
g_assemblyGraph->addGraphicsItemsToScene(&scene);
scene.setSceneRectangle();
double sceneRectAspectRatio = scene.sceneRect().width() / scene.sceneRect().height();
//Determine image size
//If neither height nor width set, use a default of height = 1000.
if (height == 0 && width == 0)
height = 1000;
//If only height or width is set, scale the other to fit.
if (height > 0 && width == 0)
width = height * sceneRectAspectRatio;
else if (height == 0 && width > 0)
height = width / sceneRectAspectRatio;
bool success = true;
QPainter painter;
if (pixelImage)
{
QImage image(width, height, QImage::Format_ARGB32);
image.fill(Qt::white);
painter.begin(&image);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
scene.render(&painter);
success = image.save(imageSaveFilename);
painter.end();
}
else //SVG
{
QSvgGenerator generator;
generator.setFileName(imageSaveFilename);
generator.setSize(QSize(width, height));
generator.setViewBox(QRect(0, 0, width, height));
painter.begin(&generator);
painter.fillRect(0, 0, width, height, Qt::white);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
scene.render(&painter);
painter.end();
}
int returnCode;
if (!success)
{
out << "There was an error writing the image to file." << Qt::endl;
returnCode = 1;
}
else
returnCode = 0;
if (blastUsed)
deleteBlastTempDirectory();
return returnCode;
}
void printImageUsage(QTextStream * out, bool all)
{
QStringList text;
text << "Bandage image will generate an image file of the graph visualisation without opening the GUI.";
text << "";
text << "Usage: Bandage image [options]";
text << "";
text << "Positional parameters:";
text << " A graph file of any type supported by Bandage";
text << " The image file to be created (must end in '.jpg', '.png' or '.svg')";
text << "";
text << "Options: --height Image height (default: 1000)";
text << "--width Image width (default: not set)";
text << "--color csv file with 2 column first the node name second the node color";
text << "";
text << "If only height or width is set, the other will be determined automatically. If both are set, the image will be exactly that size.";
text << "";
getCommonHelp(&text);
if (all)
getSettingsUsage(&text);
getOnlineHelpMessage(&text);
outputText(text, out);
}
QString checkForInvalidImageOptions(QStringList arguments)
{
QString error = checkOptionForInt("--height", &arguments, IntSetting(0, 1, 32767), false);
if (error.length() > 0) return error;
error = checkOptionForInt("--width", &arguments, IntSetting(0, 1, 32767), false);
if (error.length() > 0) return error;
error = checkOptionForString("--colors", &arguments, QStringList(), "a path of csv file");
if (error.length() > 0) return error;
return checkForInvalidOrExcessSettings(&arguments);
}
//This function parses the command line options. It assumes that the options
//have already been checked for correctness.
void parseImageOptions(QStringList arguments, int * width, int * height)
{
if (isOptionPresent("--height", &arguments))
*height = getIntOption("--height", &arguments);
if (isOptionPresent("--width", &arguments))
*width = getIntOption("--width", &arguments);
parseSettings(arguments);
}
//This function parses the command line options. It assumes that the options
//have already been checked for correctness.
QString parseColorsOption(QStringList arguments)
{
QString path = "";
if (isOptionPresent("--colors", &arguments))
path = getStringOption("--colors", &arguments);
parseSettings(arguments);
return path;
}