An Interactive Sky Chart in Pure HTML/CSS
I’ve embarked on a project to build a series of web applications for the Arlington Planetarium website that illustrate and teach concepts in astronomy and related sciences. This article describes the first of these, an interactive sky map. I’ve aimed this writeup at web designers and programmers who might be interested in the techniques I used to produce a sky map that’s responsive, fast-loading, and has a fair amount of interactivity, using purely HTML5 and css3 with no javascript.
Perhaps I should mention that a sky map is a chart designed to help you figure out where to look to find stars, planets, constellations, and other inhabitants of the, usually, night sky. As the Earth turns, the apparent positions of all these objects changes from hour to hour; they change as well, although more gradually, as the Earth revolves around the sun along with the other planets, and as the Moon revolves around the Earth. The chart is also dependent on where you are on the Earth. In additional to all of this, there are transient objects such as asteroids and comets, which should also be charted, and the effects of light pollution, which should be taken into account to limit the amount of information on the map, so that it does not too unrealistically exceed what we can be expected to see from our semi-urban vantage point.
I feel I should also apologize somewhat for my use of the term “interactive.” This word has certainly become far too common! What’s not interactive on the web? Maybe a plain text file with no hyperlinks. But if this word still means anything, I think it refers to something that you can manipulate, change, and put in different states. A bunch of pages with hyperlinks is not quite interactive, because you’re not manipulating a thing, but rather jumping between different things. A page with a table that you can sort by dragging its entries around is getting interactive.
The goal of my sky chart is to provide a place where the backyard astronomer in our target area can go to get a relevant map of the current sky: the stars right now. And that’s what you initially see when you go to the url. But you’re often not concerned with the sky right now, but maybe in a few hours, when it’s dark and your telescope is set up. Or you want to move the map forward in time to see when you’ll be able to get a view of Saturn. And you might want to overlay a grid or diagram of the constellations on the map, to help you locate these celestial objects.
If you’re a serious observer you probably have software that does all this for you and that you’re as intimate with as the controls of your car. But if you’re a more casual sky gazer you probably don’t; or perhaps you are outside with an iPad and just need a quick reference to the sky. This sky map is made for you.
Why no javascript? I have nothing against it, and expect to use it1 liberally in subsequent projects for the Planetarium. But I decided to see if I could acheive the requisite level of interactivity for the sky map without it, both as a challenge, and for the usual reasons of accessibility. Using javascript excludes the small but significant number of people who have decided to turn it off or whose devices happen not to support it, or support it well enough. But the main reason is an idea of economy that urges us to resist incorporating an entire layer of technology unless it is actually required. Since I found that I could build the tool that I wanted to without it, I didn’t use it. Also, on many, especially mobile, devices css solutions tend to be faster and smoother than javascript.
In place of javascript I have relied on a few simple techniques
made possible by recent additions to the Cascading Syle Sheet standards. I invented none of these css3 tricks myself, but am merely applying and combining ideas that I’ve found elsewhere, usually to implement fancy menus. Part of my motivation was to see how far I could push these techniques in the service of a different type of interactivity.
The general outline of my approach is to generate a large number of images, namely the basic star charts at different times, and some informational overlays for each chart, offline, one time per day. The sky map application is essentially a special-purpose viewer for all of these images that is assembled from HTML and css.
The sky maps themselves are created with Stellarium, the excellent open-source astronomy program. There are packages for Stellarium on some Linux distributions, and compiled versions are available for other operating systems as well. Stellarium can be scripted with what essentially is javascript calling functions from a pretty extensive API. The script that calculates and saves all the images is here. If you look at it you may notice that I do some slightly odd things, but scripting support in Stellarium is still a bit rough, and there are some bugs to work around.
The Stellarium script is run once each morning with a cron job that starts it with the command
xvfb-run
is a facility for running programs that want to run in an X-window in circumstances, such as a cron job, where you might not actually want or need for them to write to a display.
The –fov
selects the field of view of the map; this is something that of course I would prefer to put in the script, but, although there is a call for it in the API, it seemed not to work.
In the script there are calls to methods in the core
, GridLinesMgr
, and ConstellationMgr
namespaces, all of which control various aspects of the Stellarium display. Other functions in the script, without these prefixes, work as in familiar Javascript code.
I’m not going to go through this script in detail, as this is an article about the web interface for viewing the results, rather than about scripting Stellarium. Briefly, however, the script creates a skymap for each of 24 hours, covering the span from six am local time in the current day to five am the next morning. For each of these times we save the basic star chart with little adornment, plus three additional versions: one with an azimuthal grid, one with constellation outlines and labels, and one with “constellation art,” which overlays pictures that purport to show the origins of the ancient, fanciful names applied to these apparently random groups of stars. The simple script is fairly self-explanatory. The frequent calls to core.wait
seem to be required to allow Stellarium to catch up before saving a screenshot; I found that without these I would sometimes get screenshots showing incomplete drawing of the maps. Nevertheless, I still see some problems in the orientation of the charts, which are sometimes not at zero azimuth despite the call to core.moveToAltAzi(90, 0)
, as you may notice in the results.
I want the entire set of pictures to be loaded when the application is first opened, so the user can navigate freely through time and apply adornments at will without having to wait for additional graphics to download. Each image generated by Stellarium is roughly 500 kilobytes. For each time period (hour) there are eight possible configurations for the chart, as there are three possible overlays that can be applied. This means that the entire set, covering 24 hours, will be roughly 96 megabytes. We need to figure out how to make this smaller.
The first thing to notice is that there is a great deal of redundancy in the image set. Each of the eight images contains the same star field, and consists of different combinations of overlays on top of that. If we could somehow store and transmit the differences between images and eliminate repeating the pixels that they had in common, that would be a significant savings. We can, in fact, do this, with the help of the fantastically useful ImageMagick suite of image processing routines. Once ImageMagick is installed, the command (for example)
will calculate the difference between base.png
and grid.png
and save the result as +grid.png
. In our application base.png
is the starfield without any overlays and grid.png
is the starfield with an azimuthal grid overlay. The result of this command is to create a new file, +grid.png
, which consists of only the grid; everything else is transparent.
The above command, as well as two more for the constellation outlines and the constellation art, are included in a bash script that is run after each Stellarium run. This script applies a few additional transformations to the Stellarium images to save some more space: the base image is converted from a PNG to a jpeg and saved with moderate quality, and all of the overlays created with the composite
command described above are processed with the program, an image “quantizer,” which saves them with a reduced color palette. After processing each hour’s image set takes up about 200K, or about 4.8 megabytes for the entire 24-hour set. This is still a pretty hefty download, but it’s not unreasonable, and much more manageable than the original 96 MB. 2
The ImageMagick composite
command calculates and stores the deltas between the base star map and the map with each of three overlays. The star chart application then applies these deltas, when requested by the user, by superimposing the desired overlays on top of the star field. This is possible because of browser support for transparent PNGs; the application will have limited usefulness in browsers that lack this support, mainly older versions of Internet Explorer.
If you interact with the application (after giving it a minute to load) you will discover its simple operation: you can hover the mouse pointer over the yellow column of hours on the right, and should see the stars rotate, the sun rise and set, etc., as fast as you can scrub over the hours. If you click on a time the map for that hour loads, and that’s the map you see when you move the pointer elsewhere. Finally, you can click on the bottom buttons to place overlays on the map.
After the map loads, you can open a window on the source of the iframe to get an overview of the code, which is a single page of HTML with an embedded stylesheet.
In order to achieve instant scrolling through time we preload all of the base star images; this is the purpose of the div
with id = “preload”
at the start of the body
, and the style rule
This loads all the images in the document, but positions them out of the viewport. They are slid into position when you hover over the hour with the style rule
The hover rules are wrapped in a media query that excludes small screens that are more likely to be touch devices.
Now, perhaps after locating the time of interest by hovering over the hour indicators, you can click on an hour and the star map for that time will load without delay. You can click on the bottom buttons to overlay the chart with with any combination of gridlines, constellation markers, and constellation art. The overlays appear using a brief fade-in animation, and in the fashion of a “single-page application,” with no reloading of the page.
This is accomplished by preloading and positioning all of the graphic elements at the start, but giving them an opacity of 0, so that they are all invisible and transparent. In the html all of these assets are contained within divs that are inside the skymap
div. The css rule
makes all these divs invisible. The transition
rules ensure that, when the opacity is eventually changed, it will do so with a slight fading effect, rather than abruptly.
When you click on a button you are invoking a hyperlink with a url consisting entirely of a fragment identifier, for example <a href=“#bgc” [etc.]
. This makes the element with id = “bgc”
the target of the url. This allows us to exploit the css target pseudoclass: the magic css feature that lets us put a web page into different states.
All we need to say in the stylesheet is
This will cause the div targeted by the id
in the url to become visible.
Since there are eight possible states the map can be in for any chosen time, we have eight corresponding divs in the markup, all nestled within the skymap
div. These divs contain the overlays invoked by the buttons, and also load the button set appropriate for the current state.
These techniques can be used to construct any single-page web application that consists of a reasonable, finite set of states, as long as the states can be precomputed. This excludes anything that needs to do actual computation. But if the application fits this description, then css3 provides, through its target
pseudoclass, a way to respond to user action by switching state; and, through css animations, the state changes can be made more visually meaningful for the user.
Although this application is fairly simple, coding the repetitive markup required was tedious and error-prone. There is no reason these techniques can not be used to build more complex applications, but in that case I would suggest writing a program to generate the bulk of the html for you.
or a language that compiles to it↩︎
In fact, the space situation is a bit more complicated than what I’ve described here, because only the base starmaps, for all hours, plus overlays for the current hour, are downloaded initially; but I’ve tried to provide the flavor of how we saved space by using transparency.↩︎