text: /*
   * Copyright (C) 2005-2019 by Centre National d'Etudes Spatiales (CNES)
   *
   * This file is licensed under MIT license:
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  
  #ifndef ossimSarSensorModel_HEADER
  #define ossimSarSensorModel_HEADER
  
  #include <boost/config.hpp>
  #include <string>
  #include <stdint.h>
  #include <cassert>
  
  #if defined(__GNUC__) || defined(__clang__)
  # pragma GCC diagnostic push
  #   pragma GCC diagnostic ignored "-Wdeprecated-copy"
  #   pragma GCC diagnostic ignored "-Wunused-parameter"
  #   pragma GCC diagnostic ignored "-Woverloaded-virtual"
  #   pragma GCC diagnostic ignored "-Wshadow"
  #   pragma GCC diagnostic ignored "-Waddress"
  #if defined(__GNUC__) && (__GNUC__ > 5)
  #pragma GCC diagnostic ignored "-Wnonnull-compare"
  #endif
  #   include <ossim/projection/ossimSensorModel.h>
  #   include <ossim/elevation/ossimHgtRef.h>
  #   include "ossim/ossimTimeUtilities.h"
  
  # pragma GCC diagnostic pop
  
  #else
  #   include <ossim/projection/ossimSensorModel.h>
  #   include <ossim/elevation/ossimHgtRef.h>
  #   include "ossim/ossimTimeUtilities.h"
  #endif
  
  #if defined(USE_BOOST_TIME)
  #  include <boost/date_time/posix_time/posix_time.hpp>
  #include <ostream>
  #endif
  
  namespace ossimplugins
  {
  
  class OSSIM_PLUGINS_DLL ossimSarSensorModel : public ossimSensorModel
  {
  public:
  
     struct ProductType
     : private equality_comparable<ProductType>
     , private less_than_comparable<ProductType>
     {
       enum Type { SLC, GRD, MGD, GEC, EEC, SCS_B, SCS_U, MAX__, UNDEFINED__, FIRST__=0 };
  
        explicit ProductType(unsigned char value)
           : m_value(Type(value))
           {
              if (value >= MAX__)
                 throw std::runtime_error("Invalid Sar Sensor Product Type id");
           }
        ProductType(Type value)
           : m_value(Type(value))
           { assert(m_value < MAX__); }
        ProductType()
           : m_value(UNDEFINED__)
           {}
        ProductType(string_view const& s);
        ProductType& operator++() {
           assert(m_value < MAX__);
           m_value = ProductType::Type(m_value+1);
           return *this;
        }
        ProductType operator++(int) {
           ProductType tmp = *this;
           ++*this;
           return tmp;
        }
  
        string_view ToString() const;
        Type ToInternal() const { return m_value; }
        friend bool operator< (ProductType lhs, ProductType rhs)
        { return lhs.m_value <  rhs.m_value; }
        friend bool operator==(ProductType lhs, ProductType rhs)
        { return lhs.m_value == rhs.m_value; }
        friend std::ostream & operator<<(std::ostream & os, const ossimSarSensorModel::ProductType & v)
        { return os << v.ToString(); }
  
  
        static ProductType Max  ()
        { return ProductType(build_max()); }
        static ProductType begin()
        { return FIRST__; }
        static ProductType end  ()
        { return ProductType(build_max()); }
  
     private:
        struct build_max {};
        ProductType(build_max) : m_value(MAX__) {}
  
        Type m_value;
     };
  
  #if defined(USE_BOOST_TIME)
     typedef boost::posix_time::ptime            TimeType;
     typedef boost::posix_time::precise_duration DurationType;
  #else
     typedef time::ModifiedJulianDate TimeType;
     typedef time::Duration           DurationType;
  #endif
  
     struct OrbitRecordType
     {
        TimeType        azimuthTime;
        ossimEcefPoint  position;
        ossimEcefVector velocity;
        friend std::ostream & operator<<(std::ostream & os, const OrbitRecordType & v)
        {
           return os << "{ azimuthTime: " << v.azimuthTime
              <<        ", position: "    << v.position
              <<        ", velocity: "    << v.velocity
              <<        "}";
        }
     };
  
     struct GCPRecordType
     {
        TimeType azimuthTime;
        double   slantRangeTime;
        ossimDpt imPt;
        ossimGpt worldPt;
        friend std::ostream & operator<<(std::ostream & os, const GCPRecordType & v)
        {
           return os << "{ azimuthTime: " << v.azimuthTime
              <<        ", imPt: "       << v.imPt
              <<        ", worldPt: "    << v.worldPt
              <<        "}";
        }
     };
  
     struct BurstRecordType
     {
        TimeType      azimuthStartTime;
        unsigned long startLine;
        TimeType      azimuthStopTime;
        unsigned long endLine;
        unsigned long startSample;
        unsigned long endSample;
        double        azimuthAnxTime;
        friend std::ostream & operator<<(std::ostream & os, const BurstRecordType & v)
        {
           return os << "{ azimuthStartTime: " << v.azimuthStartTime
              <<        ", azimuthStopTime: "  << v.azimuthStopTime
              <<        ", startLine: "        << v.startLine
              <<        ", stopLine: "         << v.endLine
  	    <<        ", startSample: "      << v.startSample
              <<        ", stopSample: "       << v.endSample
  	    <<        ", azimuthAnxTime:"    << v.azimuthAnxTime
              <<        "}";
        }
     };
  
     struct IsWithin {
        IsWithin(BurstRecordType const& br)
           : m_start(br.azimuthStartTime)
           , m_stop(br.azimuthStartTime)
           {}
        bool operator()(TimeType v) const
        { return m_start <= v && v < m_stop; }
     private:
        TimeType m_start;
        TimeType m_stop;
     };
  
     struct DoesContain {
        DoesContain(TimeType value)
           : m_value(value)
           {}
        bool operator()(BurstRecordType const& br) const
        { return br.azimuthStartTime <= m_value && m_value < br.azimuthStopTime; }
     private:
        TimeType m_value;
     };
  
     struct CoordinateConversionRecordType
     {
        TimeType            azimuthTime;
        double              rg0;
        std::vector<double> coefs;
        friend std::ostream & operator<<(std::ostream & os, const CoordinateConversionRecordType & v)
        {
           return os << "{ azimuthTime: " << v.azimuthTime
              <<        ", rg0: "         << v.rg0
              <<        ", coeffs: "      << v.coefs.size()
              <<        "}";
        }
     };
  
     /** Constructor */
     ossimSarSensorModel();
  
  #if ! (defined(BOOST_NO_DEFAULTED_FUNCTIONS) || defined(BOOST_NO_CXX1_DEFAULTED_FUNCTIONS))
     /** Copy constructor */
     ossimSarSensorModel(ossimSarSensorModel const& m) = default;
     /** Move constructor */
     ossimSarSensorModel(ossimSarSensorModel && m) = default;
  
     /** Destructor */
     virtual ~ossimSarSensorModel() = default;
  #endif
  
     virtual void lineSampleHeightToWorld(const ossimDpt& imPt, const double & heightEllipsoid, ossimGpt& worldPt) const override;
  
     virtual void lineSampleToWorld(const ossimDpt& imPt, ossimGpt& worldPt) const override;
  
  
     /** This method implement inverse sar geolocation using method found
      *  in ESA document "Guide to ASAR geocoding" (ref
      *  RSL-ASAR-GC-AD). Equation numbers can be found in source code
      *  comments.
      *
      * \param[in] worldPt World point to geocode
      * \param[out] imPt Corresponding estimated image point
      */
     virtual void worldToLineSample(const ossimGpt& worldPt, ossimDpt & imPt) const override;
  
     /**
      * Sub-routine of lineSampleToWorld that computes azimuthTime and
      * slant range time from worldPoint
      *
      * \param[in] worldPoint World point to geocode
      * \param[out] azimuthTime Estimated zero-doppler azimuth time
      * \param[out] rangeTime Estimated range time
      * \param[out] interpSensorPos interpolated ECEF sensor position
      * \param[out] interpSensorVel interpolated ECEF sensor velocity
      * \return True if success, false otherwise. In this case,
      * azimuthTime and rangeTime will not be modified.
      */
     /*virtual*/ bool worldToAzimuthRangeTime(const ossimGpt& worldPt, TimeType & azimuthTime, double & rangeTime, ossimEcefPoint & interpSensorPos, ossimEcefVector & interpSensorVel) const;
  
     /**
      * This method implement inverse sar geolocation similar to
      * worldToLineSample, except that it also returns (y,z)
      * coordinates, defined as follows:
      * Let n = |sensorPos|,
      * ps2 = scalar_product(sensorPos,worldPoint)
      * d = distance(sensorPos,worldPoint)
      *
      * z = n - ps2/n
      * y = sqrt(d*d - z*z)
      * 
      * sign of y is furher adapted to be inverted if sensor is left or
      * right looking 
      *
      * \param[in] worldPoint World point to geocode
      * \param[out] imPt Corresponding estimated image point
      * \param[out] y 
      * \param[out] z 
      * \return True if success, false otherwise. In this case,
      */
     void worldToLineSampleYZ(const ossimGpt& worldPt, ossimDpt& imPt, double & y, double & z) const;
     
     void lineSampleToAzimuthRangeTime(const ossimDpt & imPt, TimeType & azimuthTime, double & rangeTime) const;
  
     // TODO: document me
     bool autovalidateInverseModelFromGCPs(const double & xtol = 1, const double & ytol = 1, const double azTimeTol = 500, const double &rangeTimeTo=0.0000000001) const;
  
     // TODO: document me
     bool autovalidateForwardModelFromGCPs(double resTol = 25);
  
     //Pure virtual in base class
     bool useForward() const override;
  
     void optimizeTimeOffsetsFromGcps();
  
     /**
      * This method will perform a deburst operation, and return the
      * a vector of lines range of lines to keep in the image file.
      * Note that the deburst operation has no effect if theBurstRecords
      * contains a single burst. Otherwise it will merge burst together
      * into a single burst, and update GCPs accordingly.
      * Two modes are available for the output image : with all samples and
      * with only valid samples. A pair of samples specifies first and last samples
      * \return true if the deburst operation succeeded. No changes is
      * made to the object if the operation fails.
      * \param lines A container for the lines ranges to keep in the
      * deburst image.
      * \param samples A container for the samples to keep in the
      * deburst image.
      * \param onlyValidSample If true, the selected mode is with only valid sample.
      */
     bool deburst(std::vector<std::pair<unsigned long,unsigned long> >& lines, 
  		std::pair<unsigned long,unsigned long> & samples, bool onlyValidSample=false);
     
  
     /**
      * This is a helper function to convert image line to deburst image
      * line.
      * \param lines The vector of lines range to keep
      * \param imageLine The input image line
      * \param deburstLine The output deburst line
      * \return True if imageLine is within a preserved range, false otherwise
      */
     static bool imageLineToDeburstLine(const std::vector<std::pair<unsigned long,unsigned long> >& lines, unsigned long imageLine, unsigned long & deburstLine);
  
     /** 
      * This is a helper function to convert deburst line to input image
      * line
      * \param lines The vector of lines range to keep
      * \param imageLine The input deburst line
      * \param deburstLine The output original image line
      */
     static void deburstLineToImageLine(const std::vector<std::pair<unsigned long,unsigned long> >& lines, unsigned long deburstLine, unsigned long & imageLine);
  
     /**
      * This method will perform an extration of one burst. It wil return the
      * lines and samples to extract in the image file.
      * \return true if the extraction operation succeeded. No changes is
      * made to the object if the operation fails.
      * \param burst_index Index of Burst.
      * \param lines A container for the lines to keep in the
      * standalone burst.
      * \param samples A container for the samples to keep in the
      * standalone burst.
      */
     bool burstExtraction(const unsigned int burst_index, std::pair<unsigned long,unsigned long> & lines, 
  			std::pair<unsigned long,unsigned long> & samples, bool allPixels=false);
  
     /**
      * This method will perform a deburst and concatenation operation, and return the
      * vector of lines and the vector of samples to keep in the 
      * image file. The lines and samples represents start/size into each indepedent bursts. 
      * Note that the deburst operation has no effect if theBurstRecords
      * contains a single burst. Otherwise it will merge burst together
      * into a single burst, and update GCPs accordingly.
      * \return true if the deburst operation succeeded. No changes is
      * made to the object if the operation fails.
      * \param lines A container for the lines ranges to keep in the
      * deburst image.
      * \param samples A container for the samples ranges to keep in the
      * deburst image.
      * \param lines A Boolean to indicate only valids samples are required.
      */
     bool deburstAndConcatenate(std::vector<std::pair<unsigned long,unsigned long> >& linesBursts, 
  			      std::vector<std::pair<unsigned long,unsigned long> >& samplesBursts,
  			      unsigned int & linesOffset, unsigned int first_burstInd,
  			      bool inputWithInvalidPixels=false);
  
     /**
      * This method will estime the overlap area between two bursts and return the
      * vector of lines and the vector of samples (with two elements : Burst Up and Burst Low). 
      * Note that this operation has no effect if theBurstRecords
      * contains a single burst. 
      * \return true if this operation succeeded. No changes is
      * made to the object if the operation fails.
      * \param linesUp A container for the lines ranges to keep into the first Burst
      * \param linesLow A container for the lines ranges to keep into the second Burst
      * \param samplesUp A container for the samples ranges to keep into the first Burst.
      * \param samplesDown A container for the samples ranges to keep into the second Burst.
      * \param burstIndUp Index of the first Burst
      * \param inputWithInvalidPixels A Boolean to indicate if invalids pixels are into inputs.
      */
     bool overlap(std::pair<unsigned long,unsigned long> & linesUp, 
  		std::pair<unsigned long,unsigned long> & linesLow,
  		std::pair<unsigned long,unsigned long> & samplesUp,
  		std::pair<unsigned long,unsigned long> & samplesLow,
  		unsigned int burstIndUp,
  		bool inputWithInvalidPixels=false);
  
     /**
      * Returns pointer to a new instance, copy of this.
      */
     virtual ossimObject* dup() const override;
  
     //TODO: Add virtual method readAnnotationFile?
  
     virtual bool saveState(ossimKeywordlist      & kwl, const char* prefix=NULL) const override;
     virtual bool loadState(ossimKeywordlist const& kwl, const char* prefix=NULL) override;
  
     bool isGRD() const {
        switch (theProductType.ToInternal()) {
           case ProductType::GRD:
           case ProductType::MGD:
           case ProductType::GEC:
           case ProductType::EEC:
              return true;
           default:
              return false;
        }
     }
  
     virtual std::ostream& print(std::ostream& out) const override;
  protected:
  #if defined(__clang__)
  #pragma clang diagnostic push
  
  #if defined(__apple_build_version__)
  /* Apple's 3.6.0 based clang doesn't support -Winconsistent-missing-override */
  #if __apple_build_version__ >= 7000053
  #pragma clang diagnostic ignored "-Winconsistent-missing-override"
  #endif
  #elif __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 7)
  #pragma clang diagnostic ignored "-Winconsistent-missing-override"
  #endif
     TYPE_DATA
  #pragma clang diagnostic pop
  #else
     TYPE_DATA
  #endif
  
     /**
      * Compute range and doppler frequency from an input point, sensor
      * position and velocity.
      *
      * \param[in] inputPt The target point
      * \param[in] sensorPos The sensor position
      * \param[in] sensorvel The sensor velocity
      * \param[out] range Estimated range
      * \param[out] doppler Estimated doppler frequency
      */
     virtual void computeRangeDoppler(const ossimEcefPoint & inputPt, const ossimEcefPoint & sensorPos, const ossimEcefVector sensorVel, double & range, double & doppler) const;
  
     /**
      * Interpolate sensor position and velocity at given azimuth time
      * using lagragian interpolation of orbital records.
      *
      * \param[in] azimuthTime The time at which to interpolate
      * \param[out] sensorPos Interpolated sensor position
      * \param[out] sensorvel Interpolated sensor velocity
      * \param[in] deg Degree of lagragian interpolation
      */
     /*virtual*/ void interpolateSensorPosVel(const TimeType & azimuthTime, ossimEcefPoint& sensorPos, ossimEcefVector& sensorVel, unsigned int deg = 8) const;
  
     /**
      * Convert slant range to ground range by interpolating slant range
      * to ground range coefficients.
      *
      * \param[in] slantRangeTime The slantRange to convert (meters)
      * \param[in] azimuthTime The corresponding azimuth time
      * \param[out] groundRange The estimated ground range (meters)
      */
     /*virtual*/ void slantRangeToGroundRange(const double & slantRange, const TimeType & azimuthTime, double & groundRange) const;
  
     // TODO: Document me
     /*virtual*/ void groundRangeToSlantRange(const double & groundRange, const TimeType & azimuthTime, double & slantRange) const;
  
     // TODO: Document me
     /*virtual*/ void applyCoordinateConversion(const double & in, const TimeType& azimuthTime, const std::vector<CoordinateConversionRecordType> & records, double & out) const;
     /**
      * Estimate the zero-doppler azimuth time and corresponding sensor
      * position and velocity from the inputPt.
      *
      * \param[in] inputPt The point to estimated zero-doppler time on
      * \param[out] interpAzimuthTime Interpolated azimuth time
      * \param[out] interpSensorPos Interpolated sensor position
      * \param[out] interpSensorVel Interpolated sensor velocity
      * \return True if success, false otherwise. In this case, output
      * parameters are left untouched.
      */
     /*virtual*/ bool zeroDopplerLookup(const ossimEcefPoint & inputPt, TimeType & interpAzimuthTime, ossimEcefPoint & interpSensorPos, ossimEcefVector & interpSensorVel) const;
  
     /**
      * Compute the bistatic correction to apply.
      *
      * \param[in] inputPt The point to compute bistatic correction on
      * \param[in] sensorPos The corresponding sensor position
      * \param[out] bistaticCorrection The estimated bistatic correction
      */
     /*virtual*/ void computeBistaticCorrection(const ossimEcefPoint & inputPt, const ossimEcefPoint & sensorPos, DurationType & bistaticCorrection) const;
  
     /**
      * Convert azimuth time to fractional line.
      *
      * \param[in] azimuthTime The azimuth time to convert
      * \param[out] The estimated fractional line
      */
     /*virtual*/ // TODO: check why virtual
     void azimuthTimeToLine(const TimeType & azimuthTime, double & line) const;
  
     // TODO: document me
     /*virtual*/ // TODO: check why virtual
     void lineToAzimuthTime(const double & line, TimeType & azimuthTime) const;
  
     // TODO: document me
     /*virtual*/ // TODO: check why virtual
     bool projToSurface(const GCPRecordType & initGcp, const ossimDpt & target, const ossimHgtRef & hgtRef, ossimEcefPoint & ellpt) const;
  
     /**
      * Finds closest GCP.
      *
      * \param[in] imPt  «imPt-explanations»
      * \return the closest GCP record to \c imPt.
      * \throw None
      * \pre `theGCPRecords` shall not be empty.
      */
     GCPRecordType const& findClosestGCP(ossimDpt const& imPt) const;
  
  
     std::vector<OrbitRecordType>                theOrbitRecords;
     std::vector<GCPRecordType>                  theGCPRecords;
     std::vector<BurstRecordType>                theBurstRecords;
     std::vector<CoordinateConversionRecordType> theSlantRangeToGroundRangeRecords;
     std::vector<CoordinateConversionRecordType> theGroundRangeToSlantRangeRecords;
  
     double                                      theRadarFrequency; // in Hz
     DurationType                                theAzimuthTimeInterval;
     double                                      theNearRangeTime; // in seconds
     double                                      theRangeSamplingRate; // in Hz
     double                                      theRangeResolution; // in meters
     bool                                        theBistaticCorrectionNeeded; // Do we need to compute
     // bistatic correction ?
     ProductType                                 theProductType; // GRD/SLC
     DurationType                                theAzimuthTimeOffset; // Offset computed
     double                                      theRangeTimeOffset; // Offset in seconds, computed
     bool                                        theRightLookingFlag;
     
     TimeType                                    theFirstLineTime;
     TimeType                                    theLastLineTime;
  
     unsigned int                                theGeomVersion; // version of input geom
  
     unsigned long                                theNumberOfLinesPerBurst;
     unsigned long                                theNumberOfSamplesPerBurst;
  
     bool redaptMedataAfterDeburst;
     
     static const double C;
  
     static const unsigned int thePluginVersion; // version of the SarSensorModel plugin
     static const unsigned int thePluginVersionMin; // minimal version required of the SarSensorModel plugin
  
  private:
     /** Disabled assignment operator.  */
     ossimSarSensorModel& operator=(ossimSarSensorModel const& rhs);
     // Disable a warning, and disable inherited buggy assignment (buggy because
     // value semantics are incompatible with public inheritance thanks to
     // slicing)
     using ossimSensorModel::operator=;
     using ossimOptimizableProjection::operator=;
  };
  
  }
  
  #endif
  