导出传单地图与SVG图层图像

Export leaflet map WITH SVG layer to image

本文关键字:SVG 图层 图像 地图 单地图      更新时间:2023-09-26

我使用的是Leaflet- d3的hexbin。我想将地图与hexbin层导出为图像。为了将地图导出到图像,到目前为止我一直使用Leaflet-image。然而,由于hexbin叠加是一个SVG层,当我使用相同的库导出地图+hexbin时,只有地图出现在图像上。

我怎么做才能导出该hexbin图层到相同的图像由Leaflet-image?例如,我见过将SVG导出为图像的工具,但这在我的情况下并不完全有效,因为它只处理hexbin而不是map。

这真的很酷。使用这里描述的代码,您可以实际地将生成的传单图像与从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>

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

<!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>