All examples By author By category About

timelyportfolio

flubberized US states to/from hex


This is a combination of quite a few numerous generous open-source contributions. Thanks especially to Mike Bostock, Noah Veltman, Bob Rudis, and Andy Teucher.

Code in R

library(d3r)
library(albersusa)
library(sf)
library(geojsonio)
library(rmapshaper)
library(htmltools)

tl <- tagList(
  tags$head(tags$script(src="https://unpkg.com/flubber")),
  tag("svg",list(id="map",style="height:400px; width:900px;")),
  tags$script(HTML(
sprintf(
"
  var us_geo = %s;
  var us_hex = %s;
  var projection = d3.geoAlbersUsa();
  projection.fitSize([450,400], us_geo);
  var path = d3.geoPath().projection(projection);
  var path_hex = d3.geoPath()
    .projection(d3.geoMercator().fitSize([450,400], us_hex));
  var svg = d3.select('#map');

  var states = svg.append('g')
    .attr('class','states')
    .selectAll('path')
    .data(us_geo.features)
    .enter()
      .append('path')
      .attr('d', path)
      .style('stroke', 'black')
      .style('fill', 'none')
      .style('pointer-events', 'all');

  var hexstates = svg.append('g')
    .attr('class', 'hex')
    .style('transform', 'translate(450px,0)')
    .selectAll('path')
    .data(us_hex.features)
    .enter()
      .append('path')
      .attr('d', path_hex)
      .style('stroke', 'black')
      .style('fill', 'none')
      .style('pointer-events', 'all');

  states.on('click', function(d) {
    var path_strings = flubber.splitPathString(path(d));

    var hex_path = hexstates.nodes()
          .filter(function(hd) {
            return d.properties.iso_3166_2 === d3.select(hd).datum().properties.iso3166_2
          })[0]
          .getAttribute('d');

    if(path_strings.length == 1) {
      var interpolator = flubber.interpolate(
        d3.select(this).attr('d'),
        hex_path
      );
    } else {
      var interpolator = flubber.combine(
        path_strings,
        hex_path,
        {single: true}
      );
    }

    var cloned = d3.select(
      d3.select('g.states')
        .node()
        .appendChild(this.cloneNode(true))
    );

    cloned
      .transition()
      .duration(2000)
      .attr('transform', 'translate(450,0)')
      .style('stroke', 'red')
      .style('stroke-width', '3px')
      .attrTween('d', function(){ return interpolator; })
      .transition()
      .style('opacity',0.001)
      .remove();
  })

  hexstates.on('click', function(d) {

    
    var state_path = states.nodes()
      .filter(function(hd) {
        return d.properties.iso3166_2 === d3.select(hd).datum().properties.iso_3166_2;
      })[0]
      .getAttribute('d');

    var path_strings = flubber.splitPathString(state_path);
    
    if(path_strings.length == 1) {
      var interpolator = flubber.interpolate(
        d3.select(this).attr('d'),
        state_path
      );
    } else {
      var interpolator = flubber.separate(
        d3.select(this).attr('d'),
        path_strings,
        {single: true}
      );
    }
    
    var cloned = d3.select(
      d3.select('g.hex')
        .node()
        .appendChild(this.cloneNode(true))
      );
    
    cloned
      .transition()
      .duration(2000)
      .attr('transform', 'translate(-450,0)')
      .style('stroke', 'red')
      .style('stroke-width', '3px')
      .attrTween('d', function(){ return interpolator; })
      .transition()
      .style('opacity',0.001)
      .remove();
  })
",
ms_simplify(geojson_json(usa_sf()), keep=0.03),
paste0(readLines("us_states_hexgrid.geojson"), collapse = "\n")
)
  )),
  d3_dep_v4(offline=FALSE)
)

browsable(tl)