0

(Edit: the question have been rewritten to better isolate the issue.)


I'am writing a single D3js script.js which could be reused "as it" in both web browser for client side and nodejs for server side uses. I made some good progress :

  1. a single d3js script.js indeed create basic svg shapes on both client and server sides. On web browser displaying the viz, on server side outputing the wanted map.svg file.
  2. I have a more complex d3js map making code: client side it works like a charm (link)

enter image description here

but! the nodejs call fails without error message. The xhr d3.json() seems to be hanging up waiting and not firing up its callback:

mapIt.node.js (server side, fails)

Pass variables and run me:

$ WIDTH=800 ITEM="world-1e3" node script.node.js

Content of script.node.js:

var jsdom = require('jsdom');         // npm install jsdom
var fs    = require('fs');            // natively in nodejs.

jsdom.env(
  "<html><body></body></html>",       // CREATE DOM HOOK
  [ './d3.v3.min.js',             // load assets into window environment (!)
  './topojson.v1.min.js', 
  './queue.v1.min.js', 
  './script.js' ],

  function (err, window) {
    /* ***************************************************************** */
    /* Check availability of loaded assets in window scope. ************ */
    console.log(typeof window.mapIt);       // expect: 'function',  because exist in script.js !
    console.log(typeof window.doesntExist); // expect: 'undefined', because doesn't exist anywhere.
    // if all as expected, should work !

    /* ***************************************************************** */
    /* COLLECT ENV.VARIABLES ******************************************* */
    var width = process.env.WIDTH,
        target= process.env.ITEM;

    /* ***************************************************************** */
    /* D3js FUNCTION *************************************************** */
    var url = "http://rugger-demast.codio.io/output/"+target+"/administrative.topo.json";
    console.log(url);
    console.warn(window.document.URL);

    var mapIt = function(width, target){
        console.log("mapIt(): start");

        var svg = d3.select('body').append('svg')
            .attr('width', width)
            .attr('height', width/960*500);
        var projection = d3.geo.mercator()
            .scale(100)
            .translate([width / 2, height/2]);
        var path = d3.geo.path()
            .projection(projection);

        var url = "http://rugger-demast.codio.io/output/"+target+"/administrative.topo.json";

        /* FROM HERE SOMETHING FAILS ********************** */
        d3.json(url, function (error, json) { // from here: NOT fired on server side :(
        if (error) return console.warn(error);
            d3.select("svg").append("g").attr("log","d3.json"); 
            svg.append('path')
                .datum(topojson.feature(json, json.objects.admin_0))
                    .attr('d', path)
                    .attr('class', 'L0');
        });    
    };
    window.mapIt(width,target);
    /* ***************************************************************** */
    /* SVG PRINT ******************************************************* */
    // better svg header:
    var svgheader = '<?xml version="1.0" encoding="utf-8"?>\n'
    +'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n';
    // printing + timer, to be sure all is ready when printing file:
    setTimeout(
      fs.writeFileSync('map.svg', svgheader + window.d3.select("body").html()) ,
      30000
    );
 }
);

map.svg (incomplete)

Since d3.json callback is not fired, I get the incomplete map.svg such:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="800" height="416.6666666666667"></svg>

Given map.svg's content, the script first work as expected, setting the svg width and height attributes, then the d3.json() doesn't work, the call back is never fired up. There is no error message back. But given where the script stop, it seems the query is hanging up waiting.

Notes:

The same exact d3js script works client side (link).

Interestingly, console.warn(window.document.URL) returns file:///data/yug/projects_active/map/script.node.js (purely local) while the d3.json() xhr request is on http://bl.ocks.org/hugolpz/raw/1c34e14d8b7dd457f802/administrative.topo.json.

Question

How to get the request working ? or How to run nodejs script with so the query is allowed ?

Testing

To try out the script (Github gist):

git clone https://gist.github.com/9bdc50271afc49d33e35.git ./map
cd ./map; npm install
WIDTH=800 ITEM="world-1e3" node script.node.js

Help: D3js>API>Requests

Hugolpz
  • 17,296
  • 26
  • 100
  • 187
  • Are you getting any errors? That queue cross-domain request looks dodgy. – Union find Feb 23 '15 at 06:34
  • Cross domain query fails may be a clue, i am indeed runing it on my PC, which query .json data on some online website. Strange thing is, the client side demo on blocks.org have no trouble with this cross domain. But i remember M. Bostock 's blocks.org has some settings allowing cross domain : *"By default, most browsers do not allow cross-domain requests. To enable cross-domain requests, have the server set the header Access-Control-Allow-Origin: * ."* ([source](https://github.com/mbostock/d3/wiki/Requests)) – Hugolpz Feb 23 '15 at 10:49
  • Couple things: First, in `d3.json(url, function (error, json) { `, you drop the error, log it `if (error) return console.warn(error);`, this might provide some clues. Second, are you sure this isn't a race condition? You are making an async call and then waiting 7 seconds to write out the SVG. Are you sure it will complete? Seems problematic... – Mark Feb 23 '15 at 14:43
  • Also, I don'tthink it's a CORs problem. the `ruggr-demast.codio.io` response included `Access-Control-Allow-Origin:*`. – Mark Feb 23 '15 at 14:44
  • @Mark: Using my connexion, d3.json query usually done within 2-3 secs in google chrome ([try it](https://rugger-demast.codio.io/nodejs/index.html)). Waiting 7000ms seems save. Let me try 20000ms. **Update:** checked with 20 000ms, same fails. – Hugolpz Feb 23 '15 at 14:46
  • `if (error) return console.warn(error);` returns nothing, it seems d3.json is hanging up waiting, and the callback functions is never called. – Hugolpz Feb 23 '15 at 14:52
  • Somthing interesting : `console.warn(window.document.URL);` logs `file:///data/yug/projects_active/map/script.node.js` , and the d3.json() request is on `http://rugger-demast.codio.io/output/world-1e3/administrative.topo.json`. – Hugolpz Feb 23 '15 at 15:55
  • 1
    Tried to replicate your problem but am getting an error deep down in the guts of `jsdom`; apparently it doesn't run well on Windows... :( – Mark Feb 23 '15 at 15:59
  • @Mark: Yes, jsdom has troubles with contextify. Yet, thanks for your good will! :D – Hugolpz Feb 23 '15 at 16:04

1 Answers1

1

Replace :

setTimeout(
      fs.writeFileSync('map.svg', svgheader + window.d3.select("body").html()) ,
      10000
    );

by

setTimeout(
      function(){ fs.writeFileSync('map.svg', svgheader + window.d3.select("body").html()) } ,
      10000
    );
Hugolpz
  • 17,296
  • 26
  • 100
  • 187