将缩放行为添加到自定义地图点

时间:2018-05-01 16:34:26

标签: javascript d3.js

作为学习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>

所以看起来像Looking like this

在放大this upon zoom

时,

看起来像这样

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

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

1 个答案:

答案 0 :(得分: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);
    });