/*-------------------------------------------------------------------------*/
/*                                                                         */
/*                         Beleg - Neuronale Netze                         */
/*                                                                         */
/*                       TRAVELLING SALESMAN PROBLEM                       */
/*                  KOHONEN NETWORK (SELF ORGANIZING MAP)                  */
/*                                                                         */
/*                                                                         */
/*  Sven Boerner                                                           */
/*  98/041/02                                                              */
/*  htw9919                                                                */
/*                                                                         */
/*  file name:             TSP.java (Java Applet)                          */
/*                                                                         */
/*-------------------------------------------------------------------------*/

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;

/**
Class TSP is the applet itself describing the Travelling Salesman Problem
*/
public class TSP extends Applet implements ActionListener
{
  private Color     LightGrey = new Color(0xEEEEEE),
                    DarkGrey  = new Color(0xCCCCCC),
                    White     = new Color(0xFFFFFF),
                    Black     = new Color(0x000000),
                    Red       = new Color(0xFF0000);

  private int       numberOfCities, numberOfNeurons;
  private int       currentCycle, numberOfCycles;
  private double    etamin, etamax;
  private double    sigmin, sigmax;
  private double    time = 0.0, length = 0.0;
  private int       nextCity[], update;
  private long      timeBegin, timeEnd;
  private double    x[], y[], cities[];
  private double    weightSom[], weightWinner[];
  private static    Random r = new Random();
  private boolean   isAnimated = false;
  private boolean   doit = false;

  private Button    bStart, bNewMap,
                    bAnimation, bDefaultValues;
  private Label     lEvery, lCycles, lNumberOfCycles,
                    lNumberOfCities, lNumberOfNeurons,
                    lCurrentCycle, lCurrentCycleValue,
                    lEtamin, lEtamax, lSigmin, lSigmax,
                    lTime, lTimeValue, lLength, lLengthValue;
  private TextField tUpdate, tCycles, tCities, tNeurons,
                    tEtamin, tEtamax, tSigmin, tSigmax;

  private GO        myAnimator;
  private Image     zBuffer;
  private Thread    findRoute = null;
  private int       areaWidth, areaHeight;

  Graphics networkArea = this.getGraphics();
  DecimalFormat df = new DecimalFormat("0.000");

  void setDefaultUpdate()          { this.update          = 100;  }
  void setDefaultNumberOfCities()  { this.numberOfCities  = 120;  }
  void setDefaultNumberOfNeurons() { this.numberOfNeurons = 600;  }
  void setDefaultNumberOfCycles()  { this.numberOfCycles  = 3000; }
  void setDefaultEtamin()          { this.etamin = 0.3; }
  void setDefaultEtamax()          { this.etamax = 0.7; }
  void setDefaultSigmin()          { this.sigmin = 0.1; }
  void setDefaultSigmax()          { this.sigmax = 50.; }

  /**
  Setting Default Values
  */
  void setDefaultValues()
  {
    setDefaultUpdate();
    setDefaultNumberOfCities();
    setDefaultNumberOfNeurons();
    setDefaultNumberOfCycles();
    setDefaultEtamin();
    setDefaultEtamax();
    setDefaultSigmin();
    setDefaultSigmax();
  }

  /**
  Initializing the Kohonen Network
  */
  void initKohonenNetwork()
  {
    int i;
    double angle;

    x            = new double[numberOfCities];
    y            = new double[numberOfCities];
    nextCity     = new int   [numberOfCities];
    cities       = new double[2*numberOfCities];
    weightSom    = new double[2*numberOfNeurons];
    weightWinner = new double[2*numberOfNeurons];

    for(i=0; i<numberOfCities; i++)
    {
      cities[2*i] = Math.random();
      cities[2*i+1] = Math.random();
    }

    for(i=0; i<numberOfCities; i++)
    {
      x[i] = cities[2*i];
      y[i] = cities[2*i+1];
    }

    for(i=0; i<numberOfNeurons; i++)
    {
      angle = Math.PI * 2.0 * i / numberOfNeurons;
      weightSom[2*i] = 0.5 + Math.cos(angle) / 4;
      weightSom[2*i+1] = 0.5 + Math.sin(angle) / 4;
    }
    time = 0.0; length = 0.0;
  }

  /**
  Creates a new Probability List of the Cities
  */
  void distributeCities(int n)
  {
    int i, j, k, m;
    for(i=n-1; i>0; i--)
    {
      j = r.nextInt(i+1);
      k = nextCity[j];
      for(m=j; m<i; m++) nextCity[m] = nextCity[m+1];
      nextCity[i] = k;
    }
  }

  /**
  Gets the Winner Neuron
  */
  int winnerNeuron(int j)
  {
    int i, g = 0;
    double a, b, min, dif;
    for(i=0, min=1.0e+10; i<numberOfNeurons; i++)
    {
      a = x[j] - weightSom[2*i];
      b = y[j] - weightSom[2*i+1];
      dif = a * a + b * b;
      if(dif < min)
      {
        min = dif;
        g = i;
      }
    }
    return g;
  }

  /**
  Gets the Distance between two Points
  */
  double distance(int a, int b)
  {
    double deltaX, deltaY;

    deltaX = Math.abs(weightWinner[2*a] - weightWinner[2*b]);
    deltaY = Math.abs(weightWinner[2*a+1] - weightWinner[2*b+1]);

    return(Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)));
  }

  /**
  Gets the Length of the Route
  */
  double getLength()
  {
    double lengthOfRoute = 0.0;
    int k, l, m;
    for(k=0, l=0; k<numberOfNeurons-1; k++)
    {
      m = k + 1;
      lengthOfRoute += distance(l, m);
      l = m;
      lengthOfRoute += distance(0, l);
    }
    return lengthOfRoute;
  }

  /**
  Lerning Section for the SOM Net
  */
  void recalculateWeights()
  {
    int     i, j, m, winner, nextIndex, neighbor;
    double  h, eta, sig;

    for(i=0; i<numberOfCities; i++) nextCity[i] = i;
    distributeCities(numberOfCities);
    currentCycle = 0;

    do
    {
      eta=etamax*Math.pow(etamin/etamax,currentCycle/(double)numberOfCycles);
      sig=sigmax*Math.pow(sigmin/sigmax,currentCycle/(double)numberOfCycles);
      neighbor = (int)sig;
      sig = 2 * sig * sig;

      nextIndex = (int)(currentCycle % numberOfCities);
      j = nextCity[nextIndex];
      winner = winnerNeuron(j);

      for(i=-neighbor; i<neighbor; i++)
      {
        m = (winner + i) % numberOfNeurons;
        if (m < 0) m = numberOfNeurons + m;
        h = i * i /sig;
        if (h < 20.0) h = Math.exp(-h);
        else h = 0.0;

        weightSom[2*m] += eta * h * (x[j] - weightSom[2*m]);
        weightSom[2*m+1] += eta * h * (y[j] - weightSom[2*m+1]);
      }

      // update every n cycles
      if(currentCycle % update == 0)
      {
        if(isAnimated)
        {
          paint(networkArea);
        }
        lCurrentCycleValue.setText("" + currentCycle);
      }

      if(nextIndex == (numberOfCities - 1))
        distributeCities(numberOfCities);
      currentCycle++;
    } while(currentCycle < numberOfCycles);

    lCurrentCycleValue.setText("" + currentCycle);
    netComplete();
  }

  /**
  Creates a Net with the Winner Neurons
  */
  void netComplete()
  {
    int i, m, winner;
    winner = winnerNeuron(0);

    for(i=0; i<numberOfNeurons; i++)
    {
      m = (winner + i) % numberOfNeurons;
      weightWinner[2*i] = weightSom[2*m];
      weightWinner[2*i+1] = weightSom[2*m+1];
    }
  }

  /**
  Class for the Thread that paints the Connection between the Cities,
  implemented as inner class
  */
  private class GO implements Runnable
  {
    // implementation of the run section
    public void run()
    {
      if(doit)
      {
        recalculateWeights();
        paint(networkArea);
        findRoute = null;
      }
    }
  }

  public void init()
  {
    setDefaultValues();
    initKohonenNetwork();

    setLayout(new BorderLayout());
    GridBagLayout gbl = new GridBagLayout();

    Panel northPanel = new Panel();
    northPanel.setLayout(gbl);
    northPanel.setBackground(White);

    GridBagConstraints gbc = new GridBagConstraints();
    gbc.fill = GridBagConstraints.BOTH;
    gbc.weightx = 1.0;
    gbc.weighty = 1.0;
    gbc.insets = new Insets(2, 2, 2, 2);

    bStart = new Button("Start");
    bStart.addActionListener(this);
    gbc.gridx = 0;
    gbc.gridy = 0;
    gbc.gridwidth = 2;
    gbc.gridheight = 1;
    gbl.setConstraints(bStart, gbc);
    northPanel.add(bStart);

    bNewMap = new Button("New Map");
    bNewMap.addActionListener(this);
    gbc.gridx = 2;
    gbc.gridy = 0;
    gbc.gridwidth = 2;
    gbc.gridheight = 1;
    gbl.setConstraints(bNewMap, gbc);
    northPanel.add(bNewMap);

    bDefaultValues = new Button("Default Values");
    bDefaultValues.addActionListener(this);
    gbc.gridx = 4;
    gbc.gridy = 0;
    gbc.gridwidth = 3;
    gbc.gridheight = 1;
    gbl.setConstraints(bDefaultValues, gbc);
    northPanel.add(bDefaultValues);

    bAnimation = new Button("With Animation");
    bAnimation.addActionListener(this);
    gbc.gridx = 4;
    gbc.gridy = 1;
    gbc.gridwidth = 3;
    gbc.gridheight = 1;
    gbl.setConstraints(bAnimation, gbc);
    northPanel.add(bAnimation);

    lNumberOfCities = new Label("Number of Cities:");
    gbc.gridx = 0;
    gbc.gridy = 1;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lNumberOfCities, gbc);
    northPanel.add(lNumberOfCities);

    tCities = new TextField(Integer.toString(numberOfCities));
    gbc.gridx = 1;
    gbc.gridy = 1;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(tCities, gbc);
    northPanel.add(tCities);

    lNumberOfNeurons = new Label("Number of Neurons:");
    gbc.gridx = 0;
    gbc.gridy = 2;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lNumberOfNeurons, gbc);
    northPanel.add(lNumberOfNeurons);

    tNeurons = new TextField(Integer.toString(numberOfNeurons));
    gbc.gridx = 1;
    gbc.gridy = 2;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(tNeurons, gbc);
    northPanel.add(tNeurons);

    lNumberOfCycles = new Label("Number of Cycles:");
    gbc.gridx = 0;
    gbc.gridy = 3;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lNumberOfCycles, gbc);
    northPanel.add(lNumberOfCycles);

    tCycles = new TextField(Integer.toString(numberOfCycles));
    gbc.gridx = 1;
    gbc.gridy = 3;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(tCycles, gbc);
    northPanel.add(tCycles);

    lCurrentCycle = new Label("Current Cycle:");
    gbc.gridx = 0;
    gbc.gridy = 4;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lCurrentCycle, gbc);
    northPanel.add(lCurrentCycle);

    lCurrentCycleValue = new Label(Integer.toString(currentCycle));
    gbc.gridx = 1;
    gbc.gridy = 4;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lCurrentCycleValue, gbc);
    northPanel.add(lCurrentCycleValue);

    lEtamin = new Label("etamin: ", Label.RIGHT);
    gbc.gridx = 2;
    gbc.gridy = 1;
    gbl.setConstraints(lEtamin, gbc);
    northPanel.add(lEtamin);

    tEtamin = new TextField(Double.toString(etamin));
    gbc.gridx = 3;
    gbc.gridy = 1;
    gbl.setConstraints(tEtamin, gbc);
    northPanel.add(tEtamin);

    lEtamax = new Label("etamax:", Label.RIGHT);
    gbc.gridx = 2;
    gbc.gridy = 2;
    gbl.setConstraints(lEtamax, gbc);
    northPanel.add(lEtamax);

    tEtamax = new TextField(Double.toString(etamax));
    gbc.gridx = 3;
    gbc.gridy = 2;
    gbl.setConstraints(tEtamax, gbc);
    northPanel.add(tEtamax);

    lSigmin = new Label("sigmin: ", Label.RIGHT);
    gbc.gridx = 2;
    gbc.gridy = 3;
    gbl.setConstraints(lSigmin, gbc);
    northPanel.add(lSigmin);

    tSigmin = new TextField(Double.toString(sigmin));
    gbc.gridx = 3;
    gbc.gridy = 3;
    gbl.setConstraints(tSigmin, gbc);
    northPanel.add(tSigmin);

    lSigmax = new Label("sigmax:", Label.RIGHT);
    gbc.gridx = 2;
    gbc.gridy = 4;
    gbl.setConstraints(lSigmax, gbc);
    northPanel.add(lSigmax);

    tSigmax = new TextField(Double.toString(sigmax));
    gbc.gridx = 3;
    gbc.gridy = 4;
    gbl.setConstraints(tSigmax, gbc);
    northPanel.add(tSigmax);

    lEvery = new Label("Update every", Label.RIGHT);
    gbc.gridx = 4;
    gbc.gridy = 2;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lEvery, gbc);
    northPanel.add(lEvery);

    tUpdate = new TextField("" + update);
    tUpdate.setEditable(false);
    gbc.gridx = 5;
    gbc.gridy = 2;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(tUpdate, gbc);
    northPanel.add(tUpdate);

    lCycles = new Label("Cycles");
    gbc.gridx = 6;
    gbc.gridy = 2;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lCycles, gbc);
    northPanel.add(lCycles);

    lTime = new Label("Time needed:", Label.RIGHT);
    gbc.gridx = 4;
    gbc.gridy = 3;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lTime, gbc);
    northPanel.add(lTime);

    lTimeValue = new Label("" + df.format(time) + " s");
    gbc.gridx = 5;
    gbc.gridy = 3;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lTimeValue, gbc);
    northPanel.add(lTimeValue);

    lLength = new Label("Route Length:", Label.RIGHT);
    gbc.gridx = 4;
    gbc.gridy = 4;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lLength, gbc);
    northPanel.add(lLength);

    lLengthValue = new Label("" + df.format(length));
    gbc.gridx = 5;
    gbc.gridy = 4;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbl.setConstraints(lLengthValue, gbc);
    northPanel.add(lLengthValue);

    add("North", northPanel);

    myAnimator = new GO();
  }

  /**
  Listening to Events like pressing a Button
  */
  public void actionPerformed(ActionEvent e)
  {
    int dummyCities = numberOfCities;
    int dummyNeurons = numberOfNeurons;

    String which = (String)e.getActionCommand();

    try
    {
      etamin = Double.parseDouble(tEtamin.getText());
      etamax = Double.parseDouble(tEtamax.getText());
      sigmin = Double.parseDouble(tSigmin.getText());
      sigmax = Double.parseDouble(tSigmax.getText());

      update = Integer.parseInt(tUpdate.getText());
      numberOfCycles = Integer.parseInt(tCycles.getText());
      numberOfCities = Integer.parseInt(tCities.getText());
      numberOfNeurons = Integer.parseInt(tNeurons.getText());

      if(etamin<=0 || etamin>=1)
      {
        setDefaultEtamin();
        tEtamin.setText("" + etamin);
      }
      if(etamax<=0 || etamax>=1)
      {
        setDefaultEtamax();
        tEtamax.setText("" + etamax);
      }
      if(etamin>etamax)
      {
        setDefaultEtamin();
        setDefaultEtamax();
        tEtamin.setText("" + etamin);
        tEtamax.setText("" + etamax);
      }
      if(sigmin<=0)
      {
        setDefaultSigmin();
        tSigmin.setText("" + sigmin);
      }
      if(sigmax<=0)
      {
        setDefaultSigmax();
        tSigmax.setText("" + sigmax);
      }
      if(sigmin>sigmax)
      {
        setDefaultSigmin();
        setDefaultSigmax();
        tSigmin.setText("" + sigmin);
        tSigmax.setText("" + sigmax);
      }
      if(numberOfCities<1 || numberOfCities>1000)
      {
        setDefaultNumberOfCities();
        tCities.setText("" + numberOfCities);
      }
      if(numberOfNeurons<1 || numberOfNeurons>100000)
      {
        setDefaultNumberOfNeurons();
        tNeurons.setText("" + numberOfNeurons);
      }
      if(numberOfCycles<0 || numberOfCycles>100000)
      {
        setDefaultNumberOfCycles();
        tCycles.setText("" + numberOfCycles);
      }
      if(update<1 || update>numberOfCycles)
      {
        update = numberOfCycles;
        tUpdate.setText("" + update);
      }
      if(dummyCities != numberOfCities || dummyNeurons != numberOfNeurons)
        initKohonenNetwork();

      if(which.equals("Start"))
      {
        doit = true;
        timeBegin = new Date().getTime();
        findRoute = new Thread(myAnimator);
        findRoute.start();
      }
      if(which.equals("New Map"))
      {
        doit = true;
        initKohonenNetwork();
        timeBegin = new Date().getTime();
        findRoute = new Thread(myAnimator);
        findRoute.start();
      }
    }
    catch(Exception te)
    {
      tEtamin.setText("" + etamin);
      tEtamax.setText("" + etamax);
      tSigmin.setText("" + sigmin);
      tSigmax.setText("" + sigmax);

      tUpdate.setText("" + update);
      tCycles.setText("" + numberOfCycles);
      tCities.setText("" + numberOfCities);
      tNeurons.setText("" + numberOfNeurons);

      System.out.println
        ("wrong input - continue using default values...\n"+te);
    }

    if(which.equals("With Animation"))
    {
      bAnimation.setLabel("Without Animation");
      isAnimated = true;
      tUpdate.setEditable(true);
    }

    if(which.equals("Without Animation"))
    {
      bAnimation.setLabel("With Animation");
      isAnimated = false;
      tUpdate.setEditable(false);
    }

    if(which.equals("Default Values"))
    {
      setDefaultValues();

      tEtamin.setText("" + etamin);
      tEtamax.setText("" + etamax);
      tSigmin.setText("" + sigmin);
      tSigmax.setText("" + sigmax);
      tUpdate.setText("" + update);
      tCycles.setText("" + numberOfCycles);
      tCities.setText("" + numberOfCities);
      tNeurons.setText("" + numberOfNeurons);

      initKohonenNetwork();
    }
  }

  /**
  Translates the x Values between 0 and 1 to the
  painting area
  */
  private int translateToX(double dummy)
  {
    int w = this.getSize().width;
    return (int)(dummy *((double) w - 0.0) + 0.0);
  }

  /**
  Translates the y Values between 0 and 1 to the
  painting area
  */
  private int translateToY(double dummy)
  {
    int h = this.getSize().height;
    return (int)(dummy *((double) h - 140.0)  + 140.0);
  }

  /**
  Translates the x Values between 0 and 1 to the
  painting area (with a margin for the Cities)
  */
  private int translateToXmargin(double dummy)
  {
    // cities shouldn't be located outside the painting area
    // that's why let them a margin of 4 pixels (radius) ...
    int w = this.getSize().width;
    return (int)(dummy *((double) w - 8.0) + 4.0);
  }

  /**
  Translates the y Values between 0 and 1 to the
  painting area (with a margin for the Cities)
  */
  private int translateToYmargin(double dummy)
  {
    // cities shouldn't be located outside the painting area
    // that's why let them a margin of 4 pixels (radius) ...
    int h = this.getSize().height;
    return (int)(dummy *((double) h - 148.0)  + 144.0);
  }

  /**
  Gives the Time needed to a Label
  */
  void printTime()
  {
    timeEnd = new Date().getTime();
    time = (double)(timeEnd - timeBegin) / 1000;
    lTimeValue.setText("" + df.format(time) + " s");
  }

  /**
  Gives the Length to a Label
  */
  void printLength()
  {
    lLengthValue.setText("" + df.format(getLength()));
  }

  /**
  Painting all the Cities
  */
  public void drawCities(Graphics g)
  {
    for(int i=0; i<numberOfCities; i++)
    {
      g.setColor(White);
      g.fillOval(translateToXmargin(x[i])-4, translateToYmargin(y[i])-4,8,8);
      g.setColor(Red);
      g.drawOval(translateToXmargin(x[i])-4, translateToYmargin(y[i])-4,8,8);
    }
  }

  /**
  Painting the Grid
  */
  public void drawGrid(Graphics g)
  {
    for(double i=0; i<=1.0; i+=(1.0/14.0))
    {
      g.drawLine(translateToX(0.0),translateToY(i),
                 translateToX(1.0)-1,translateToY(i));
      g.drawLine(translateToX(i),translateToY(0.0),
                 translateToX(i),translateToY(1.0)-1);
    }
  }

  /**
  Draws my Path
  */
  public void drawNetwork(Graphics g)
  {
    Dimension size = this.getSize();
    int w = size.width, h = size.height;

    // clean it up
    g.setColor(LightGrey);
    g.fillRect(0, 0, w, h);

    // draw my grid
    g.setColor(DarkGrey);
    drawGrid(g);

    g.setColor(Black);
    if(isAnimated)
    {
      for(int i=0; i<numberOfNeurons-1; i++)
      {
        g.drawLine(translateToXmargin(weightSom[2*i]),
                   translateToYmargin(weightSom[2*i+1]),
                   translateToXmargin(weightSom[2*i+2]),
                   translateToYmargin(weightSom[2*i+3]));
      }
      // avoid gaps in the "circle" when animated
      g.drawLine(translateToXmargin(weightSom[2*numberOfNeurons-2]),
                 translateToYmargin(weightSom[2*numberOfNeurons-1]),
                 translateToXmargin(weightSom[0]),
                 translateToYmargin(weightSom[1]));

      if(currentCycle>0)
      {
        printTime();
        printLength();
      }
    }
    else
    {
      for(int i=0; i<numberOfNeurons-1; i++)
      {
        g.drawLine(translateToXmargin(weightWinner[2*i]),
                   translateToYmargin(weightWinner[2*i+1]),
                   translateToXmargin(weightWinner[2*i+2]),
                   translateToYmargin(weightWinner[2*i+3]));
      }

      if(currentCycle>0)
      {
        printTime();
        printLength();
      }
    }

    drawCities(g);
  }

  public void paint(Graphics g)
  {
    Dimension size = this.getSize();
    int w = size.width, h = size.height;
    this.setBackground(White);

    if ((zBuffer == null) || ((areaWidth != w) || (areaHeight != h)))
    {
      zBuffer = this.createImage(w, h);
      areaWidth = w; areaHeight = h;
    }

    Rectangle myRectangle = new Rectangle(translateToX(0),translateToY(0),
                                   translateToX(1.0),translateToY(1.0));

    Graphics imageBufferBack = zBuffer.getGraphics();
    imageBufferBack.clipRect(
      myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
    Graphics imageBufferFront = this.getGraphics();
    imageBufferFront.clipRect(
      myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);

    drawNetwork(imageBufferBack);
    imageBufferFront.drawImage(zBuffer, 0, 0, this);

    // cleaning and finishing process, automatic garbage collection
    myRectangle      = null;
    imageBufferBack  = null;
    imageBufferFront = null;
    System.gc();
  }

  public void start()
  {
    findRoute = new Thread(myAnimator);
    findRoute.start();
  }

  public void stop()
  {
    findRoute = null;
  }
}

