Daniel81
Daniel81

Reputation: 111

d3js map, clipping geographic paths with bounding box?

I'm building map with d3js. I can render a USA state map from this geojson file easily, and plot point data over it as needed (not shown). But I would like to clip the visualization using a bounding box of long/lat values.

I'm not even sure at what stage or on what object to do the clipping, generally. There is some minimalistic documentation about methods for clipping projections on the d3 API reference (for instance here). I am uncertain if clipping the projection would be helpful here.

Image of my map and the map output I would like - from Wikle et al. 2019 - are attached. My HTML skeleton is minimal, it calls up the js script, and provides a division for the svg ("svg1").

The code at the end of this message generates this map:

enter image description here

Filtering out Alaska and Hawaii is not a problem and not part of this question. I would like an image like the following (please ignore the point data, I am only curious about clipping the state paths down to size):

enter image description here

Here is my js code:

window.addEventListener("load", main)

function main(){
  // base svg and svg props
  const width = 400, height = 400;
  let facetWidth = width; 
  let facetHeight = height; 
  let svg1 = d3.select("#plot").append('svg')
    .style("width", width)
    .style("height", height)
    .attr("id", "svg1");

  // projection and generator
  let projection = d3.geoAlbersUsa();
  let geoGenerator = d3.geoPath().projection(projection);

  // state drawing function:
  function renderStates(data) {

    projection.fitSize([facetWidth, facetHeight], data); 

    svg1.append('g')
    .attr('id','statePaths')
    .attr('id','statePaths')
    .selectAll('path')
      .data(data.features)
      .join('path')
      .attr('d', geoGenerator)
      .attr('fill', '#ffffff')
      .attr('stroke', '#000')
    };

Any advice would be appreciated. I am still very new to rendering maps with d3js.

Upvotes: 3

Views: 706

Answers (1)

Daniel81
Daniel81

Reputation: 111

I'll post the answer to my own question, in case it can be useful, as there doesn't seem to be a lot of explicit examples for clipping with d3js outside of the observables community. Their examples are a bit specialized to their platform (though they are a great help!).

To clip the above down to an area of interest, I used the projection.postclip function, using d3.v7. My code is now too large to include here, but the following is the general pattern of use:

// set the standard d3js projection stuff: 
let projection = d3.geoEquirectangular()
  .translate([width/2, height/2])
  .scale(1000)
  .center([-89.04423584210527, 38.66203010526314])
   ;

// get your geographic coordinates that you would like to use as a bounding box. 

// In my case I am grabbing them from array called `stations` 
// using d3 accessor functions: 

let xMinLL = d3.min(stations, d => +d.lon);
let yMinLL = d3.min(stations, d => +d.lat);
let xMaxLL = d3.max(stations, d => +d.lon);
let yMaxLL = d3.max(stations, d => +d.lat);


// assign our pixel coordinates. Remember that y is flipped, up is down. 
[xMinPix, yMaxPix] = projection([xMinLL, yMinLL]);
[xMaxPix, yMinPix] = projection([xMaxLL, yMaxLL]);

// update your projection with the pixel coordinates:
const buff = 5;
projection.postclip(d3.geoClipRectangle(xMinPix - buff,
                                        yMinPix - buff,
                                        xMaxPix + buff,
                                        yMaxPix + buff));

Upvotes: 3

Related Questions