Wolfram Computation Meets Knowledge

Making of the “Facts of the Moment” Plaque for the David Cameron Visit

Several people have asked me to write about the virtual plaque that we made for the official opening of the Wolfram Research Europe office by UK Prime Minister David Cameron.

The concept that came out of the short brainstorming meeting was to have a button on an iPad that would trigger a video on our display board, leading to an image showing facts about the world at the moment of revelation.

David Cameron Plaque

This is the story of how we made it happen.

I start with a disclaimer—this is not an example of elegant coding or optimal use of Mathematica, but is a real-life story of implementing an under-specified idea (subject to feature creep) quickly and with no regard for future maintenance—in short, like a lot of software projects.

Jeremy Davis, Wolfram’s Design Director, quickly produced a mockup in Photoshop of what it might look like while I set about building the data extraction code.

I knew that pulling data from Wolfram|Alpha and visualizing it would be easy, but what occupied my thoughts were the potential network failures, Wolfram|Alpha failures, local CPU overloads, or other horrors that could transpire to embarrass me in front of the PM!

So I started with a function that would query Wolfram|Alpha with a failure mode that would return a previous query result if no valid answer came within 20 seconds:

safeWAGet[expr___] := Block[{rawdata = TimeConstrained[WolframAlpha[expr], 20, $Failed]}, If[rawdata === $Failed || Head[rawdata] === Missing || rawdata === {}, rawdata = $LastGood[expr], $LastGood[expr] = rawdata]; rawdata]; SetAttributes[safeWAGet, HoldFirst];

Likewise, here is a version of First, which won’t complain if there is no first element, but will instead return something invisible.

safeFirst[expr_] := If[Length[expr] === 0, "", First[expr]]

Anticipating that the design and color scheme would change repeatedly (though in the end it never did), I separated out the style choices:

backgroundColor = RGBColor[0.3333333, 0.4588235294, 0.611764]; textColor = RGBColor[0.9, 1, 1]; fontOpts =  Apply[Sequence, {FontColor -> textColor, Background -> backgroundColor, FontSize -> 14, FontFamily -> "Arial"}];

To get the right arguments for the WolframAlpha function, the easiest way is to do a full linguistic query from the notebook by entering == followed by the query. If you then click in the pod corner and select either “Subpod content” or “Computable data” from the popup menu, you get the API code generated for you automatically.

For example, the parameters for our current local weather are "weather oxford",{{"InstantaneousWeather:WeatherData",1},"ComputableData"}. With some styling and text substitution, I end up with this code for generating the final image pod:

weatherPodFinal[] := ImageCrop[Block[{temp, cover, humid, speed, dir}, {temp, cover, humid, speed} = safeWAGet["weather oxford", {{"InstantaneousWeather:WeatherData", 1}, "ComputableData"}][[1, All, 2]]; dir = StringReplace[safeWAGet["wind direction oxford", {{"Result", 1}, "Plaintext"}], {x__ ~~ "° " -> "", " (" ~~ ___ -> ""}]; Graphics[{Text[Style[Row[{Round[5/9 (temp - 32)], "°C", Style["  |  ", RGBColor[0.62, 0.75, 0.89]], cover, Style["  |  ", RGBColor[0.62, 0.75, 0.89]], "wind: ", dir, " at ", speed, "mph", Style["  |  ", RGBColor[0.62, 0.75, 0.89]], "humidity:", humid, "%"}], fontOpts], {0, 0}]}, Background -> backgroundColor]]];

weatherPodFinal[]

18° | clear | wind: E at 6.mph | humidity:28%

The ImageCrop part I will explain in a minute.

Images are a little trickier, as Wolfram|Alpha returns the Computable Document Format (CDF) structure of the image rather than the semantic structure. So I first used ToExpression to turn it into a meaningful Graphics expression, and then used replacement rules to swap out colors and fonts from the Wolfram|Alpha defaults to Jeremy’s design. I made a function for this:

processPod[pod_, opts___] := Show[ToExpression[pod] /. {RGBColor[___] -> textColor, x_Polygon -> {backgroundColor, x}, x_Line -> {textColor, x}, Inset[str_, coords_, opts2___] -> Inset[Style[str, textColor, Background -> backgroundColor], coords, opts2, Background -> backgroundColor]} /. Style[txt_String, rest___] -> Style[txt, FontColor -> textColor,  Background -> backgroundColor], Background -> backgroundColor,  opts];

Here is the skyPod using that (as it appears today):

skyPodFinal[] := processPod[safeWAGet["star chart witney", {{"SkyMap:AstronomicalData", 1}, "Content"}][[1]], ImageSize -> 344];

skyPodFinal[]

Final skyPod result

The other pods were variations on these and can be downloaded at the bottom of this post. Happy with this, I sent the code off to Jeremy with a rough Grid structure to put them all together and went home.

Jeremy works in a different time zone and so had final formatted versions ready for me when I came in the next day. But he obviously thinks differently from me, as he took my symbolic graphics expressions, turned them into bitmap images, and used Mathematica‘s image-processing commands to size them and assemble them into a Photoshop-generated background image (this is where the ImageCrop I mentioned earlier came from). This is not how I would have done it, but there was no time to argue about programming style!

Now it was time to address the first real problem. All these web queries (over a UK ISP) meant that it took sometimes over 20 seconds to get all the components of the final image. Far too long to keep the Prime Minister waiting. So I split the assembly into three parts: pods that don’t change much (such as the planet locations), pods that change sometimes (such as the star chart, which changes about once per minute), and pods that change all the time (such as share prices). I also created an error-resistant version of ImageCompose that wouldn’t fail if one of the images turned out not to be an image.

imageComposeSafe[expr1_Image, expr2_Image, more___] := ImageCompose[expr1, expr2, more]; imageComposeSafe[expr1_, expr2_Image, more___] := expr2; imageComposeSafe[expr1_Image, expr2_, more___] := expr1;

Here is the modified version of his code for the slow refresh (most of the code is position and size information):

Code for the slow refresh elements

And then the fast refresh elements added in:

buildFastImage[bimg_Image] := Image[imageComposeSafe[bimg, ftsePodFinal[], {0, 0}, {-22, -30}], ImageSize -> 944];

And finally, the parts that absolutely had to be real-time:

buildFinalImage[bimg_Image] := buildFinalImage[bimg, AbsoluteTime[]]

Adding in final elements that need to be real-time

Having done all this, I formatted it as a package to be loaded with Needs["Plaque`"].

You may have noticed that we faked Cameron’s age. Wolfram|Alpha does compute that data, but that idea was added to the plaque at the very last moment, so there was no time to think about computing it.

The idea of splitting the code was to run the low-refresh parts only every 60 seconds and the medium-refresh parts every 10 seconds and write the latest version to disk. I did that by using CreateScheduledTask. And to avoid the risk of a scheduled task causing the kernel to run slowly when it was called on, I put that into a second compute kernel using ParallelEvaluate.

ParallelEvaluate[Needs["Plaque`"]; $BackgroundImage = buildSlowImage[]; StartScheduledTask[CreateScheduledTask[$BackgroundImage = buildSlowImage[], 60]]; StartScheduledTask[CreateScheduledTask[Export["ComponentTemp.jpg", buildFastImage[$BackgroundImage], "JPEG", "CompressionLevel" -> 0]; DeleteFile["Component.jpg"]; RenameFile["ComponentTemp.jpg", "Component.jpg"], 10]], 1];

That left my control kernel free to generate the final version and write that to disk every two seconds. It also maintained an archive, so I could retrieve the actual displayed image later.

i = 1; While[True, Export["FactsOfTheMomentTemp.jpg", buildFinalImage[Import["Component.jpg"], AbsoluteTime[]], "JPEG", "CompressionLevel" -> 0]; CopyFile["FactsOfTheMomentTemp.jpg", "Archive\\" <> ToString[i++] <> ".jpg"]; DeleteFile["image1.jpg"]; RenameFile["FactsOfTheMomentTemp.jpg", "image1.jpg"];  Pause[2];]

The use of RenameFile was to reduce the chance that we tried to access the latest image while it was being written. Renaming is much faster than writing.

So now, thanks to this chain of web service calls to Wolfram|Alpha, symbolic transformation, image processing, and scheduled tasks, the plaque image is rewritten to disk every two seconds, with no part of it more than 60 seconds old.

The rest was done outside of Mathematica, as we had a Flash video that we wanted to play, and Mathematica can’t embed Flash. In the end we dropped almost all of the video and could have used a CDF for the button, but the sequence was built by then. The iPad was linked by a horrible hack of remote desktoping to a PC that was in, turn, screen sharing another PC that was driving the display. An HTML button on the final machine called the Flash script, which played the movie and loaded the most recently generated Mathematica image to display when the movie finished.

And here was the moment of truth: I was standing behind the photographer and holding my breath, hoping I had considered and trapped all possible failures:

United Kingdom Prime Minister David Cameron and Conrad Wolfram

Download this post with expanded code as a Computable Document Format (CDF) file.

Comments

Join the discussion

!Please enter your comment (at least 5 characters).

!Please enter your name.

!Please enter a valid email address.

1 comment

  1. I love this line:

    “I start with a disclaimer—this is not an example of elegant coding or optimal use”

    Yes, welcome to the “desert of the real”. One would love to have programs that are examples of mathematical beauty.

    The reality however is other than perhaps say a trivial Fibonacci function, its all pretty much “gritty” code.

    I’m not suggesting this is a bad thing, its the nature of “real world programming” ™

    Other than that, excellent work guys! Very interesting read indeed.

    Reply