protected void calcAndStore(final GeodeticCalculator calc, final Point locA, final Point locB) {
      // get the output dataset
      Length_M target = (Length_M) getOutputs().get(0);

      // now find the range between them
      calc.setStartingGeographicPoint(
          locA.getCentroid().getOrdinate(0), locA.getCentroid().getOrdinate(1));
      calc.setDestinationGeographicPoint(
          locB.getCentroid().getOrdinate(0), locB.getCentroid().getOrdinate(1));
      double thisDist = calc.getOrthodromicDistance();
      target.add(Measure.valueOf(thisDist, target.getUnits()));
    }
  /**
   * Computes the angle from the first point to the last point of a LineString or MultiLineString.
   * TODO: put this method into org.opentripplanner.common.geometry.DirectionUtils
   *
   * @param geometry a LineString or a MultiLineString
   * @return
   */
  public synchronized double getFirstToLastSegmentAngle(Geometry geometry) {
    LineString line;
    if (geometry instanceof MultiLineString) {
      line = (LineString) geometry.getGeometryN(geometry.getNumGeometries() - 1);
    } else {
      assert geometry instanceof LineString;
      line = (LineString) geometry;
    }
    int numPoints = line.getNumPoints();
    Coordinate coord0 = line.getCoordinateN(0);
    Coordinate coord1 = line.getCoordinateN(numPoints - 1);
    int i = numPoints - 3;
    while (distanceLibrary.fastDistance(coord0, coord1) < 10 && i >= 0) {
      coord1 = line.getCoordinateN(i--);
    }

    geodeticCalculator.setStartingGeographicPoint(coord0.x, coord0.y);
    geodeticCalculator.setDestinationGeographicPoint(coord1.x, coord1.y);
    return geodeticCalculator.getAzimuth() * Math.PI / 180;
  }
    /**
     * wrap the actual operation. We're doing this since we need to separate it from the core
     * "execute" operation in order to support dynamic updates
     *
     * @param unit
     * @param outputs
     */
    private void performCalc(List<IStoreItem> outputs) {

      // and the bounding period
      TimePeriod period = getBoundingTime(data.values());

      // check it's valid
      if (period.invalid()) {
        System.err.println("Insufficient coverage for datasets");
        return;
      }

      // ok, let's start by finding our time sync
      IBaseTemporalCollection times = getOptimalTimes(period, data.values());

      // check we were able to find some times
      if (times == null) {
        System.err.println("Unable to find time source dataset");
        return;
      }

      // get the output dataset
      final Temporal.Frequency_Hz output = (Frequency_Hz) outputs.iterator().next();

      final GeodeticCalculator calc = GeoSupport.getCalculator();

      // and now we can start looping through
      Iterator<Long> tIter = times.getTimes().iterator();
      while (tIter.hasNext()) {
        long thisTime = (long) tIter.next();

        if ((thisTime >= period.startTime) && (thisTime <= period.endTime)) {
          // ok, now collate our data
          Geometry txLoc = locationFor(data.get(TX + "LOC"), thisTime);
          Geometry rxLoc = locationFor(data.get(RX + "LOC"), thisTime);

          double txCourseRads = valueAt(data.get(TX + "COURSE"), thisTime, SI.RADIAN);
          double rxCourseRads = valueAt(data.get(RX + "COURSE"), thisTime, SI.RADIAN);

          double txSpeedMSec = valueAt(data.get(TX + "SPEED"), thisTime, SI.METERS_PER_SECOND);
          double rxSpeedMSec = valueAt(data.get(RX + "SPEED"), thisTime, SI.METERS_PER_SECOND);

          double freq = valueAt(data.get(TX + "FREQ"), thisTime, SI.HERTZ);

          double soundSpeed = valueAt(data.get("SOUND_SPEED"), thisTime, SI.METERS_PER_SECOND);

          // now find the bearing between them
          calc.setStartingGeographicPoint(
              txLoc.getCentroid().getOrdinate(0), txLoc.getCentroid().getOrdinate(1));
          calc.setDestinationGeographicPoint(
              rxLoc.getCentroid().getOrdinate(0), rxLoc.getCentroid().getOrdinate(1));
          double angleDegs = calc.getAzimuth();
          if (angleDegs < 0) angleDegs += 360;

          double angleRads = Math.toRadians(angleDegs);

          // ok, and the calculation
          double shifted =
              calcPredictedFreqSI(
                  soundSpeed,
                  txCourseRads,
                  rxCourseRads,
                  txSpeedMSec,
                  rxSpeedMSec,
                  angleRads,
                  freq);

          output.add(thisTime, shifted);
        }
      }
    }