@Override
 public void setLayers(Layers parent) {
   // ok, we've been pasted. just double check that our children know who is
   // the boss
   Enumeration<Editable> numer = getSegments().elements();
   while (numer.hasMoreElements()) {
     Editable editable = (Editable) numer.nextElement();
     PlanningSegment seg = (PlanningSegment) editable;
     seg.setWrapper(this);
   }
 }
    @Override
    double getMinuteDelta(PlanningSegment seg) {
      // how far will we travel in time?
      double metresPerSec = seg.getSpeed().getValueIn(WorldSpeed.M_sec);
      double metresPerMin = metresPerSec * 60d;

      double distanceM = metresPerSec * getSecsTravelled(seg);
      WorldDistance wd = new WorldDistance(distanceM, WorldDistance.METRES);
      seg.setDistanceSilent(new WorldDistance(wd.getValueIn(WorldDistance.NM), WorldDistance.NM));

      return metresPerMin;
    }
    @Override
    protected double getSecsTravelled(PlanningSegment seg) {
      // how long does it take to travel this distance?
      double secsTaken =
          seg.getDistance().getValueIn(WorldDistance.METRES)
              / seg.getSpeed().getValueIn(WorldSpeed.M_sec);

      // sort out the leg length
      seg.setDurationSilent(new Duration(secsTaken, Duration.SECONDS));

      return secsTaken;
    }
  public void recalculate() {
    Enumeration<Editable> numer = getSegments().elements();
    WorldLocation thisOrigin = getOrigin();
    HiResDate thisDate = getStartDate();
    while (numer.hasMoreElements()) {
      Editable editable = (Editable) numer.nextElement();
      PlanningSegment seg = (PlanningSegment) editable;

      PlanningCalc theCalc = null;
      int model = seg.getCalculation();
      switch (model) {
        case PlanningLegCalcModelPropertyEditor.RANGE_SPEED:
          theCalc = new FromRangeSpeed();
          break;
        case PlanningLegCalcModelPropertyEditor.RANGE_TIME:
          theCalc = new FromRangeTime();
          break;
        case PlanningLegCalcModelPropertyEditor.SPEED_TIME:
          theCalc = new FromSpeedTime();
          break;
      }

      // see if this is the closing segment
      if (seg instanceof ClosingSegment) {
        // what's the range and bearing back to the origin
        WorldVector offset = getOrigin().subtract(thisOrigin);

        // and store it.
        seg.setSpeedSilent(new WorldSpeed(12, WorldSpeed.Kts));
        seg.setDistanceSilent(new WorldDistance(offset.getRange(), WorldDistance.DEGS));
        seg.setCourseSilent(MWC.Algorithms.Conversions.Rads2Degs(offset.getBearing()));
        seg.setDepthSilent(new WorldDistance(offset.getDepth(), WorldDistance.METRES));
      }

      theCalc.construct(seg, thisOrigin, thisDate);

      // did we generate anything?
      if (seg.size() > 0) {
        // ok, now update the date/location
        thisOrigin = seg.last().getBounds().getCentre();
        thisDate = seg.endDTG();
      }
    }

    // ok, sort out the symbol & label freq
    HiResDate symFreq = this.getSymbolFrequency();
    HiResDate labelFreq = this.getLabelFrequency();

    this.setSymbolFrequency(new HiResDate(0));
    this.setLabelFrequency(new HiResDate(0));

    // and restore them
    setSymbolFrequency(symFreq);
    setLabelFrequency(labelFreq);
  }
  @Override
  public void findNearestHotSpotIn(
      Point cursorPos,
      WorldLocation cursorLoc,
      ComponentConstruct currentNearest,
      Layer parentLayer) {
    // initialise thisDist, since we're going to be over-writing it
    WorldDistance thisDist = new WorldDistance(0, WorldDistance.DEGS);

    Enumeration<Editable> numer = getSegments().elements();
    while (numer.hasMoreElements()) {
      final PlanningSegment thisSeg = (PlanningSegment) numer.nextElement();
      if (thisSeg.getVisible()) {
        // produce a location for the end
        FixWrapper endFix = (FixWrapper) thisSeg.last();
        if (endFix != null) {

          // how far away is it?
          thisDist = endFix.getLocation().rangeFrom(cursorLoc, thisDist);

          final WorldLocation fixLocation =
              new WorldLocation(endFix.getLocation()) {
                private static final long serialVersionUID = 1L;

                @Override
                public void addToMe(WorldVector delta) {
                  super.addToMe(delta);

                  // so, what's the bearing back to the leg start?
                  double newBearing = super.bearingFrom(thisSeg.first().getBounds().getCentre());

                  newBearing = MWC.Algorithms.Conversions.Rads2Degs(newBearing);

                  // limit the bearing to the nearest 5 deg marker
                  int m = ((int) newBearing / 10);
                  newBearing = m * 10d;

                  // trim it to being positive
                  if (newBearing < 0) newBearing += 360;

                  thisSeg.setCourse(newBearing);
                }
              };

          // try range
          currentNearest.checkMe(this, thisDist, null, parentLayer, fixLocation);
        }
      }
    }
  }
    @Override
    double getMinuteDelta(PlanningSegment seg) {
      // home long to travel along it (secs)
      double travelSecs = seg.getDuration().getValueIn(Duration.SECONDS);
      double metresPerSec = seg.getDistance().getValueIn(WorldDistance.METRES) / travelSecs;

      double metresPerMin = metresPerSec * 60d;

      // update the speed, so it makes sense in the fix
      WorldSpeed speedMtrs = new WorldSpeed(metresPerSec, WorldSpeed.M_sec);
      WorldSpeed speedKTs = new WorldSpeed(speedMtrs.getValueIn(WorldSpeed.Kts), WorldSpeed.Kts);
      seg.setSpeedSilent(speedKTs);

      return metresPerMin;
    }
  @Override
  public void add(Editable point) {
    if (point instanceof PlanningSegment) {

      // hey, is this a closing segment?
      if (point instanceof ClosingSegment) {
        // do we already have one?
        if (this.getSegments().last() instanceof ClosingSegment) {
          // skip....
          _toolParent.logError(ToolParent.WARNING, "Already have closing segment", null);
        }
      }

      // take a copy of the name, to stop it getting manmgled
      String name = point.getName();

      super.add(point);

      if (point.getName() != name) ((PlanningSegment) point).setName(name);

      // better do a recalc, aswell
      recalculate();
    } else {
      throw new RuntimeException("can't add this type to a composite track wrapper");
    }
  }
    void construct(PlanningSegment seg, WorldLocation origin, HiResDate date) {
      // check we have some data
      if (date == null || origin == null) return;

      double timeTravelledMillis = getSecsTravelled(seg) * 1000;

      // ditch the existing items
      seg.removeAllElements();

      // ok build for this segment
      double courseDegs = seg.getCourse();
      double courseRads = MWC.Algorithms.Conversions.Degs2Rads(courseDegs);

      long timeMillis = date.getDate().getTime();
      final long timeStepMillis;
      final long ONE_MIN = 60 * 1000;
      final long ONE_HOUR = 60 * ONE_MIN;
      final long ONE_DAY = 24 * ONE_HOUR;

      // use a time step appropriate to how long we're generating the track for
      if (timeTravelledMillis <= 4 * ONE_HOUR) timeStepMillis = ONE_MIN;
      else if (timeTravelledMillis <= 12 * ONE_HOUR) timeStepMillis = 10 * ONE_MIN;
      else if (timeTravelledMillis <= 2 * ONE_DAY) timeStepMillis = 30 * ONE_MIN;
      else timeStepMillis = ONE_HOUR;

      // now work out how far he will have travelled in a time step
      double distPerMinute = getMinuteDelta(seg);
      double distPerStep = distPerMinute * (timeStepMillis / ONE_MIN);
      WorldVector vec =
          new WorldVector(courseRads, new WorldDistance(distPerStep, WorldDistance.METRES), null);

      for (long tNow = timeMillis;
          tNow <= timeMillis + timeTravelledMillis;
          tNow += timeStepMillis) {
        HiResDate thisDtg = new HiResDate(tNow);

        // ok, do this fix
        Fix thisF =
            new Fix(thisDtg, origin, courseRads, seg.getSpeed().getValueIn(WorldSpeed.ft_sec) / 3);

        // override the depth
        thisF.getLocation().setDepth(seg.getDepth().getValueIn(WorldDistance.METRES));

        FixWrapper fw = new FixWrapper(thisF);

        fw.setColor(seg.getColor());

        // and store it
        seg.add(fw);

        // reset the name, we're not going to use a human generated one
        fw.resetName();

        // produce a new position
        origin = origin.add(vec);
      }
    }
  @FireExtended
  public void appendReverse() {
    // ok, get the legs
    SegmentList list = super.getSegments();
    ArrayList<PlanningSegment> holder = new ArrayList<PlanningSegment>();
    Enumeration<Editable> iterator = list.elements();
    while (iterator.hasMoreElements()) {
      // put this element at the start
      holder.add(0, (PlanningSegment) iterator.nextElement());
    }

    // now run the legs back in reverse
    Iterator<PlanningSegment> iter2 = holder.iterator();
    while (iter2.hasNext()) {
      PlanningSegment pl = iter2.next();

      try {
        PlanningSegment pl2 = (PlanningSegment) pl.clone();

        // now reverse it
        double newCourse = pl2.getCourse() + 180d;
        if (newCourse > 360) newCourse -= 360;
        pl2.setCourse(newCourse);

        // show the name as reversed
        pl2.setName(pl2.getName() + "(R)");

        // ok, now add it
        this.add(pl2);
      } catch (CloneNotSupportedException e) {
        e.printStackTrace();
      }
    }

    // ok, better throw in a recalculate
    this.recalculate();
  }
 @Override
 protected double getSecsTravelled(PlanningSegment seg) {
   return seg.getDuration().getValueIn(Duration.SECONDS);
 }
 @Override
 double getMinuteDelta(PlanningSegment seg) {
   // find out how far it travels
   double distPerMinute = seg.getSpeed().getValueIn(WorldSpeed.M_sec) * 60d;
   return distPerMinute;
 }