2
votes

I am attempting to render a map of Brooklyn's building and lots using D3.js. To accomplish this task, I have done the following:

  1. Obtained shapefile from MapPLUTO:

http://www.nyc.gov/html/dcp/html/bytes/dwn_pluto_mappluto.shtml.

  1. Converted shapefile to GeoJSON with the following ogr2ogr command:

ogr2ogr -f GeoJSON -lco COORDINATE_PRECISION=4 -select 'geometry, BBL' -s_srs EPSG:2263 -t_srs EPSG:4326 bk-gll1.json BKMapPLUTO.shp

  1. Transformed GeoJSON to TopoJSON with the following: topojson -o bkgll1-topojson.json bk-gll1.json

I've experimented with different projections, scales, and data bindings, but I've only managed to render either a black square that fills the SVG viewport, or nothing.

The TopoJSON and GeoJSON files are located here: https://github.com/RobertPTC/nyc_maps. Also included is a screenshot of the TopoJSON rendered through mapshaper.org

$(function() {
    var width = 960,
        height = 500;
    var svg = d3.select('body').append('svg')
        .attr('width', width)
        .attr('height', height)
    var projection = d3.geo.mercator()
             .rotate([96,0])
             .center([-73.98, 40.70])
             .scale(237000)
             .translate([width/2, height/2])
    var path = d3.geo.path()
                 .projection(projection)
    d3.json('bk-gll1-topojson.json', function(error, bk) {
             console.log(bk);
             var featureCollection = topojson.feature(bk, bk.objects['bk-gll1']);
             var bounds = d3.geo.bounds(featureCollection);

             svg.append('path')
                .datum(featureCollection)
                .attr('d', path)
 });
});

As a D3.js megafan, I would be tremendously appreciative if anyone could help solve this problem!

UPDATE

I seem to have results with this code:

$(function() {

var width = 1260,
  height = 1000;

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

var projection = d3.geo.mercator()
  .center([-73.98, 40.70])
  .scale(237000)
  .translate([width/2, height/2]);

var features = svg.append('g')
            .attr('class', 'features');
var path = d3.geo.path()
        .projection(projection);

var zoom = d3.behavior.zoom()
        .scaleExtent([1, Infinity])
        .on('zoom', zoomed);

d3.json('bk-gll1-topojson.json', function(error, bk) {
    console.log(bk);
var featureCollection = topojson.feature(bk, bk.objects['bk-gll1']);
var bounds = d3.geo.bounds(featureCollection);
console.log(featureCollection.features[0]);

features.selectAll('path')
  .data(featureCollection.features.slice(0, 241000))
  .enter()
  .append('path')
  .attr('d', path)
 });
function zoomed() {
console.log('zooming');
features.attr("transform", "translate(" + zoom.translate() + ")scale("  + zoom.scale() + ")")
    .selectAll("path").style("stroke-width", 1 / zoom.scale() + "px"      );
 };
});

However, I'm only able to render about a third of the 670000 elements in the featuresCollection array. Any advice as to how I can load the entire dataset without crashing the browser? Is it possible?

UPDATE 2

So, it would seem that load the data in incrementally helps...still a long load time, however, so probably not feasible for production environment:

features.selectAll('path')
  .data(featureCollection.features.slice(0, 241500))//after this number of elements point, map renders as giant black square
  .enter()
  .append('path')
  .attr('d', path)
  .on('click', clicked);
features.selectAll('path')
  .data(featureCollection.features.slice(241500, 246500))
  .enter()
  .append('path')
  .attr('d', path)
  .on('clicked', clicked);
1

1 Answers

0
votes

Have you tried rendering into canvas instead of SVG? That's still a lot of elements, and may still be quite slow, but at least you won't overload the DOM.