如何启用拖动传单地图上叠加的D3元素

时间:2016-01-18 22:01:08

标签: d3.js leaflet

我跟随Mike Bostock's tutorial在Leaflet地图上用D3渲染svg元素,但除了他的示例中的功能之外我还希望用户能够重新定位D3通过拖动它们来拖动地图上的元素。也就是说,我希望用户能够通过拖动这些元素来在地图上重新定位传单覆盖窗格中D3渲染的SVG元素中的对象,并在用户拖动或缩放传单地图时保留更新的地理位置。 / p>

在D3拖动事件中有一些非常hacky的html编辑,我设法做到了这一点(see jsFiddle - 拖动蓝色圆圈以确认功能)但我希望找到更好的方法。有什么建议?以下是我到目前为止的情况:

var svg, g, map, collection, transform, path, json, d_string;
json = get_json();
setup_map();

function setup_map(){
    map = L.map('map').fitBounds([[-3.82,-73.24],[-3.69,-73.35]]);
    L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
        maxZoom: 18,
        attribution: 'OpenStreetMap, D3'
    }).addTo(map);  
    map.on("viewreset", reset);
    setup_d3();
}

function setup_d3(){
    svg = d3.select(map.getPanes().overlayPane).append("svg");
    g = svg.append("g").attr("class", "leaflet-zoom-hide");

    drag = d3.behavior.drag()
    .origin(function(d) { return d; })
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended);

    transform = d3.geo.transform({point: projectPoint});
    path = d3.geo.path().projection(transform);

    g.selectAll("path")
    .data(json.features)
    .enter().append("path");

    g.selectAll("path")
    .attr('fill','blue')
    .attr("r", 10)
    .style("cursor", "pointer")
    .call(drag);
    reset();
}

function reset() {
    var buffer_space = 200; //so point markers are fully drawn, and to give you space to move markers around
    bounds = path.bounds(json);
    var topLeft = bounds[0], bottomRight = bounds[1];
    topLeft[0] -= buffer_space;
    topLeft[1] -= buffer_space;
    bottomRight[0] += buffer_space;
    bottomRight[1] += buffer_space;

    svg.attr("width", bottomRight[0] - topLeft[0])
      .attr("height", bottomRight[1] - topLeft[1])
      .style("left", topLeft[0] + "px")
      .style("top", topLeft[1] + "px");

    g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
    g.selectAll("path").attr("d", path);
}

function projectPoint(x, y) {
    var point = map.latLngToLayerPoint(new L.LatLng(y, x));
    this.stream.point(point.x, point.y);
}

function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    d3.select(this).classed("dragging", true);
    d_string = d3.select(this).attr("d");
    d_string = d_string.substring(d_string.indexOf("m"));
}

function dragged(d) {
    var offset = get_leaflet_offset();
    var size = d3.select(this).attr("r")/2;
    var pt = [d3.event.sourceEvent.clientX - size - offset[0], d3.event.sourceEvent.clientY - size - offset[1]];
    var hackpath = "M" + pt[0] + "," + pt[1] + d_string;
    d3.select(this).attr("d", hackpath);
}

function dragended(d) {
    var offset = get_leaflet_offset();
    var size = d3.select(this).attr("r")/2;
    var pt = layer_to_LL(d3.event.sourceEvent.clientX - size - offset[0], d3.event.sourceEvent.clientY - size - offset[1]);
    d.geometry.coordinates = [pt.lng, pt.lat];
    d3.select(this).classed("dragging", false);
    reset();
}

function get_leaflet_offset(){
    var trfm = $(".leaflet-map-pane").css('transform');
    trfm = trfm.split(", ");
    return [parseInt(trfm[4]), parseInt(trfm[5])];
}

function layer_to_LL(x,y){return map.layerPointToLatLng(new L.Point(x,y));}

function projectPoint(x, y) {
  var point = map.latLngToLayerPoint(new L.LatLng(y, x));
  this.stream.point(point.x, point.y);
}

function projectSinglePoint(x, y) {
  var point = map.latLngToLayerPoint(new L.LatLng(y, x));
  console.log(point);
  return point;
}

function get_json(){
    return {
    "type": "FeatureCollection",
    "crs": {
        "type": "name",
        "properties": {"name": "urn:ogc:def:crs:EPSG::4269"}
    },
    "features": [{
        "type": "Feature",
        "properties": {"id": "pt0"},
        "geometry": {
            "type": "Point",
            "coordinates": [-73.25, -3.72]
        }
    }, {
        "type": "Feature",
        "properties": {"id": "pt1"},
        "geometry": {
            "type": "Point",
            "coordinates": [-73.37, -3.82]
        }
    }, {
        "type": "Feature",
        "properties": {"id": "pt2"},
        "geometry": {
            "type": "Point",
            "coordinates": [-73.32, -3.67]
        }
    }]
    }
}

谢谢, 克里斯

1 个答案:

答案 0 :(得分:0)

在我看来,我会使用默认的传单选项来制作markers并将其设为draggable

这样的事情:

  json.features.forEach(function(d) {
    var marker = L.marker(new L.LatLng(d.geometry.coordinates[1], d.geometry.coordinates[0]), {
        draggable: true
    });
    marker.addTo(map);
  })

这将减轻您在D3上执行拖动逻辑的过程,您可以看到减少了多少代码:)

工作代码here

标记文档here

希望这有帮助!