【问题标题】:Adding Zoom behavior to custom map points将缩放行为添加到自定义地图点
【发布时间】:2018-10-11 17:10:46
【问题描述】:

作为学习 D3 的练习,我使用了一个来自之前项目的数据集,该数据集来自于世界各地机场的位置和名称。我正在使用 D3.csv 将其加载到我的网页中,并使用 topojson 在地图上绘制点。

在我的练习中,我正在尝试添加一项功能,让用户可以放大和缩小世界地图。可以想象,由于我还没有添加任何过滤器逻辑,所以有很多机场并且地图变得拥挤。

最糟糕的是,我可以让 Zoom 行为适用于国家,但我不确定如何让它适用于我绘制的圆圈。如果我使用滚轮放大地图,地图会放大,但圆圈会保持原位。

<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<style type="text/css">
    .feature {
        fill: none;
        stroke: grey;
        stroke-width: 1px;
        stroke-linejoin: round;
    }
    .mesh {
        fill: none;
        stroke: lightgrey;
        stroke-width: 2px;
        stroke-linejoin: round;
    }
    h1 {
        font-family: sans-serif;
    }

    svg {
  background: #eee;
}

.sphere {
  fill: #fff;
}

.land {
  fill: #000;
}

.boundary {
  fill: none;
  stroke: #fff;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.overlay {
  fill: none;
  pointer-events: all;
}

circle{
  fill: steelblue;
  stroke-width: 1.5px;  
}

.d3-tip {
  line-height: 1;
  font-weight: bold;
  padding: 12px;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  border-radius: 2px;
}

/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
  box-sizing: border-box;
  display: inline;
  font-size: 10px;
  width: 100%;
  line-height: 1;
  color: rgba(0, 0, 0, 0.8);
  content: "\25BC";
  position: absolute;
  text-align: center;
}

/* Style northward tooltips differently */
.d3-tip.n:after {
  margin: -1px 0 0 0;
  top: 100%;
  left: 0;
}
</style>
</head>
<body>
    <h1>Lots of airports across the world</h1>

<script type="text/javascript">
var width = 950,
    height = 550;
    scale0 = (width - 1) / 2 / Math.PI;

var projection = d3.geo.mercator();

var zoom = d3.behavior.zoom()
    .translate([width / 2, height / 2])
    .scale(scale0)
    .scaleExtent([scale0, 8 * scale0])
    .on("zoom", zoomed);

var path = d3.geo.path()
    .projection(projection);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g");

var g = svg.append("g");

var circle = svg.append("circle");

svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);

svg
    .call(zoom)
    .call(zoom.event);

var tip = d3.tip()
    .attr("class", "d3-tip")
    .offset([-10, 0])
    .html(function(d) {
        return "Name" + ": " + d[2] + "<br>" + "Location" + ": " + d[3];
    });

svg.call(tip);

d3.json("world-110m.v1.json", function(error, world) {
  if (error) throw error;

  g.append("g")
      .attr("d", path)
      .on("click", clicked)
      .on("zoom", zoomed);

  g.append("path")
      .datum({type: "Sphere"})
      .attr("class", "sphere")
      .attr("d", path);

  g.append("path")
      .datum(topojson.merge(world, world.objects.countries.geometries))
      .attr("class", "land")
      .attr("d", path);

  g.append("path")
      .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
      .attr("class", "boundary")
      .attr("d", path)
      .on("click", clicked);

    d3.csv("output.csv",
        function(data) {return {name: data.Airport_name, location: data.Location_served, 
            long : +data.Longitude, lat : +data.Latitude}},
        function(data) {
        var new_array = data.map(function (d) {return [d.long, d.lat, d.name, d.location]});
        console.log("new", new_array)

        svg.selectAll("circle")
            .data(new_array)
            .enter()
            .append("circle")
            .attr("cx", function (d) { return projection(d)[0]; })
            .attr("cy", function (d) { return projection(d)[1]; })
            .attr("r", "2px")
            .on("mouseover", tip.show)
            .on("mouseout", tip.hide);
        });

    }) //closes the json, do not move.

// begin click-zoom listeners
function clicked(d) {
    console.log("d:",d)
  var centroid = path.centroid(d),
      translate = projection.translate();

  projection.translate([
    translate[0] - centroid[0] + width / 2,
    translate[1] - centroid[1] + height / 2
  ]);

  zoom.translate(projection.translate());

  g.selectAll("path").transition()
      .duration(700)
      .attr("d", path);
}

function zoomed() {
  projection.translate(d3.event.translate).scale(d3.event.scale);
  g.selectAll("path").attr("d", path);
}
</script>    
</body>

那么开始看起来像这样

放大后看起来像这样

我希望圆圈和国家一样移动。

CSV 样本:

Airport_name,DST,IATA,ICAO,Location_served,Time,Latitude,Longitude
Anaa Airport,,AAA,NTGA,"Anaa, Tuamotus, French Polynesia",UTC?10:00,-16.9419074,-144.8646172
Arrabury Airport,,AAB,YARY,"Arrabury, Queensland, Australia",UTC+10:00,-26.7606354,141.0269959
El Arish International Airport,,AAC,HEAR,"El Arish, Egypt",UTC+02:00,31.1272509,33.8045859
Adado Airport,,AAD,,"Adado (Cadaado), Galguduud, Somolia",UTC+03:00,9.56045635,31.65343724
Rabah Bitat Airport (Les Salines Airport),,AAE,DABB,"Annaba, Algeria",UTC+01:00,36.8970249,7.7460806
Apalachicola Regional Airport,Mar-Nov,AAF,KAAF,"Apalachicola, Florida, United States",UTC?05:00,29.7258675,-84.9832278
Arapoti Airport,Oct-Feb,AAG,SSYA,"Arapoti, Paraná, Brazil",UTC?03:00,-24.1458941,-49.8228117
Merzbrück Airport,Mar-Oct,AAH,EDKA,"Aachen, North Rhine-Westphalia, Germany",UTC+01:00,50.776351,6.083862
Arraias Airport,,AAI,SWRA,"Arraias, Tocantins, Brazil",UTC?03:00,-12.9287788,-46.9437231

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您的缩放功能做了两件事,它修改投影并使用修改后的投影更新路径:

    function zoomed() {
      projection.translate(d3.event.translate).scale(d3.event.scale); // modify the projection
      g.selectAll("path").attr("d", path);   // update the paths
    }
    

    好的,所以除了使用绑定基准修改每次缩放的路径之外,我们还需要修改圆圈:

    function zoomed() {
      projection.translate(d3.event.translate).scale(d3.event.scale); // modify the projection
      g.selectAll("path").attr("d", path);   // update the paths
    
      // update the circles/points:
      svg.selectAll("circle")
        .attr("cx", function (d) { return projection(d)[0]; })
        .attr("cy", function (d) { return projection(d)[1]; })
      });
    }
    

    然而这并不完全奏效,我们需要看看你是如何附加圆圈的:

        svg.selectAll("circle")
            .data(new_array)
            .enter()
            .append("circle")
            .attr("cx", function (d) { return projection(d)[0]; })
            .attr("cy", function (d) { return projection(d)[1]; })
    

    如果 svg 上还没有圆圈,那就太好了 - 但是有,您在此处附加了一个:

    var circle = svg.append("circle");
    

    这意味着不会添加数组中的第一个机场,因为 svg 中已经为数据数组中的该项目添加了一个圆圈。空选择 (d3.selectAll(null)) 将确保为数据数组中的每个项目输入一个项目。

    这里最重要的是,在数据加载之前,第一个圆没有绑定基准。这会在调用缩放时导致一些问题,没有用于重新缩放圆圈的绑定数据,您会收到错误消息。相反,您可以在机场附加一个类并在缩放事件期间选择它们。

    在我的示例here 中,我使用空选择来输入机场,并给它们一个类,以便我可以根据更新的投影轻松选择要重新定位的圆圈。 (为了演示,我还简化了世界地图,增加了点半径)。

    这看起来像:

    function zoomed() {
      projection.translate(d3.event.translate).scale(d3.event.scale);
      g.selectAll("path").attr("d", path);
    
     svg.selectAll(".airport")
       .attr("cx", function (d) { return projection(d)[0]; })
       .attr("cy", function (d) { return projection(d)[1]; }) 
    }
    

    输入是:

        svg.selectAll()   // selectAll() is equivilant to selectAll(null)
            .data(new_array)
            .enter()
            .append("circle")
            .attr("class","airport")
            .attr("cx", function (d) { return projection(d)[0]; })
            .attr("cy", function (d) { return projection(d)[1]; })
            .attr("r", "6px")
            .on("mouseover", tip.show)
            .on("mouseout", tip.hide);
        });
    

    【讨论】:

    • "TypeError: Cannot read property '0' of undefined" 如何在 d3.csv 函数之外读取用于生成我的点的数据?
    • 啊,我没看到你在附加一个额外的圆圈:svg.append("circle"); 这没有绑定数据。缩放使用绑定到点的数据(与重绘路径时的路径相同),我将进行编辑。不过,如果您能提供数据样本,我可以进行适当的演示。
    • 示例添加到主要问题
    • 谢谢,我希望我在更新后的答案中很清楚(example) - 现在还为时过早或对我来说清楚。
    • 这真是一个示范!谢谢,我会更多地阅读关于附加元素以及添加和操作类属性的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    • 2011-02-16
    相关资源
    最近更新 更多