Thursday, May 2, 2019

Space Observatories Update 2019-05

The Lomonossow and Spektr-R missions sadly have stopped working. Some new missions: SMILE (Solar wind Magnetosphere Ionosphere Link Explorer) and SPHEREx (Spectro-Photometer for the History of the Universe).

The source data are now available on my space exploration history GitHub repository, together with the included infographics.

Tuesday, April 30, 2019

Solar System Missions Update 05/2019

Here's my map of all active and future Solar System Missions as of May 1st 2019.

The Chang'e 4 mission continues on the lunar far side, Hayabusa 2 will check out the crater it made last month with the SCI Impactor at Asteroid Ryugu. ISRO has delayed the launch of Chandrayaan 2 Moon mission to July.

Another exciting announcement: The Chinese asteroid mission, as it was called on this graphic, now has the official name Zheng He (鄭和) after the famouse Admiral. It will return a sample from the near-earth asteroid/quasi-moon 469219 Kamo`oalewa, and continue on to a rendezvous with the main belt comet 133P/Elst-Pizarro

Data, images and documentation are available on my space exploration history GitHub repository and the associated website.

Sunday, March 31, 2019

Solar System Missions Update 04/2019

Here's my map of all active and future Solar System Missions as of April 1st 2019.

The Chang'e 4 mission continues on the lunar far side, in the first week of April Hayabusa 2 will launch an Impactor at Asteroid Ryugu, on the 11th the Beresheet mission will attempt a lunar landing, while ISRO also has announced to launch the Chandrayaan 2 Moon mission this month.

Data, images and documentation are available on my space exploration history GitHub repository and the associated website.

Sunday, March 3, 2019

How to Put Point Source Data on the Celestial Map

I have added another example how to add your own data to my interactive celestial map. Specifically, how to display point data at their proper position with self-defined symbols. This builds on the previous example How To Put Your Own Data on the Celestial Map It'll look like this:

First we need to add the proper libraries and stylesheet:

<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="https://d3js.org/d3.geo.projection.v0.min.js"></script>
<script type="text/javascript" src="https://ofrohn.github.io/celestial.min.js"></script>
<link rel="stylesheet" href="https://ofrohn.github.io/celestial.css">

First we have to define the objects as valid geoJSON data again, as described in the readme of the data folder of the project on GitHub. Since we're dealing with point sources, the definition is quite simple, the geometry only needs single points. If distinct point sizes are desired, a size criterion in the properties section is required, like the magnitude or extension of each object, and also a name if you want to label the objects on the map. This example uses supernova remnants filtered from the main deep space objects data file that comes with d3-celestial, but you can define your own data as below:

var jsonSN = {
  "type":"FeatureCollection",
  // this is an array, add as many objects as you want
  "features":[
    {"type":"Feature",
     "id":"SomeDesignator",
     "properties": {
       // Name
       "name":"Some Name",
       // magnitude, dimension in arcseconds or any other size criterion
       "mag": 10,
       "dim": 30
     }, "geometry":{
       // the location of the object as a [ra, dec] array in degrees [-180..180, -90..90]
       "type":"Point",
       "coordinates": [-80.7653, 38.7837]
     }
    }  
  ]};

Next we define the appearance of the objects and labels as they will appear on the map. The values are equivalent to CSS-formats. Fill and stroke colors are only necessary if the objects should appear solid (fill) or as an outline (stroke), or an outline with a semitransparent filling as below. Width gives the line width for outlines.

var pointStyle = { 
      stroke: "#f0f", 
      width: 3,
      fill: "rgba(255, 204, 255, 0.4)"
    };
var textStyle = { 
      fill:"#f0f", 
      font: "bold 15px Helvetica, Arial, sans-serif", 
      align: "left", 
      baseline: "top" 
    };

Now we are ready to add the functions that do the real work of putting the data on the map.

Celestial.add({file:string, type:json|raw, callback:function, redraw:function)

The file argument is optional for providing an external geoJSON file; since we already defined our data, we don't need it. Type is 'line', that leaves two function definitions: the first one is called at loading, this is where we add our data to the d3-celestial data container, while the second function 'redraw* is called at every redraw event for the map. So here you need to define how to display the added object(s). Here are two different possibilities to add data to the D3 data container. Either add the data defined as a JSON-Object in-page, as below with the jsonSN object we defined before.

callback: function(error, json) {
  if (error) return console.warn(error);
  // Load the geoJSON file and transform to correct coordinate system, if necessary
  var dsn = Celestial.getData(jsonSN, config.transform);

  // Add to celestial objects container in d3
  Celestial.container.selectAll(".snrs")
    .data(asterism.features)
    .enter().append("path")
    .attr("class", "snr"); 
  // Trigger redraw to display changes
  Celestial.redraw();
}

Or add data from an external file with optional filtering, as shown below. In this case the file parameter of the Celsestial.add() function needs to give a valid url to the data file, while the filter function returns true for every object that meets the intended criteria.

callback: function(error, json) {
  if (error) return console.warn(error);
  // Load the geoJSON file and transform to correct coordinate system, if necessary
  var dsos = Celestial.getData(json, config.transform);

  // Filter SNRs and add to celestial objects container in d3
  Celestial.container.selectAll(".snrs")
    .data(dsos.features.filter(function(d) {
      return d.properties.type === 'snr'
    }))
    .enter().append("path")
    .attr("class", "snr"); 
  // Trigger redraw to display changes
  Celestial.redraw();
}

However you add the data, as long as they receive the same class name - 'snr' in the examples above - the display method is the same, as shown below. With point data we can't rely on the map function to do all the work, we need to paint on the canvas step by step. First, check if the point is currently displayed (clip), then get the location (mapProjection), size (whatever scaling formula you like) and styling.

Now we are ready to throw pixels at the canvas: set the styles (fill color, stroke color & width), followed by whatever canvas commands are required to draw the object shape, here a filled circle outline. And then the same for the adjacent object name offset by the previously calculated radius.

redraw: function() {
  // Select the added objects by class name as given previously
  Celestial.container.selectAll(".snr").each(function(d) {
    // If point is visible (this doesn't work automatically for points)
    if (Celestial.clip(d.geometry.coordinates)) {
      // get point coordinates
      var pt = Celestial.mapProjection(d.geometry.coordinates);
      // object radius in pixel, could be varable depending on e.g. dimension or magnitude 
      var r = Math.pow(20 - prop.mag, 0.7); // replace 20 with dimmest magnitude in the data
   
      // draw on canvas
      //  Set object styles fill color, line color & width etc.
      Celestial.setStyle(pointStyle);
      // Start the drawing path
      Celestial.context.beginPath();
      // Thats a circle in html5 canvas
      Celestial.context.arc(pt[0], pt[1], r, 0, 2 * Math.PI);
      // Finish the drawing path
      Celestial.context.closePath();
      // Draw a line along the path with the prevoiusly set stroke color and line width      
      Celestial.context.stroke();
      // Fill the object path with the prevoiusly set fill color
      Celestial.context.fill();     

      // Set text styles       
      Celestial.setTextStyle(textStyle);
      // and draw text on canvas
      Celestial.context.fillText(d.properties.name, pt[0] + r - 1, pt[1]- r + 1);
    }      
  }); 
}});

Finally, the whole map can be displayed.

Celestial.display();

Bonus: Avoid overlapping labels

You will note that there is a lot of overlap between distinct labels. Fortunately, d3 already has a solution for this: d3.geom.quadtree, which builds a hierarchical data structure ordered by proximity. First we set the closest allowed distance between two labels in pixels, get the map dimensions from Celestial.metrics, and create a quadtree object with the extent of those dimensions.

var PROXIMITY_LIMIT = 20,    // Closest distance between labels
    m = Celestial.metrics(), // Get the current map size in pixels
    // empty quadtree, will be used for proximity check
    quadtree = d3.geom.quadtree().extent([[-1, -1], [m.width + 1, m. height + 1]])([]);

After proceeding as above - get the projected map position in pixelspace (pt) and draw the snr symbol - we use the quadtree.find() function to find the nearest neighbor relative to this position, and if it is more distant than our limit above, add it to quadtree and draw the label, otherwise don't.

// Find nearest neighbor
var nearest = quadtree.find(pt);

// If neigbor exists, check distance limit
if (!nearest || distance(nearest, pt) > PROXIMITY_LIMIT) { 
  // Nothing too close, add it and go on
  quadtree.add(pt)
  // draw the label
}

Now we need just one more thing, the distance function used above, which is the standard Pythagorean square root of the sum of the differences squared function.

// Simple point distance function
function distance(p1, p2) {
  var d1 = p2[0] - p1[0],
      d2 = p2[1] - p1[1];
  return Math.sqrt(d1 * d1 + d2 * d2);
}

This will only draw non-overlapping labels and scales with zoom-level, since it checks in pixel-space and not in coordinate-space.

Check out the documentation or download/fork the D3-Celestial source in the GitHub repository. The complete sample code is in the file snr.html in the demo folder.

Thursday, February 28, 2019

Solar System Missions Update 03/2019

Here's my map of all active and future Solar System Missions as of March 1st 2019, this time with the first ever past event on the list, because otherwise I felt there would have been a rover-shaped hole in the map.

The Chang'e 4 mission continues on the lunar far side, the SpaceIL lander Beresheet is on its way to a lunar landing in April, when ISRO also has announced to launch the Chandrayaan 2 Moon lander mission.

InSIGHT is doing a science on Mars now, OSIRIS-REx is in close orbit at Asteroid Bennu, and Hayabusa 2 will launch an Impactor at Asteroid Ryugu this month.

Data, images and documentation are available on my space exploration history GitHub repository and the associated website.

Saturday, February 23, 2019

Mikly Way Halo update: Globular Clusters

Another update for the Milky Way halo map with new globular clusters. They were discovered from data by the Gaia mission and lie mostly in the galactic bulge, a region previously difficult to analyze because of the great stellar density and extinction from dust clouds. Plus some more from other surveys I habe missed in the last few years (See the bottom of the page above for the source papers). This brings the number of Milky Way globular cluster to 147, with more candidates already discovered, but not enough data to be featured on the map yet.

Sunday, February 17, 2019

Space Exploration History update

I have updated the Space Exploration History app with lots of new data. Ground Systems got a new entrant with South Korea, which is planning it's own Moon missions, the necessary ground infrastructure therefore has been added to the map.

[Larger image]