Saturday, March 24, 2018

How To Put Your Own Data on the Celestial Map

I have added a simple example of how to add your own data to my interactive celestial map. Specifically, how to display the Summer Triangle at its proper position. 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">

Whatever you add needs to be valid geoJSON. The various types of objects are described in the readme of the data folder in the GitHub repository. This can be a separate file or a JSON object filled at runtime or defined inline. Like so:

  var jsonLine = {
    "type":"FeatureCollection",
    // this is an array, add as many objects as you want
    "features":[
      {"type":"Feature",
       "id":"SummerTriangle",
       "properties": {
         // Name
         "n":"Summer Triangle",
         // Location of name text on the map
         "loc": [-67.5, 52]
       }, "geometry":{
         // the line object as an array of point coordinates, 
         // always as [ra -180..180 degrees, dec -90..90 degrees]
         "type":"MultiLineString",
         "coordinates":[[
           [-80.7653, 38.7837],
           [-62.3042, 8.8683],
           [-49.642, 45.2803],
           [-80.7653, 38.7837]
         ]]
       }
      }  
    ]
  }; 

As you can see, this defines the Summer Triangle asterism, consisting of the bright stars Vega (Alpha Lyr), Deneb (Alpha Cyg) and Altair (Alpha Aql).

You also need to define how the triangle is going to look like with some styles (see documentation on GitHub):

  var lineStyle = { 
    stroke:"#f00", 
    fill: "rgba(255, 204, 204, 0.4)",
    width:3 
  };
  var textStyle = { 
    fill:"#f00", 
    font: "bold 15px Helvetica, Arial, sans-serif", 
    align: "center", 
    baseline: "bottom" 
  };

Lastly, we need some settings for the proper map, also documented on the GitHub repository:

  var config = { 
    width: 640,
    projection: "airy", 
    center: [-65, 0],
    background: { fill: "#fff", stroke: "#000", opacity: 1, width: 1 },
    datapath: "https://ofrohn.github.io/data/",
    stars: { 
      colors: false, 
      names: false, 
      style: { fill: "#000", opacity:1 }, 
      limit: 6, 
      size:5 
    },
    dsos: { show: false },
    mw: { 
      style: { fill:"#996", opacity: 0.1 } 
    },
  };

Now we can get to work, with the function

Celestial.add({file:string, type:"line", callback:function, redraw:function)

The file argument is optional for providing an external geoJSON file, since we already defingd our data, we don't need it. Type is 'line', that leaves two function definiions, the first one gets called at loading, this is where we add our data to the d3-celestial data container, and redraw is called at every redraw event for the map. so here you need to define how to display the added object(s).

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

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

The callback funtion is pretty straight forward: Load the data with Celestial.getData, add to Celestial.container in te usual d3 manner, and redraw. It also provides a json parameter that contains the parsed JSON if a file property is given, but we already have defined jsonLine above, so we use that.

  redraw: function() {   
    // Select the added objects by class name as given previously
    Celestial.container.selectAll(".ast").each(function(d) {
      // Set line styles 
      Celestial.setStyle(lineStyle);
      // Project objects on map
      Celestial.map(d);
      // draw on canvas
      Celestial.context.fill();
      Celestial.context.stroke();
      
      // If point is visible (this doesn't work automatically for points)
      if (Celestial.clip(d.properties.loc)) {
        // get point coordinates
        pt = Celestial.mapProjection(d.properties.loc);
        // Set text styles       
        Celestial.setTextStyle(textStyle);
        // and draw text on canvas
        Celestial.context.fillText(d.properties.n, pt[0], pt[1]);
      }      
    })
  }

And the redraw function with the actual display of the elements, contained in a d3.selectAll call on the previously set class property of the added objects. Celestial.setStyle applies the predefined canvas styles, Celestial.map projects each line on the map. However, that doesn't work for points, so that is done manually with Celestial.clip (true if point is currently visible) and Celestial.mapProjection. and the rest are standard canvas fill and stroke operations. The beginPath and closePath commands are done automatically.

  Celestial.display(config);

Finally, the whole map is displayed. Check out the documentation or download/fork the D3-Celestial source in the GitHub repository. The complete sample code is in the file triangle.html in the demo folder.

No comments:

Post a Comment