• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

Zwei Knoten nacheinander auswählen und Pfad dazwischen anzeigen.

milesdavis

New member
Guten Tag liebe Freunde,

ich habe folgendes Anliegen mit dem Force-Layout von d3.js:

Die Grundlagen zum Force-Layout habe ich bereits verstanden und auch bereits Algorithmen gefunden, um zb. direkte Nachbarn von angeklickten Knoten hervorzuheben.

Nun würde ich gern zwei Knoten auswählen können und mir den kompletten Weg zwischen den beiden Knoten anzeigen lassen, also auch andere Knoten, die zwischen den beiden ausgewählten Knoten liegen.

Nun weiss ich nicht so ganz, wie ich vorgehen soll. Es scheitert schon daran, dass ich mit "click" nur einen einzigen Knoten anklicken kann und danach den nächsten.

Ich würde aber gern, dass man quasi einen Knoten a anklickt und sich dadurch ein Speicherzustand einstellt, der sich merkt, dass dieser Knoten a geklickt wurde, und man schliesslich den zweiten Knoten b anklicken kann. Gibt es dafür einen speziellen Algorithmus? Beim hervorheben des Weges zwischen den Knoten hätte ich an Dijkstra gedacht...

Vielen dank für eure Hilfe!
Gruss
 
na was genau ist denn deine frage?
keiner wird dir hier dein problem lösen, aber antworten auf konkrete fragen kannst du gerne bekommen.

Es scheitert schon daran, dass ich mit "click" nur einen einzigen Knoten anklicken kann und danach den nächsten.
man kann ja nur "einen einzigen Knoten anklicken"
Ich würde aber gern, dass man quasi einen Knoten a anklickt und sich dadurch ein Speicherzustand einstellt, der sich merkt, dass dieser Knoten a geklickt wurde, und man schliesslich den zweiten Knoten b anklicken kann. Gibt es dafür einen speziellen Algorithmus?
wie einen "speziellen Algorithmus" um sich etwas zu merken? du hast ein datenmodell und die grafische repräsentation des modelles. jetzt könnte man sich den zustand start und ende zwar auch über eine klasse in der grafischen repräsentation merken, aber besser wäre du merkst dir das in deinem datenmodell
 
So, ich habe nun längere Zeit geforscht und ein Beispiel gefunden, bei dem sich mehrere Nodes anklicken lassen, indem man die Shift-Taste hält. darauf aufbauend werde ich mir wohl meine Funktion selbst basteln.
D3 Selectable Force-Directed Graph - bl.ocks.org

Meine Frage dazu wären ein paar Codeschnipsel, die ich nicht ganz verstehe. Die Funktion, die es erlaubt, Shift zu halten und dabei mehrere Nodes auszuwählen ist ja diese:

Code:
 function keydown() {
        shiftKey = d3.event.shiftKey || d3.event.metaKey;
        ctrlKey = d3.event.ctrlKey;

        console.log('d3.event', d3.event)

        if (d3.event.keyCode == 67) {   //the 'c' key
            center_view();
        }

        if (shiftKey) {
            svg_graph.call(zoomer)
            .on("mousedown.zoom", null)
            .on("touchstart.zoom", null)                                                                      
            .on("touchmove.zoom", null)                                                                       
            .on("touchend.zoom", null);                                                                       

            //svg_graph.on('zoom', null);                                                                     
            vis.selectAll('g.gnode')
            .on('mousedown.drag', null);

            brush.select('.background').style('cursor', 'crosshair')
            brush.call(brusher);
        }
    }


Ich verstehe nicht ganz, wo in dieser Funktion die Möglichkeit gegeben wird, Shift zu halten und die Nodes auszuwählen.

Kann mir einer von euch guten Leuten helfen?
Danke euch!
 
Ich verstehe nicht ganz, wo in dieser Funktion die Möglichkeit gegeben wird, Shift zu halten und die Nodes auszuwählen.
die funktion, welche es erlaubt mehrere knoten auszuwählen ist diese:

Code:
.on("click", function(d) {
            if (d3.event.defaultPrevented) return;

            if (!shiftKey) {
                //if the shift key isn't down, unselect everything
                node.classed("selected", function(p) { return p.selected =  p.previouslySelected = false; })
            }

            // always select this node
            d3.select(this).classed("selected", d.selected = !d.previouslySelected);
        })

hier werden sich die knoten über eine klasse gemerkt.
du brauchst ja das mit dem shift icht zu machen, du willst doch einfach nur 2 knoten haben. sprich beim 1. klick ist der knoten dein startknoten, beim 2. der endknoten, beim 3. ist der vom 2. klick dein start, der vom 3. dein ende usw.

- - - Aktualisiert - - -

mal das beispiel hier http://mbostock.github.io/d3/talk/20110921/#1 mit diesen daten: view-source:http://mbostock.github.io/d3/talk/20110921/flare.json
Code:
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>Force Layouts</title>
    <style>
      rect {
        fill: none;
        pointer-events: all;
      }
    </style>
    <script type="text/javascript" src="d3.js"></script>
  </head>
  <body>
    <div id="body">
      <div id="chart"></div>
    </div>
    <script type="text/javascript">
      var w = 1280,
          h = 800,
          z = d3.scale.category20c();
      
      var force = d3.layout.force()
          .size([w, h]);
      
      var svg = d3.select("#chart").append("svg:svg")
          .attr("width", w)
          .attr("height", h);
      
      svg.append("svg:rect")
          .attr("width", w)
          .attr("height", h);
      
      d3.json("flare.json", function(root)
      {
        var nodes = flatten(root),
            links = d3.layout.tree().links(nodes);
      
        force
          .nodes(nodes)
          .links(links)
          .start();
            
        var points = [];
      
        var link = svg.selectAll("line")
          .data(links)
          .enter().insert("svg:line")
            .style("stroke", "#999")
            .style("stroke-width", "1px");
      
        var node = svg.selectAll("circle.node")
          .data(nodes)
          .enter().append("svg:circle")
            .attr("r", 4.5)
            .style("fill", function(d) { return z(d.parent && d.parent.name); })
            .style("stroke", "#000")
            .call(force.drag)
            .on("click", function(d)
            {
              points.push(d);
              if (points.length > 2)
              {
                points.shift();
              }
              if (points.length > 1)
              {
                console.log("start: ", points[0].name, "end: ", points[1].name)
              }
            });
            
        force.on("tick", function(e)
        {
          link.attr("x1", function(d) { return d.source.x; })
              .attr("y1", function(d) { return d.source.y; })
              .attr("x2", function(d) { return d.target.x; })
              .attr("y2", function(d) { return d.target.y; });
      
          node.attr("cx", function(d) { return d.x; })
              .attr("cy", function(d) { return d.y; });
        });
      });
      
      function flatten(root)
      {
        var nodes = [];
        function recurse(node, depth)
        {
          if (node.children)
          {
            node.children.forEach(function(child)
            {
              child.parent = node;
              recurse(child, depth + 1);
            });
          }
          node.depth = depth;
          nodes.push(node);
        }
        recurse(root, 1);
        return nodes;
      }
    </script>
  </body>
</html>

- - - Aktualisiert - - -

den dijkstra algorithmus findest du hier: http://bl.ocks.org/sdjacobs/3900867adc06c7680d48.
auf die schnelle hab ich nur den gefunden, da der typ aber ein algorithmen fetischist ist, findest du bestimmt auch noch einen welcher nicht nur den baum durchrechnet, sondern sich auch die knoten merkt um aus diesen den kürzesten weg zu bestimmen. ansonsten ist das aber auch schnell geändert.
 
Hey, danke dir erstmal.
Was möchtest du mir mit deinem Beispiel zeigen? Habe es geöffnet, aber kann dort keine 2 Nodes gleichzeitig auswählen.

- - - Aktualisiert - - -
 
Zuletzt bearbeitet:
ach und in der v4 ist auch schon eine pfadberechnung enthalten, keine ahnung welche version du nutzt
Code:
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>Force Layouts</title>
    <style>
      rect {
        fill: none;
        pointer-events: all;
      }
    </style>
    <script type="text/javascript" src="d3.v4.js"></script>
  </head>
  <body>
    <div id="body">
      <div id="chart"></div>
    </div>
    <script type="text/javascript">

var w = 1280,
    h = 800,
    z = d3.scaleOrdinal(d3.schemeCategory20c);

var simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody())
    .force("link", d3.forceLink().distance(20).strength(1))
    .force("x", d3.forceX())
    .force("y", d3.forceY())
    .force("center", d3.forceCenter(w / 2, h / 2));

var svg = d3.select("#chart").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

svg.append("svg:rect")
    .attr("width", w)
    .attr("height", h);

d3.json("flare.json", function(root) {
  var hierarchy = d3.hierarchy(root),
      links = hierarchy.links()
      nodes = hierarchy.descendants();

  console.log(nodes, links);
  
  var startNode = null;
  
  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(links)
    .enter().append("line")
      .style("stroke", "#999")
      .style("stroke-width", "1px");

  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes)
    .enter().append("circle")
      .attr("r", 4.5)
      .style("fill", function(d) { return z(d.parent && d.parent.data.name); })
      .style("stroke", "#000")
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended))
        .on("click", function(d, i) 
        {
          var path = startNode && startNode.path(d) || [];
          startNode = d;
          link.style("stroke", function(d) 
          {
            return path.includes(d.source) && path.includes(d.target) ? "#900" : "#999";
          });
          node.attr("r", function(d) 
          {
            return path.includes(d) || startNode == d ? 10 : 4.5;
          });
        });

  simulation
      .nodes(nodes)
      .on("tick", function()
      {
        link
          .attr("x1", function(d) { return d.source.x; })
          .attr("y1", function(d) { return d.source.y; })
          .attr("x2", function(d) { return d.target.x; })
          .attr("y2", function(d) { return d.target.y; });
    
        node
          .attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
      });

  simulation.force("link")
      .links(links);
  
});

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

    </script>
  </body>
</html>
in v4 werden auch die knoten nicht mehr erweitert, sondern ein wrapper drumgelegt

- - - Aktualisiert - - -

Hey, danke dir erstmal.
Was möchtest du mir mit deinem Beispiel zeigen? Habe es geöffnet, aber kann dort keine 2 Nodes gleichzeitig auswählen.
doch, nur nicht hervorgehoben, in der console stehen dann die namen
 
Warte mal, was machst du in diesem Beispiel genau?
D3.js
du hast den code aus #5 in ein html kopiert? die flare.json datei ins selbe verzeichnis kopiert? eine d3.js (version 3) auch?
dann klicke nacheinander ein paar knoten an. die namen der knoten sollten mit start und ende kennzeichnung in der konsole erscheinen.

- - - Aktualisiert - - -

ich habs nochmal in ein zip geschmissen. Anhang anzeigen test.zip
* einmal start- und endpunkt aus #5
* pfadberechnung mit path aus d3 version 4
* den angepassten dijkstra-algorithmus(ich lasse immer den ganzen baum durchrechnen, eigentlich kannst du beim zielknoten abbrechen - zeile 181 den zielknoten mit übergeben)
 
Hallo,
ich bin mir nicht sicher, welche version von d3 ich nehme, aber ich binde quasi d3 immer mit

Code:
 <script src="http://d3js.org/d3.v3.js"></script>
ein, also müsste es ja quasi v3 sein.

und nochmal eine kleine nachfrage: in deinem zip sind ja 3 programme...hast du mir die gesendet, um zu zeigen, dass es bessere vorschläge gibt, als das programm, welches ich gefunden habe? also das mit dem anklicken durch shift?

und zu deinen files selbst:

bei "d3 v4 path" und "dijkstra" :
kann man sich ja quasi immer den weg zwischen den geklickten nodes anzeigen lassen und zwar grafisch durch den roten gekennzeichneten pfad. da würde mir "d3 v4 path" besser zum lernen für mein beispiel gefallen, da hier die nodes, die zwischen den beiden angeklickten nodes sind, schön fett hervorgehoben werden.

"select start end" finde ich auch ganz gut!

aber jedoch bei "dijkstra": die nodes laden sich immer ewig neu und erst danach wird der pfad angezeigt...ist das so bei "dijkstra" gewollt? was hat das denn für einen sinn? meine anfangsidee mit dijkstra werde ich wohl verwerfen, das schaut mir im code auch etwas kompliziert aus.

Danke erstmal für deine hilfe, werde mir jetzt die codes angucken und bei fragen mich einfach melden :)
 
Zuletzt bearbeitet:
danke dir! habe auch schon die erste frage bezüglich "d3 v4 path":
Und zwar ist der code, mit dem man 2 nodes anklicken kann und der path zwischen den angeklickten nodes erscheint dieser:

Code:
  .on("click", function(d, i) 
              {
                var path = startNode && startNode.path(d) || [];
                startNode = d;
                link.style("stroke", function(d) 
                {
                  return path.includes(d.source) && path.includes(d.target) ? "#900" : "#999";
                });
                node.attr("r", function(d) 
                {
                  return path.includes(d) || startNode == d ? 10 : 4.5;
                });
              });


eine frage zu dieser stelle:
Code:
var path = startNode && startNode.path(d) || [];
                startNode = d;


er definiert eine variable path als zusammensetzung von
Code:
startNode und startNode.path(d) || []

nun verstehe ich diesen teil leider nicht:
Code:
 startNode.path(d) || []

also er ruft mit startNode quasi path(d) auf ODER ein leeres Array?
 
Zuletzt bearbeitet:
Ja, danke dir :)
Ich verstehe nur nicht, was
Code:
 var path =  startNode && startNode.path(d)
bedeuten soll (angenommen, startNode ist nicht undefiniert)

soll startNode.path(d) der endknoten sein? Also quasi der knoten, der als zweites nach startNode angeklickt wurde?
 
das kannst du doch ganz einfach ausprobieren
Code:
var d = "d";

// angenommen startNode ist null
var startNode = null;
var path =  startNode && startNode.path(d);
console.log("startNode ist null: ", path);

// angenommen startNode ist nicht null
startNode = {
  path: function(d)
  {
    return d;
  }
};
path =  startNode && startNode.path(d);
console.log("startNode ist nicht null: ", path);

// dort steht aber var path =  startNode && startNode.path(d) || [];
// angenommen startNode ist null
startNode = null;
path =  startNode && startNode.path(d) || [];
console.log("startNode ist wieder null: ", path);

// wieder mit startNode != null
startNode = {
  path: function(d)
  {
    return d;
  }
};
path =  startNode && startNode.path(d) || [];
console.log("startNode ist wieder nicht null: ", path);

// zum vergleich
if (startNode)
{
  if (!(path =  startNode.path(d)))
  {
    path = [];
  }
}
else
{
  path = [];
}
console.log(path);
 
den hier, wo kommt der plötzlich her?

Code:
var d = "d";

// angenommen startNode ist null
var startNode = null;
var path =  startNode && startNode.path(d);
console.log("startNode ist null: ", path);

// angenommen startNode ist nicht null
startNode = {
  path: function(d)
  {
    return d;
  }
};
path =  startNode && startNode.path(d);
console.log("startNode ist nicht null: ", path);

// dort steht aber var path =  startNode && startNode.path(d) || [];
// angenommen startNode ist null
startNode = null;
path =  startNode && startNode.path(d) || [];
console.log("startNode ist wieder null: ", path);

// wieder mit startNode != null
startNode = {
  path: function(d)
  {
    return d;
  }
};
path =  startNode && startNode.path(d) || [];
console.log("startNode ist wieder nicht null: ", path);

// zum vergleich
if (startNode)
{
  if (!(path =  startNode.path(d)))
  {
    path = [];
  }
}
else
{
  path = [];
}
console.log(path);
 
Nochmal zum verständnis zu
Code:
 var path =  startNode && startNode.path(d)

- wenn startNode = 0, dann ist path = 0 && startNode.path(d) = 0

- wenn als Beispielwert startNode = 1, dann ist path = 1 && startNode.path(d) = was genau?
 
Zuletzt bearbeitet:
Zurück
Oben