private void computeDelays(AfterMobsimEvent event) {

    TravelTime travelTime = event.getServices().getLinkTravelTimes();
    int timeBinSize =
        this.congestionInfo.getScenario().getConfig().travelTimeCalculator().getTraveltimeBinSize();

    for (Link link : this.congestionInfo.getScenario().getNetwork().getLinks().values()) {

      Map<Integer, Double> time2avgDelay = new HashMap<>();
      double freespeedTravelTime = link.getLength() / link.getFreespeed();

      int timeBinCounter = 0;
      for (int endTime = timeBinSize;
          endTime
              <= this.congestionInfo.getScenario().getConfig().travelTimeCalculator().getMaxTime();
          endTime = endTime + timeBinSize) {
        double avgDelay =
            travelTime.getLinkTravelTime(link, (endTime - timeBinSize / 2.), null, null)
                - freespeedTravelTime;
        time2avgDelay.put(timeBinCounter, avgDelay);
        timeBinCounter++;
      }

      if (this.congestionInfo.getlinkInfos().containsKey(link.getId())) {
        this.congestionInfo.getlinkInfos().get(link.getId()).setTime2avgDelay(time2avgDelay);
      } else {
        LinkInfo linkInfo = new LinkInfo(link.getId());
        linkInfo.setTime2avgDelay(time2avgDelay);
        this.congestionInfo.getlinkInfos().put(link.getId(), linkInfo);
      }
    }
  }
  private void relaxNode(final Node n, PriorityQueue<Node> pendingNodes) {
    NodeData nData = nodeData.get(n.getId());
    double currTime = nData.getTime();
    double currCost = nData.getCost();
    for (Link l : n.getOutLinks().values()) {
      Node nn = l.getToNode();
      NodeData nnData = nodeData.get(nn.getId());
      if (nnData == null) {
        nnData = new NodeData();
        this.nodeData.put(nn.getId(), nnData);
      }
      double visitCost =
          currCost + tcFunction.getLinkTravelDisutility(l, currTime, PERSON, VEHICLE);
      double visitTime = currTime + ttFunction.getLinkTravelTime(l, currTime, PERSON, VEHICLE);

      if (visitCost < nnData.getCost()) {
        pendingNodes.remove(nn);
        nnData.visit(n.getId(), visitCost, visitTime);
        additionalComputationsHook(l, currTime);
        pendingNodes.add(nn);
      }
    }
  }
  @Override
  public Path calcLeastCostPath(
      Node fromNode, Node toNode, double starttime, Person person, Vehicle vehicle) {

    List<Node> nodes = new ArrayList<>();
    List<Link> links = new ArrayList<>();
    double travelTime = starttime;
    double travelCost = 0;
    Node currentNode = fromNode;
    if (networkPainter != null) {
      networkPainter.addForegroundPixel(fromNode.getId());
      networkPainter.addForegroundPixel(toNode.getId());
    }
    while (currentNode != toNode) {
      Object[] outLinks = currentNode.getOutLinks().values().toArray();
      // come up witha set of costs based on how much nearer (eucl) the link brings you to your
      // destination,
      // use them as probabilities of selecting the link
      double[] cumulativeWeights = new double[outLinks.length];
      double[] weights = new double[outLinks.length];
      double[] dists = new double[outLinks.length];
      double minCost = Double.POSITIVE_INFINITY;
      double maxCost = 0;
      Link selection = null;
      for (int i = 0; i < outLinks.length; i++) {
        double linkCost =
            CoordUtils.calcEuclideanDistance(
                toNode.getCoord(), ((Link) outLinks[i]).getToNode().getCoord());
        if (((Link) outLinks[i])
            .getToNode()
            .equals(toNode)) { // found destination, stop mucking about
          selection = (Link) outLinks[i];
          break;
        }
        dists[i] = linkCost;
        minCost = Math.min(minCost, linkCost);
        maxCost = Math.max(maxCost, linkCost);
      }
      for (int i = 0; i < outLinks.length; i++) {
        double linkCost = dists[i] - minCost;
        if (links.contains(outLinks[i]) || nodes.contains(((Link) outLinks[i]).getToNode())) {
          linkCost = 1e6 * (maxCost - minCost);
        }
        linkCost = Math.exp(-beta * linkCost);
        linkCost = linkCost == 1 ? 2 : linkCost;
        cumulativeWeights[i] = i > 0 ? cumulativeWeights[i - 1] + linkCost : linkCost;
        weights[i] = linkCost;
      }
      if (selection == null) {
        // sample if the destination has not been found
        double sampleProb =
            MatsimRandom.getRandom().nextDouble() * cumulativeWeights[cumulativeWeights.length - 1];
        for (int i = 0; i < outLinks.length; i++) {
          if (cumulativeWeights[i] >= sampleProb) {
            selection = (Link) outLinks[i];
            links.add(selection);
            nodes.add(currentNode);
            travelCost +=
                travelCosts.getLinkTravelDisutility(selection, travelTime, person, vehicle);
            travelTime += travelTimes.getLinkTravelTime(selection, travelTime, person, vehicle);
            break;
          }
        }
      }
      if (networkPainter != null) {
        networkPainter.addForegroundLine(selection.getId());
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      currentNode = selection.getToNode();
    }
    if (networkPainter != null) {
      try {
        Thread.sleep(500);
        networkPainter.clearForeground();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    return new Path(nodes, links, travelTime, travelCost);
  }