src/ufo/filters/TrackCheckShip.cc
void TrackCheckShip::applyFilter(const std::vector<bool> & apply,
                                 const Variables & filtervars,
                                 std::vector<std::vector<bool>> & flagged) const {
  ObsAccessor obsAccessor = TrackCheckUtils::createObsAccessor(options_.stationIdVariable, obsdb_);

  const std::vector<size_t> validObsIds
                                   = obsAccessor.getValidObservationIds(apply, *flags_, filtervars);

  RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds);
  TrackCheckUtils::sortTracksChronologically(validObsIds, obsAccessor, splitter);

  TrackCheckUtils::ObsGroupLocationTimes obsLocTime =
      TrackCheckUtils::collectObservationsLocations(obsAccessor);

  std::vector<bool> isRejected(obsLocTime.latitudes.size(), false);
  size_t trackNumber = 0;
  for (auto track : splitter.multiElementGroups()) {
    trackNumber++;
    std::string stationId = std::to_string(trackNumber);
    std::vector<TrackObservation> trackObservations = collectTrackObservations(
          track.begin(), track.end(), validObsIds, obsLocTime);
    std::vector<std::reference_wrapper<TrackObservation>> trackObservationsReferences;
    trackObservationsReferences.reserve(trackObservations.size());
    std::transform(trackObservations.begin(), trackObservations.end(),
                   std::back_inserter(trackObservationsReferences),
                   [](TrackObservation &obs) {
      return std::ref<TrackObservation>(obs); });
    calculateTrackSegmentProperties(trackObservationsReferences,
                                    CalculationMethod::FIRSTITERATION);
    if (!trackObservationsReferences.empty() &&
        this->options_.core.earlyBreakCheck &&
        TrackCheckShip::earlyBreak(trackObservationsReferences, stationId)) {
      continue;
    }
      bool firstIterativeRemoval = true;
      while (trackObservationsReferences.size() >= 3) {
        // Initial loop: fastest (as determined by set of comparisons) observation removed
        // until all segments show slower speed than max threshold
        auto maxSpeedReferenceIterator = std::max_element(
              trackObservationsReferences.begin(), trackObservationsReferences.end(),
              [](TrackObservation a, TrackObservation b) {
            return a.getObservationStatistics().speed <
            b.getObservationStatistics().speed;});
        auto maxSpeedValue = maxSpeedReferenceIterator->get().getObservationStatistics().speed;
        if (maxSpeedValue <= (0.8 * options_.core.maxSpeed.value())) {
          break;
        } else if (maxSpeedValue < options_.core.maxSpeed.value()) {
          auto maxSpeedAngle = std::max(
                (maxSpeedReferenceIterator - 1)->get().getObservationStatistics().angle,
                maxSpeedReferenceIterator->get().getObservationStatistics().angle);
          if (maxSpeedAngle <= 90.0) {
            break;
          }
        }
        removeFaultyObservation(
              trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval,
              stationId);
        firstIterativeRemoval = false;
        calculateTrackSegmentProperties(trackObservationsReferences, CalculationMethod::MAINLOOP);
      }
      auto rejectedCount = std::count_if(trackObservations.begin(), trackObservations.end(),
                    [](const TrackObservation& a) {return a.rejected();});
      if (rejectedCount >= options_.core.rejectionThreshold.value() * trackObservations.size()) {
        oops::Log::trace() << "CheckShipTrack: track " << stationId << " NumRej " <<
                              rejectedCount << " out of " << trackObservations.size() <<
                              " reports rejected. *** Reject whole track ***\n";
        for (TrackObservation &obs : trackObservations)
          obs.setRejected(true);
      }
      flagRejectedTrackObservations(track.begin(), track.end(),
                                    validObsIds, trackObservations, isRejected);
  }
  obsAccessor.flagRejectedObservations(isRejected, flagged);
}
/// The filter begins with separating observations into tracks based on \p Station_Id. Various
/// calculations are then performed between consecutive observations: distances/speeds between
/// observation pairs, and angles between the displacement vectors formed by triplets of
/// consecutive observations. Track-specific counters are incremented for various metrics that
/// indicate an unexpected track path.
///
/// Based on these counter values, the filter may ignore the current track and move on to the next
/// one. Using the described calculations and additional ones if necessary, the observation
/// directly before and/or after the fastest segment is chosen for rejection. This will continue
/// until all remaining segments that link accepted observations are either:
/// 1. slower than a user-defined "max speed (m/s)" where the angles between the fastest accepted
/// segment's displacement vector and its two neighbouring segments' displacement vectors are
/// both less than 90 degrees.
/// 2. slower than 80 percent of this user-defined speed.
///
/// The full track will be rejected if the number of observations removed is more than the
/// user-defined "rejection threshold".
The filter begins with separating observations into tracks based on \p Station_Id. Various
calculations are then performed between consecutive observations: distances/speeds between
observation pairs, and angles between the displacement vectors formed by triplets of
consecutive observations. Track-specific counters are incremented for various metrics that
indicate an unexpected track path.

Based on these counter values, the filter may ignore the current track and move on to the next
one. Using the described calculations and additional ones if necessary, the observation
directly before and/or after the fastest segment is chosen for rejection. This will continue
until all remaining segments that link accepted observations are either:
1. slower than a user-defined "max speed (m/s)" where the angles between the fastest accepted
segment's displacement vector and its two neighbouring segments' displacement vectors are
both less than 90 degrees.
2. slower than 80 percent of this user-defined speed.

The full track will be rejected if the number of observations removed is more than the
user-defined "rejection threshold".
The filter begins with separating observations into tracks based on \ p Station_Id . Various calculations are then performed between consecutive observations : distances / speeds between observation pairs and angles between the displacement vectors formed by triplets of consecutive observations . Track - specific counters are incremented for various metrics that indicate an unexpected track path . Based on these counter values the filter may ignore the current track and move on to the next one . Using the described calculations and additional ones if necessary the observation directly before and / or after the fastest segment is chosen for rejection . This will continue until all remaining segments that link accepted observations are either : 1 . slower than a user - defined " max speed ( m / s ) " where the angles between the fastest accepted segment ' s displacement vector and its two neighbouring segments ' displacement vectors are both less than 90 degrees . 2 . slower than 80 percent of this user - defined speed . The full track will be rejected if the number of observations removed is more than the user - defined " rejection threshold " .
void TrackCheckShip::applyFilter(const std::vector<bool> & apply,
                                 const Variables & filtervars,
                                 std::vector<std::vector<bool>> & flagged) const {
  ObsAccessor obsAccessor = TrackCheckUtils::createObsAccessor(options_.stationIdVariable, obsdb_);
  const std::vector<size_t> validObsIds
                                   = obsAccessor.getValidObservationIds(apply, *flags_, filtervars);
  RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds);
  TrackCheckUtils::sortTracksChronologically(validObsIds, obsAccessor, splitter);
  TrackCheckUtils::ObsGroupLocationTimes obsLocTime =
      TrackCheckUtils::collectObservationsLocations(obsAccessor);
  std::vector<bool> isRejected(obsLocTime.latitudes.size(), false);
  size_t trackNumber = 0;
  for (auto track : splitter.multiElementGroups()) {
    trackNumber++;
    std::string stationId = std::to_string(trackNumber);
    std::vector<TrackObservation> trackObservations = collectTrackObservations(
          track.begin(), track.end(), validObsIds, obsLocTime);
    std::vector<std::reference_wrapper<TrackObservation>> trackObservationsReferences;
    trackObservationsReferences.reserve(trackObservations.size());
    std::transform(trackObservations.begin(), trackObservations.end(),
                   std::back_inserter(trackObservationsReferences),
                   [](TrackObservation &obs) {
      return std::ref<TrackObservation>(obs); });
    calculateTrackSegmentProperties(trackObservationsReferences,
                                    CalculationMethod::FIRSTITERATION);
    if (!trackObservationsReferences.empty() &&
        this->options_.core.earlyBreakCheck &&
        TrackCheckShip::earlyBreak(trackObservationsReferences, stationId)) {
      continue;
    }
      bool firstIterativeRemoval = true;
      while (trackObservationsReferences.size() >= 3) {
        auto maxSpeedReferenceIterator = std::max_element(
              trackObservationsReferences.begin(), trackObservationsReferences.end(),
              [](TrackObservation a, TrackObservation b) {
            return a.getObservationStatistics().speed <
            b.getObservationStatistics().speed;});
        auto maxSpeedValue = maxSpeedReferenceIterator->get().getObservationStatistics().speed;
        if (maxSpeedValue <= (0.8 * options_.core.maxSpeed.value())) {
          break;
        } else if (maxSpeedValue < options_.core.maxSpeed.value()) {
          auto maxSpeedAngle = std::max(
                (maxSpeedReferenceIterator - 1)->get().getObservationStatistics().angle,
                maxSpeedReferenceIterator->get().getObservationStatistics().angle);
          if (maxSpeedAngle <= 90.0) {
            break;
          }
        }
        removeFaultyObservation(
              trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval,
              stationId);
        firstIterativeRemoval = false;
        calculateTrackSegmentProperties(trackObservationsReferences, CalculationMethod::MAINLOOP);
      }
      auto rejectedCount = std::count_if(trackObservations.begin(), trackObservations.end(),
                    [](const TrackObservation& a) {return a.rejected();});
      if (rejectedCount >= options_.core.rejectionThreshold.value() * trackObservations.size()) {
        oops::Log::trace() << "CheckShipTrack: track " << stationId << " NumRej " <<
                              rejectedCount << " out of " << trackObservations.size() <<
                              " reports rejected. *** Reject whole track ***\n";
        for (TrackObservation &obs : trackObservations)
          obs.setRejected(true);
      }
      flagRejectedTrackObservations(track.begin(), track.end(),
                                    validObsIds, trackObservations, isRejected);
  }
  obsAccessor.flagRejectedObservations(isRejected, flagged);
}
void TrackCheckShip :: applyFilter ( const std :: vector < bool > & apply , const Variables & filtervars , std :: vector < std :: vector < bool > > & flagged ) const { ObsAccessor obsAccessor = TrackCheckUtils :: createObsAccessor ( options_ . stationIdVariable , obsdb_ ) ; const std :: vector < size_t > validObsIds = obsAccessor . getValidObservationIds ( apply , * flags_ , filtervars ) ; RecursiveSplitter splitter = obsAccessor . splitObservationsIntoIndependentGroups ( validObsIds ) ; TrackCheckUtils :: sortTracksChronologically ( validObsIds , obsAccessor , splitter ) ; TrackCheckUtils :: ObsGroupLocationTimes obsLocTime = TrackCheckUtils :: collectObservationsLocations ( obsAccessor ) ; std :: vector < bool > isRejected ( obsLocTime . latitudes . size ( ) , false ) ; size_t trackNumber = 0 ; for ( auto track : splitter . multiElementGroups ( ) ) { trackNumber ++ ; std :: string stationId = std :: to_string ( trackNumber ) ; std :: vector < TrackObservation > trackObservations = collectTrackObservations ( track . begin ( ) , track . end ( ) , validObsIds , obsLocTime ) ; std :: vector < std :: reference_wrapper < TrackObservation > > trackObservationsReferences ; trackObservationsReferences . reserve ( trackObservations . size ( ) ) ; std :: transform ( trackObservations . begin ( ) , trackObservations . end ( ) , std :: back_inserter ( trackObservationsReferences ) , [ ] ( TrackObservation & obs ) { return std :: ref < TrackObservation > ( obs ) ; } ) ; calculateTrackSegmentProperties ( trackObservationsReferences , CalculationMethod :: FIRSTITERATION ) ; if ( ! trackObservationsReferences . empty ( ) && this -> options_ . core . earlyBreakCheck && TrackCheckShip :: earlyBreak ( trackObservationsReferences , stationId ) ) { continue ; } bool firstIterativeRemoval = true ; while ( trackObservationsReferences . size ( ) >= 3 ) { auto maxSpeedReferenceIterator = std :: max_element ( trackObservationsReferences . begin ( ) , trackObservationsReferences . end ( ) , [ ] ( TrackObservation a , TrackObservation b ) { return a . getObservationStatistics ( ) . speed < b . getObservationStatistics ( ) . speed ; } ) ; auto maxSpeedValue = maxSpeedReferenceIterator -> get ( ) . getObservationStatistics ( ) . speed ; if ( maxSpeedValue <= ( 0.8 * options_ . core . maxSpeed . value ( ) ) ) { break ; } else if ( maxSpeedValue < options_ . core . maxSpeed . value ( ) ) { auto maxSpeedAngle = std :: max ( ( maxSpeedReferenceIterator - 1 ) -> get ( ) . getObservationStatistics ( ) . angle , maxSpeedReferenceIterator -> get ( ) . getObservationStatistics ( ) . angle ) ; if ( maxSpeedAngle <= 90.0 ) { break ; } } removeFaultyObservation ( trackObservationsReferences , maxSpeedReferenceIterator , firstIterativeRemoval , stationId ) ; firstIterativeRemoval = false ; calculateTrackSegmentProperties ( trackObservationsReferences , CalculationMethod :: MAINLOOP ) ; } auto rejectedCount = std :: count_if ( trackObservations . begin ( ) , trackObservations . end ( ) , [ ] ( const TrackObservation & a ) { return a . rejected ( ) ; } ) ; if ( rejectedCount >= options_ . core . rejectionThreshold . value ( ) * trackObservations . size ( ) ) { oops :: Log :: trace ( ) << " " << stationId << " " << rejectedCount << " " << trackObservations . size ( ) << " \n " ; for ( TrackObservation & obs : trackObservations ) obs . setRejected ( true ) ; } flagRejectedTrackObservations ( track . begin ( ) , track . end ( ) , validObsIds , trackObservations , isRejected ) ; } obsAccessor . flagRejectedObservations ( isRejected , flagged ) ; }
The filter begins with separating observations into tracks based on \p Station_Id.
The filter begins with separating observations into tracks based on \ p Station_Id .
// Initial loop: fastest (as determined by set of comparisons) observation removed // until all segments show slower speed than max threshold