[kepler-dev] RExpression image output

Dan Higgins higgins at nceas.ucsb.edu
Sun Dec 16 12:57:05 PST 2007


Hi Tristan,
    Your proposed changes look good to me. So why don't you go ahead and 
make the changes and we can then check to make sure that this doesn't 
cause some unanticipated problems.

Dan

---

Tristan King wrote:
> On Thu, 2007-12-13 at 10:17 -0800, Dan Higgins wrote:
>   
>> Hi Tristan,
>>     You are correct that the output of an added line to create a png 
>> using bitmap will not appear on the graphicsFileName port. But the file 
>> is created with a known filename in the added 'bitmap' statement. So the 
>> 'hack' is to add another output port to the RExpression actor and name 
>> it with a variable whose value is the name of the png file created. You 
>> thus have the filename output but not on the graphicsFileName port.
>>     
>
> Doable, but very hacky :)
>
>   
>>     The problem with this is that it requires a special setup to run 
>> some R code on a server. But the 'bitmap' graphics device won't run on 
>> most desktop machines since most Windows boxes (and Macs) do not have 
>> GhostScript installed. So I am hesitant to change the baseline code to 
>> allow options that won't work on most machines. I don't know if you were 
>> thinking of having some kind of translator that would convert users' 
>> workflows to use graphics devices that your server can handle to create 
>> web pages?
>>     
>
> What i've done with other actors (i.e. Display, the Graphing range,
> ImageJ) is build replacement actors which preform the same task as the
> original actors but store the results in a web accessible form rather
> than displaying the results in a window. To use the replacement actors
> I've built a MoMLFilter which i pass over each workflow before running
> it. This way Users can upload standard workflows without having to build
> them specifically for execution on the web portal (in other words, the
> workflow design and testing phases can still be done purely from
> kepler). So this process has worked fine for the previously mentioned
> actors, mainly because they preform simple tasks and there's not a lot
> of code to change. The RExpression actor on the other hand, is different
> from the previously mentioned actors. It's fire method is approx 280
> lines of code and there is only one line of code that i need to change.
> If i was to create a replacement actor for R, it means a lot of
> duplicate code. Which i'd prefer to avoid, mainly because if the
> behaviour of the actor changes, i'll have to make sure to reflect those
> changes in my replacement actor, which could result in some major
> headaches.
>
>   
>>     One option might be to figure out just which X11 library R is using 
>> to create pngs and install that on your server. This would (presumably) 
>> allow existing RExpression graphics to work on your machine and thus not 
>> require global changes to everyone's code. [I am guessing that you 
>> really don't need all of X11 !]
>>     
>
> I did a bit more digging, and found that R draws the image in X and
> grabs it from there. They use X mainly because they don't have access to
> any fonts on unix except for thru X (or ghostscript of course). Just
> installing the X font libraries doesn't work, they actually use the X
> display.
>
> So i fired up Xvfb, which starts a virtual frame buffer, and with that,
> the png() function works from the server.
>
> This is ok, but running an X server just for the small cpu time that R
> needs it for means extra memory being used up when it's not needed. So I
> would still prefer to use the Ghostscript dependent commands.
>
> So, my next suggestion:
> change the else statment starting line 330 to:
>
> } else {
>  graphicsDevice = "bitmap(file = '" + graphicsOutputFile + "', type =
> \"png256\", width = " + nxd + ", height = " + nyd + ")";
>  if (!GraphicsEnvironment.isHeadless()) {
>   try {
>    GraphicsEnvironment.getLocalGraphicsEnvironment();
>    graphicsDevice = "png(filename = '"+ graphicsOutputFile + "'"+",width
> = "+nxs+", height = "+nys+", pointsize = 12, bg = 'white')";
>   } catch (InternalError e) {
>    System.out.println("WARNING: " + e.getLocalizedMessage());
>   }
>  }
> }
>
> where GraphicsEnviroment is java.awt.GraphicsEnvironment.
>
> In this case, it will still use png() when running from the kepler gui,
> and whenever it has access to an X server (or windows), but if there is
> no display head, it will attempt to fall back to the ghostscript
> function bitmap().
>
> The try-catch is used because isHeadless just depends on whether the
> $DISPLAY env variable is set, and doesn't check if it's valid.
> getLocalGraphicsEnvironment attempts to connect to the X server, and
> throws an Error if it can't. I've been told that catching Error's is bad
> practice, but i can't see any harm in this case (we could just stick to
> using only isHeadless, but I like to make things as 'idiot' proof as
> possible).
>
> I think this is a reasonable change, because in the case that a display
> is present, the actor will function in exactly the same as before, but
> if there is no display available, rather than failing straight away, it
> will attempt to fall back onto ghostscript, which of course will fail if
> ghostscript isn't available, but it would have failed anyways since
> there's no display.
>
> Let me know what you think, and if there are no arguments against this
> change, then I will commit it to the cvs tree (along with reasonable
> commenting to explain exactly what's going on, incase it does break
> things further down the track).
>
> Cheers
> -Tristan
>
>
>
>   



More information about the Kepler-dev mailing list