Skip to content

Instantly share code, notes, and snippets.

@timelyportfolio
Created June 19, 2017 20:31
Show Gist options
  • Save timelyportfolio/c078f0e4de651e39f2249df77dad7f1b to your computer and use it in GitHub Desktop.
Save timelyportfolio/c078f0e4de651e39f2249df77dad7f1b to your computer and use it in GitHub Desktop.
R mapedit playback with d3 + flubber
license: mit

mapedit gets record functionality in 0.2.1, so I wanted to show it off by animating playback with d3.js and flubber.

Code in R

library(sf)
library(mapview)
library(mapedit)

#f <- editMap(leaflet() %>% addTiles())
f <- st_sf(
  st_as_sfc(
    "POLYGON((-135 -7.0137, -135 52.4828, -61.875 52.4828, -61.875 -7.0137, -135 -7.0137))"
  ),
  crs = 4326
)

ed <- editFeatures(f, record=TRUE, viewer=shiny::dialogViewer("edit"))
rec <- attr(ed, "recorder")


library(d3r)
library(htmltools)
library(geojsonio)

tl <- tagList(
  tags$head(tags$script(src="https://unpkg.com/flubber")),
  d3r::d3_dep_v4(offline = FALSE),
  tags$script(HTML(
sprintf(
"
var feat = %s;
var proj = d3.geoMercator().fitSize([800,400], feat[0]);
var path = d3.geoPath().projection(proj);

var svg = d3.select('body').append('svg')
  .style('height', 400)
  .style('width', 800)
  .classed('map', true);

var fpath = svg.append('g')
  .selectAll('path')
  .data([feat[0]])
    .enter()
    .append('path')
    .attr('d', path)
    .style('stroke', 'black')
    .style('fill', 'none')
    .style('pointer-events', 'all');

feat.slice(1).reduce(
  function(left,right,i) {
    var interpolator = flubber.interpolate(
      path(feat[i]),
      path(right)
    );
  
    return left
      .transition()
      .duration(2000)
      .attrTween('d', function(d) {return interpolator});
  },
  fpath
);
",
jsonlite::toJSON(
  Map(
    function(x){geojson_list(x$feature)},
    rec
  ),
  auto_unbox=TRUE,
  force=TRUE
)
)
  ))
)

browsable(tl)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<script src="https://unpkg.com/flubber"></script>
</head>
<body style="background-color:white;">
<script>
var feat = [{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"X_leaflet_id":49,"layerId":"1"},"geometry":{"type":"Polygon","coordinates":[[[-135,-7.0137],[-117.4219,24.2069],[-135,52.4828],[-61.875,52.4828],[-40.4297,24.2069],[-61.875,-7.0137],[-135,-7.0137]]]}}]},{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"X_leaflet_id":49,"layerId":"1"},"geometry":{"type":"Polygon","coordinates":[[[-135,-7.0137],[-154.6875,24.8466],[-135,52.4828],[-61.875,52.4828],[-79.8047,26.7456],[-61.875,-7.0137],[-135,-7.0137]]]}}]},{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"X_leaflet_id":49,"layerId":"1"},"geometry":{"type":"Polygon","coordinates":[[[-91.7578,9.7957],[-135,-7.0137],[-154.6875,24.8466],[-135,52.4828],[-117.0703,34.0162],[-61.875,52.4828],[-79.8047,26.7456],[-61.875,-7.0137],[-91.7578,9.7957]]]}}]},{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"X_leaflet_id":49,"layerId":"1"},"geometry":{"type":"Polygon","coordinates":[[[-135,-7.0137],[-61.875,52.4828],[-79.8047,26.7456],[-61.875,-7.0137],[-135,-7.0137]]]}}]}];
var proj = d3.geoMercator().fitSize([800,400], feat[0]);
var path = d3.geoPath().projection(proj);
var svg = d3.select('body').append('svg')
.style('height', 400)
.style('width', 800)
.classed('map', true);
var fpath = svg.append('g')
.selectAll('path')
.data([feat[0]])
.enter()
.append('path')
.attr('d', path)
.style('stroke', 'black')
.style('fill', 'none')
.style('pointer-events', 'all');
feat.slice(1).reduce(
function(left,right,i) {
var interpolator = flubber.interpolate(
path(feat[i]),
path(right)
);
return left
.transition()
.duration(2000)
.attrTween('d', function(d) {return interpolator});
},
fpath
);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment