title: D3 + Leaflet: d3.geo.path() resampling
body: <p>We've adapted Mike Bostock's original D3 + Leaflet example:
  <a href="http://bost.ocks.org/mike/leaflet/" rel="noreferrer">http://bost.ocks.org/mike/leaflet/</a>
  so that it does not redraw all paths on each zoom in Leaflet.</p>
  
  <p>Our code is here: <a href="https://github.com/madeincluj/Leaflet.D3/blob/master/js/leaflet.d3.js" rel="noreferrer">https://github.com/madeincluj/Leaflet.D3/blob/master/js/leaflet.d3.js</a></p>
  
  <p>Specifically, the projection from geographical coordinates to pixels happens here: 
  <a href="https://github.com/madeincluj/Leaflet.D3/blob/master/js/leaflet.d3.js#L30-L35" rel="noreferrer">https://github.com/madeincluj/Leaflet.D3/blob/master/js/leaflet.d3.js#L30-L35</a></p>
  
  <p>We draw the SVG paths on the first load, then simply scale/translate the SVG to match the map.</p>
  
  <p>This works very well, except for one issue: D3's path resampling, which looks great at the first zoom level, but looks progressively more broken once you start zooming in.</p>
  
  <p>Is there a way to disable the resampling?</p>
  
  <p><em>As to why we're doing this:</em> We want to draw a lot of shapes (thousands) and redrawing them all on each zoom is impractical.</p>
  
  <p><strong>Edit</strong>
  After some digging, seems that resampling happens here:</p>
  
  <pre><code>function d3_geo_pathProjectStream(project) {
     var resample = d3_geo_resample(function(x, y) {
       return project([ x * d3_degrees, y * d3_degrees ]);
     });
    return function(stream) {
      return d3_geo_projectionRadians(resample(stream));
    };
  }
  </code></pre>
  
  <p>Is there a way to skip the resampling step?</p>
  
  <p><strong>Edit 2</strong> </p>
  
  <p>What a red herring! We had switched back and forth between sending a raw function to <code>d3.geo.path().projection</code> and a <code>d3.geo.transform</code> object, to no avail. </p>
  
  <p>But in fact the problem is with leaflet's <code>latLngToLayerPoint</code>, which (obviously!) rounds point.x &amp; point.y to integers. Which means that the more zoomed out you are when you initialize the SVG rendering, the more precision you will lose.</p>
  
  <p>The solution is to use a custom function like this:</p>
  
  <pre><code>function latLngToPoint(latlng) {
    return map.project(latlng)._subtract(map.getPixelOrigin());
  };
  
  var t = d3.geo.transform({
      point: function(x, y) {
        var point = latLngToPoint(new L.LatLng(y, x));
        return this.stream.point(point.x, point.y);
      }
    });
  
  this.path = d3.geo.path().projection(t);
  </code></pre>
  
  <p>It's similar to leaflet's own <code>latLngToLayerPoint</code>, but without the rounding. (Note that <code>map.getPixelOrigin()</code> is rounded as well, so probably you'll need to rewrite it)</p>
  
  <p>You learn something every day, don't you.</p>