我跟随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]
}
}]
}
}
谢谢, 克里斯