Overlaying circles on leaflet with d3 results in not positioned properly

Stophface

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: leafletmap

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

Mark

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:

enter image description here

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related