将带有SVG图层的传单贴图导出到图像

时间:2016-08-11 18:18:00

标签: javascript d3.js svg leaflet

我正在使用Leaflet Leaflet-D3's hexbin。我想将地图与hexbin图层导出到图像。为了将地图导出到图像,到目前为止我一直在使用Leaflet-image。但是,由于hexbin overlay是一个SVG图层,当我使用相同的库导出map + hexbin时,图像上只显示地图。

如何将该hexbin图层导出到Leaflet-image生成的同一图像中?例如,我已经看到了将SVG导出到图像的工具,但是在我的情况下这不会完全有效,因为它只能解决hexbin而不是地图。

1 个答案:

答案 0 :(得分:3)

这实际上非常酷。使用描述here的代码,您实际上可以将生成的传单图像与从SVG创建的图像组合。

由于代码比单词更响亮,这里有一个我放在一起的样本:



<!DOCTYPE html>
<html>

<head>
  <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
  <script type="text/javascript" src="https://rawgit.com/d3/d3-plugins/master/hexbin/hexbin.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css">
  <script type="text/javascript" src="https://rawgit.com/Asymmetrik/leaflet-d3/master/dist/leaflet-d3.js"></script>
  <script src="https://rawgit.com/mapbox/leaflet-image/gh-pages/leaflet-image.js"></script>
</head>

<body>

  <div id="map" style="width: 600px; height: 400px; border: 1px solid #ccc"></div>
  <button onclick="generateImage()">Create Image</button>
  <div id="images"></div>

  <script>
    var center = [39.4, -78];

    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      osm = L.tileLayer(osmUrl, {
        maxZoom: 18,
        attribution: osmAttrib
      });

    map = new L.Map('map', {
      layers: [osm],
      center: new L.LatLng(center[0], center[1]),
      zoom: 7
    });

    var options = {
      radius: 12,
      opacity: 0.5,
      duration: 500,
      lng: function(d) {
        return d[0];
      },
      lat: function(d) {
        return d[1];
      },
      value: function(d) {
        return d.length;
      },
      valueFloor: 0,
      valueCeil: undefined
    };

    var hexLayer = L.hexbinLayer(options).addTo(map)
    hexLayer.colorScale().range(['white', 'blue']);

    var latFn = d3.random.normal(center[0], 1);
    var longFn = d3.random.normal(center[1], 1);
    
    var generateData = function() {
      var data = [];
      for (i = 0; i < 1000; i++) {
        data.push([longFn(), latFn()]);
      }
      hexLayer.data(data);
      
      d3.selectAll('.hexbin-hexagon')
        .style({
            "stroke": '#000',
            "stroke-width": '1px'
        });
    };
    
    generateData();
    
    var getOverlay = function(){
        // Select the first svg element
        var svg = d3.select('.leaflet-overlay-pane>svg'),
            img = new Image(),
            serializer = new XMLSerializer(),
            svgStr = serializer.serializeToString(svg.node());
            
        img.src = 'data:image/svg+xml;base64,'+window.btoa(svgStr);
        
        return {
          img: img,
          w: +svg.attr('width'),
          h: +svg.attr('height')
        }
    };

    var generateImage = function() {
      leafletImage(map, function(err, canvas) {
        
        var d3O = getOverlay();
        canvas.getContext("2d").drawImage(d3O.img,0,0,d3O.w,d3O.h);
        
        // now you have canvas
        // example thing to do with that canvas:
        var img = document.createElement('img');
        var dimensions = map.getSize();
        img.width = dimensions.x;
        img.height = dimensions.y;
        img.src = canvas.toDataURL();
        document.getElementById('images').innerHTML = '';
        document.getElementById('images').appendChild(img);
      });
    };
  </script>
</body>

</html>
&#13;
&#13;
&#13;

我的初始代码没有考虑缩放和平移。这是重写。它有点疯狂所以我只用chrome测试过。说实话,此时,我会使用phantomJS渲染此服务器端,将其捕获为JPEG并将其返回给浏览器。

&#13;
&#13;
<!DOCTYPE html>
<html>

<head>
  <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
  <script type="text/javascript" src="https://rawgit.com/d3/d3-plugins/master/hexbin/hexbin.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css">
  <script type="text/javascript" src="https://rawgit.com/Asymmetrik/leaflet-d3/master/dist/leaflet-d3.js"></script>
  <script src="https://rawgit.com/mapbox/leaflet-image/gh-pages/leaflet-image.js"></script>
</head>

<body>

  <div id="map" style="width: 600px; height: 400px; border: 1px solid #ccc"></div>
  <button onclick="generateImage()">Create Image</button>
  <div id="images"></div>

  <script>
    var center = [39.4, -78],
        width = 600,
        height = 400;

    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      osm = L.tileLayer(osmUrl, {
        maxZoom: 18,
        attribution: osmAttrib
      });

    map = new L.Map('map', {
      layers: [osm],
      center: new L.LatLng(center[0], center[1]),
      zoom: 7
    });

    var options = {
      radius: 12,
      opacity: 0.5,
      duration: 500,
      lng: function(d) {
        return d[0];
      },
      lat: function(d) {
        return d[1];
      },
      value: function(d) {
        return d.length;
      },
      valueFloor: 0,
      valueCeil: undefined
    };

    var hexLayer = L.hexbinLayer(options).addTo(map)
    hexLayer.colorScale().range(['white', 'blue']);

    var latFn = d3.random.normal(center[0], 1);
    var longFn = d3.random.normal(center[1], 1);
    
    var generateData = function() {
      var data = [];
      for (i = 0; i < 1000; i++) {
        data.push([longFn(), latFn()]);
      }
      hexLayer.data(data);
    };
    
    generateData();

    var getOverlay = function(){
        // Select the first svg element
        var svg = d3.select('.leaflet-overlay-pane>svg'),
            img = new Image(),
            serializer = new XMLSerializer();
           
        svg.select("g").attr("transform", null);
        svg.style("margin-top", null);
        svg.style("margin-left", null);
        svg.attr("height", null);
        svg.attr("width", null);
        var svgStr = serializer.serializeToString(svg.node());

        img.src = 'data:image/svg+xml;base64,'+window.btoa(svgStr);
        
        return img;
    };

    var generateImage = function() {
      leafletImage(map, function(err, canvas) {
        
        var t = d3.select('.leaflet-map-pane').style('transform').split(", "),
           img = getOverlay(),
           x = parseInt(t[4]),
           y = parseInt(t[5]);

        canvas.getContext("2d").drawImage(img,
          x,
          y,
          width,
          height
        );
        
        // now you have canvas
        // example thing to do with that canvas:
        var img = document.createElement('img');
        var dimensions = map.getSize();
        img.width = dimensions.x;
        img.height = dimensions.y;
        img.src = canvas.toDataURL();
        document.getElementById('images').innerHTML = '';
        document.getElementById('images').appendChild(img);
      });
    };
  </script>
</body>

</html>
&#13;
&#13;
&#13;