Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make d3 concurrent transitions work?

I've started doing this as a side project to learn D3. So please go easy on me;

Goal: Making an interactive airports map. When mouse is over a city planes will take off from that city's airport and land on to the destination.

Steps I've followed;

  1. I've downloaded country and airports data as geojson and defined base map on D3.

basemap

  1. I've write necessary functions to derive spatial information to use in transitions. You can view code from here if you have time.
  2. I've added transitions to planes.

transitions

Problem: When I have more than one plane to take from a city it's just not moving. Instead it stays on the same position until the transition ends. I can't add concurrent transitions to the objects.

error

My transition function is like;

function myTransition(destPoi, originPoi) {
var tr_bl = true;
pathData = []; t_dest_poi = [];
if (tr_bl) {
//Origin destination coordinates taken from the Origin Poi
originPoint[0] = originPoi[1];
originPoint[1] = originPoi[2];
originPoint = geoMercator(originPoint);//Need transformation before using on screen.
var lineGenerator = d3.line().curve(d3.curveCatmullRom);
// Destination coordinate pairs transformed to screen coordinates
// and assigned to an array 
for (i = 0; i < destPoi.length; i++) {
  t_dest_poi[i] = geoMercator(destPoi[i]);
}
//appending new group g2 with id fp 
var g2 = map_svg.append("g").attr("id", "fp");

// Creating the planes and flight paths programmatically.*************************************
for (i = 0; i < destPoi.length; i++) {
  var plane = map_svg.append("path")
    .attr("class", "plane")
    .attr("d", "m25.21488,3.93375c-0.44355,0 -0.84275,0.18332 -1.17933,0.51592c-0.33397,0.33267 -0.61055,0.80884 -0.84275,1.40377c-0.45922,1.18911 -0.74362,2.85964 -0.89755,4.86085c-0.15655,1.99729 -0.18263,4.32223 -0.11741,6.81118c-5.51835,2.26427 -16.7116,6.93857 -17.60916,7.98223c-1.19759,1.38937 -0.81143,2.98095 -0.32874,4.03902l18.39971,-3.74549c0.38616,4.88048 0.94192,9.7138 1.42461,13.50099c-1.80032,0.52703 -5.1609,1.56679 -5.85232,2.21255c-0.95496,0.88711 -0.95496,3.75718 -0.95496,3.75718l7.53,-0.61316c0.17743,1.23545 0.28701,1.95767 0.28701,1.95767l0.01304,0.06557l0.06002,0l0.13829,0l0.0574,0l0.01043,-0.06557c0,0 0.11218,-0.72222 0.28961,-1.95767l7.53164,0.61316c0,0 0,-2.87006 -0.95496,-3.75718c-0.69044,-0.64577 -4.05363,-1.68813 -5.85133,-2.21516c0.48009,-3.77545 1.03061,-8.58921 1.42198,-13.45404l18.18207,3.70115c0.48009,-1.05806 0.86881,-2.64965 -0.32617,-4.03902c-0.88969,-1.03062 -11.81147,-5.60054 -17.39409,-7.89352c0.06524,-2.52287 0.04175,-4.88024 -0.1148,-6.89989l0,-0.00476c-0.15655,-1.99844 -0.44094,-3.6683 -0.90277,-4.8561c-0.22699,-0.59493 -0.50356,-1.07111 -0.83754,-1.40377c-0.33658,-0.3326 -0.73578,-0.51592 -1.18194,-0.51592l0,0l-0.00001,0l0,0z")
    .style("opacity", 1)
    .style("fill", "transparent");
  //creating flight path data with line generator
  pathData[i] = lineGenerator([originPoint, t_dest_poi[i]]);
  //appending created flight path to variable 
  var path = g2.append("path").data([
    [originPoint],
    [t_dest_poi[i]]
  ]).attr("d", d3.line());
//flight path
  g2.selectAll('path')
    .data([
      [originPoint],
      [t_dest_poi[i]]
    ])
    .attr('d', pathData)
    .attr("stroke", "blue")
    .attr("fill", "transparent");

  plane.style("fill", "grey");

  var route = g2.append("path")
    .datum({
      type: "LineString", coordinates: [
        [originPoint],
        [t_dest_poi[i]]]
    })
    .attr("class", "route")
    .attr("d", geoPath)
    .attr("stroke", "blue")
    .attr("fill", "transparent");

  // adding transition to plane
  plane.transition()
    .duration(5000)
    .attrTween("transform", translateAlong([originPoint, t_dest_poi[i]], path.node()))
    .remove();
    }
}
else {
}}

function translateAlong(co, path) {
var l = path.getTotalLength();
return function (d, i, a) {
return function (t) {
  var p = path.getPointAtLength(t * l);
  [a1, b1] = co[0];
  [a2, b2] = co[1];
  aci = Math.atan((b2 - b1) / (a2 - a1));
  aci = aci * 180 / Math.PI;
  if (a2 > a1) {
    aci = aci + 90;
  } else {
    aci = aci - 90;
  }
  return "translate(" + (p.x) + "," + (p.y) + ") scale(" + Math.sin(Math.PI * t) + ") rotate(" + aci + ")";
};
};
}
like image 910
Capan Avatar asked Jul 03 '26 20:07

Capan


1 Answers

This seems like an issue with updating the path of the #fp element during the transition. The relatively easy fix in this case is to handle the transitions separately.

In your code, in basemap.js circa line 58-63, you call myTransition() on an array of multiple elements after populating the array in a for loop.

for (i = 0; i <= n - 1; i++) {
  fp[i] = [destAirportObjects[i][1], destAirportObjects[i][2]];
}
myTransition(fp, OriginAirport);

Call the function from within the for loop instead and your transition works fine:

for (i = 0; i <= n - 1; i++) {
  fp[i] = [destAirportObjects[i][1], destAirportObjects[i][2]];
    myTransition([fp[i]], OriginAirport);
}

If you choose this solution, you should update myTransition() to take a single set of coordinates to make the code easier to understand.

Alternatively, don't make any changes to the basemap.js file, but update myTransition so that the creation of the group (var g2) is completed within the destination for loop. Here's what you currently have in move.js starting at line 21:

var g2 = map_svg.append("g").attr("id", "fp");

// Creating the planes and flight paths programmatically.*************************************
for (i = 0; i < destPoi.length; i++) {
  //creating flight path data with line generator
  pathData[i] = lineGenerator([originPoint, t_dest_poi[i]]);
  //continue with loop

This is causing the g2 variable to be overwritten each time we move through the loop. There needs to be a distinct group created for each transition/path.

// Creating the planes and flight paths programmatically.*************************************
for (i = 0; i < destPoi.length; i++) {
  var g2 = map_svg.append("g").attr("class", "fp");
  //creating flight path data with line generator
  pathData[i] = lineGenerator([originPoint, t_dest_poi[i]]);
  //continue with loop

Finally, since there are multiple elements in this case, "fp" should be the class of var g2, not the ID. Remember to update your accessors accordingly.

Edit: Here's the sample for method #1 and method #2

like image 79
SilentStone Avatar answered Jul 06 '26 11:07

SilentStone



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!