Overlaying circles on leaflet with d3 results in not positioned properly

强颜欢笑 提交于 2019-12-25 04:18:02

问题


I want to overlay a leaflet map with circles through d3.

<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="utf-8">
<title>D3 Test</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css">
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
</head>

<body>
<div id="map" style="width:600px; height:600px;">
    <script>
        var map = new L.Map("map", {
                center: [37.8, -96.9],
                zoom: 4
            })
            .addLayer(new L.TileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"));

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

        d3.json("https://jsonblob.com/api/580256bbe4b0bcac9f7ffa43", function(error, myPoints) {
            if (error) throw error;


            myPoints.features.forEach(function(d) {
                d.LatLng = new L.LatLng(d.geometry.coordinates[1],
                    d.geometry.coordinates[0]);
            });

            var circles = svgCircles.selectAll("circle")
                .data(myPoints.features)
                .enter()
                .append("circle")
                .attr("r", 100)
                .style("fill", "red")
                .attr("fill-opacity", 0.5);

            function update() {
                circles.attr("cx", function(d) {
                    return map.latLngToLayerPoint(d.LatLng).x;
                });
                circles.attr("cy", function(d) {
                    return map.latLngToLayerPoint(d.LatLng).y;
                });
            }

            map.on("viewreset", update);
            update();

        })
    </script>
</body>

</html>

The circles are added to DOM, but do not show up on leaflet. I suppose its because they are positioned wrong. Thats how it looks like:

How would I position them correct? Note: I dont want to use map._initPathRoot(); to pick up the svg-element created by leaflet.


回答1:


First, your radius on those circles is way too large. You have thousands of points in your JSON file each with a radius of 100px and your map is only 600x600px.

Second, it seems like you are attempting to follow this tutorial, but you are leaving out a key piece. Mr. Bostock talks about how to calculate the bounding box of your features to place your SVG in the right position. You've left this part out, so your SVG is being placed at 0,0 with a default size.

Following his tutorial with your data produces the following code. Note, I limited your JSON data to only a few cities in the north-east. I didn't want to find some place to host 6mb of JSON (you might want to think about cutting that file down to only US cities).

<!DOCTYPE html>

<html lang="en">

<head>
  <meta charset="utf-8">
  <title>D3 Test</title>
  <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css">
  <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
  <script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
</head>

<body>
  <div id="map" style="width:600px; height:600px;"></div>
  <script>
    var map = new L.Map("map", {
        center: [37.8, -96.9],
        zoom: 4
      })
      .addLayer(new L.TileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"));

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

    d3.json("https://jsonblob.com/api/580256bbe4b0bcac9f7ffa43", function(error, myPoints) {
      if (error) throw error;

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

      var bounds = path.bounds(myPoints),
        topLeft = bounds[0],
        bottomRight = bounds[1];

      myPoints.features.forEach(function(d) {
        d.LatLng = new L.LatLng(d.geometry.coordinates[1],
          d.geometry.coordinates[0]);
      });

      var circles = svgCircles.selectAll("circle")
        .data(myPoints.features)
        .enter()
        .append("circle")
        .attr("r", 10)
        .style("fill", "red")
        .attr("fill-opacity", 0.5);

      // Use Leaflet to implement a D3 geometric transformation.
      function projectPoint(x, y) {
        var point = map.latLngToLayerPoint(new L.LatLng(y, x));
        this.stream.point(point.x, point.y);
      }

      function update() {
        circles.attr("cx", function(d) {
          return map.latLngToLayerPoint(d.LatLng).x;
        });
        circles.attr("cy", function(d) {
          return map.latLngToLayerPoint(d.LatLng).y;
        });
        svg.attr("width", bottomRight[0] - topLeft[0])
          .attr("height", bottomRight[1] - topLeft[1])
          .style("left", topLeft[0] + "px")
          .style("top", topLeft[1] + "px");

        svgCircles.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
      }

      map.on("viewreset", update);
      update();

    })
  </script>
</body>

</html>

RESPONSE TO COMMENTS

Because I limited the data to a smaller subset of cities (those in NY and PA) the bounds are only calculated to what's in the data. Here it is with all the cities in your JSON (wait a bit for it to load).

And a quick screenshot with all the data:



来源:https://stackoverflow.com/questions/40047326/overlaying-circles-on-leaflet-with-d3-results-in-not-positioned-properly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!