Search code examples
javascriptjqueryphantomjsleafletmarkerclusterer

All jQuery components are undefined when using Leaflet MarkerCluster with PhantomJs


I'm creating a test program with PhantomJs to create and render a Leaflet map with MarkerClusters, which is run through Phantomjs. However, the jQuery components seem to be coming back with no data.

To test this, I have a html file on the side that is doing the same functions, and can be loaded in a browser directly. It appears to work fine in the test html file, but not through Phantomjs.

Below is the code I am using.

Command Line call:

phanjs/bin/phantomjs test2.js seriesdatageo.js  600 600 geographic

Javascript file (test2.js)

var system = require('system');
var page = require('webpage').create();
var fs = require('fs');

page.injectJs("jquery-1.9.1.min.js") || ( console.log("Unable to load jQuery") && phantom.exit());
page.injectJs("leaflet-src.js") || ( console.log("Unable to load leafet-src.js") && phantom.exit());
page.injectJs("leaflet.markercluster-src.js") || (console.log("Unable to load leaflet.markercluster-src.js") && phantom.exit()); 
page.injectJs("html2canvas.js") || (console.log("Unable to load html2canvas.js") && phantom.exit()); 
page.injectJs("d3.min.js") || (console.log("Unable to load d3.min.js") && phantom.exit()); 
page.injectJs("html2canvas.svg.min.js") || (console.log("Unable to load html2canvas.svg.min.js") && phantom.exit()); 


page.onConsoleMessage = function (msg) {
    console.log(msg);
};

phantom.injectJs(system.args[1]) || (console.log("Unable to load json file") && phantom.exit());

var width = 350, height = 300;
if (system.args.length >= 4) {
    width = parseInt(system.args[2], 10);
    height = parseInt(system.args[3], 10);
}

console.log("Loaded result file");

var evalArg = {
  result: result,
  width: width,
  height: height
};

var svg = page.evaluate(function(opt) {

  if(opt.result.componentType =="chart") {
     //...
  }else if(opt.result.componentType =="geographic"){

    var content = "";
    var header = "";
    var body = "";

    header += "<link rel='stylesheet' href='leaflet.css'/> ";
    header += "<link rel='stylesheet' href='screen.css'/> ";
    header += "<link rel='stylesheet' href='MarkerCluster.css'/> ";
    header += "<link rel='stylesheet' href='MarkerCluster.Default.css'/> ";

    body += '<div id="map' + (opt.result.count==0? "": "-" + opt.result.count) + '"></div>';  
    $('head').append(header);
    $('body').append(body);

    var map = buildMap();
    createOverlay(map);

    if(checkLoaded()){
        waitForIt();
    }

    function checkLoaded() {
        return document.readyState === "complete";
    }

    function waitForIt() {
        console.log("BEFORE EXPORT");
        exportMap();
        console.log("AFTER EXPORT");
        //console.log(exportMap())
    }

    function createOverlay(container) {
        var svg = d3.select(container.getPanes().overlayPane).append("svg").attr("class", "leaflet-zoom-hide"),
            g = svg.append("g");
        var transform = d3.geo.transform({
            point: projectPoint
        }),
            path = d3.geo.path().projection(transform);

        var mapWidth = parseFloat($("#map").css("width").replace("px", ""));
        var mapHeight = parseFloat($("#map").css("height").replace("px", ""));

        svg.attr("width", mapWidth)
            .attr("height", mapHeight);

        // Use Leaflet to implement a D3 geometric transformation.
        function projectPoint(x, y) {
            var point = container.latLngToLayerPoint(new L.LatLng(y, x));
            this.stream.point(point.x, point.y);
        }

    }

    function exportMap() {
        console.log(1);

        var myDivicons = $(".leaflet-marker-icon"); //undefined
        console.log($(".leaflet-marker-icon")[0]);
        var dx = [];
        var dy = [];
        console.log(myDivicons.length); //returns 0 when it should equal 6
        for (var i = 0; i < myDivicons.length; i++) {
            var curTransform = myDivicons[i].style.transform;
            console.log(myDivicons[i]);
            var splitTransform = curTransform.split(",");
            dx.push(parseFloat(splitTransform[0].split("(")[1].replace("px", "")));
            dy.push(parseFloat(splitTransform[1].replace("px", "")));
            myDivicons[i].style.transform = "";
            myDivicons[i].style.left = dx[i] + "px";
            myDivicons[i].style.top = dy[i] + "px";
        }

        console.log(2);

        //get transform value
        image=  html2canvas($("#map")[0], {
                allowTaint: false,
                logging: true,
                useCORS: true,
                background: "#E8F0F6",
                onrendered: function (canvas) {

                    var a = document.createElement('a');
                    //a.download = name;
                    var dataUrl = canvas.toDataURL('image/png');
                    if (a.download !== undefined) {
                        a.setAttribute("id", 'download');
                        a.setAttribute("href", dataUrl);
                        a.setAttribute("download", "sample");
                    } else if (navigator.msSaveBlob) {
                        a.addEventListener("click", function (event) {
                            var blob = dataURItoBlob(dataUrl);
                            navigator.msSaveBlob(blob, "sample"+'.png');
                        }, false);
                        a.setAttribute("onmouseover", "");
                        a.setAttribute("style", "cursor: pointer; cursor: hand;");
                    }

                    document.body.appendChild(a);
                    //a.click();

                },
            });

        console.log(3);

        //console.log($('#download').attr('href'));
        console.log($('#download').innerHTML);
        return image;

    }

    function buildMap() {
         var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
                maxZoom: 18,
                attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            }),
            latlng = L.latLng(51.505, 10.09);
        var map = L.map('map', {
            center: latlng,
            zoom: 2,
            layers: [tiles],
            zoomControl: false
        });
        var markers = L.markerClusterGroup({
            spiderfyOnMaxZoom: false,
            showCoverageOnHover: false,
            zoomToBoundsOnClick: false
        });

        markers.addLayer(L.marker(L.latLng(-17.216203333, -46.870545)));
        markers.addLayer(L.marker(L.latLng(-6.18091, 106.9788)));
        markers.addLayer(L.marker(L.latLng(22.6333, 120.267)));
        markers.addLayer(L.marker(L.latLng(-6.238933794, 106.868886901)));
        markers.addLayer(L.marker(L.latLng(45.518437626, 9.10791372)));
        markers.addLayer(L.marker(L.latLng(-7.014492779, -42.125545053)));
        markers.addLayer(L.marker(L.latLng(-34.075959932, 18.819547112)));
        markers.addLayer(L.marker(L.latLng(-8.14272, 112.56221)));
        markers.addLayer(L.marker(L.latLng(45.802121064, 15.960097081)));
        markers.addLayer(L.marker(L.latLng(-7.55217, 110.8214)));
        markers.addLayer(L.marker(L.latLng(25.251247578, 55.342014803)));
        markers.addLayer(L.marker(L.latLng(14.584461153, 121.057085752)));
        markers.addLayer(L.marker(L.latLng(16.0796434, 101.8043193)));
        markers.addLayer(L.marker(L.latLng(16.0796434, 101.8043193)));
        markers.addLayer(L.marker(L.latLng(14.583363333, 120.999746667)));
        markers.addLayer(L.marker(L.latLng(16.0796434, 101.8043193)));
        markers.addLayer(L.marker(L.latLng(16.0796434, 101.8043193)));
        markers.addLayer(L.marker(L.latLng(16.0796434, 101.8043193)));
        map.addLayer(markers);

        /* Initialize the SVG layer */
        map._initPathRoot();

        return map;
    }
        return "TEST";
   }


}, evalArg);


console.log("Saved SVG to file");
console.log(svg);
phantom.exit();

Solution

  • Found the issue, the map was not displayed on the screen and the images location was not set.

    Code changes which were required were:

    add to css file. 
    #map {
      height:800px;
    }
    
     //Put this above adding the markers
     L.Icon.Default.imagePath = '/images';
     map._initPathRoot();