LCOV - code coverage report
Current view: top level - imageanalysis/ImageAnalysis - ImageRegridder.tcc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 0 386 0.0 %
Date: 2024-10-12 00:35:29 Functions: 0 12 0.0 %

          Line data    Source code
       1             : //# Copyright (C) 1998,1999,2000,2001,2003
       2             : //# Associated Universities, Inc. Washington DC, USA.
       3             : //#
       4             : //# This program is free software; you can redistribute it and/or modify it
       5             : //# under the terms of the GNU General Public License as published by the Free
       6             : //# Software Foundation; either version 2 of the License, or (at your option)
       7             : //# any later version.
       8             : //#
       9             : //# This program is distributed in the hope that it will be useful, but WITHOUT
      10             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      11             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      12             : //# more details.
      13             : //#
      14             : //# You should have received a copy of the GNU General Public License along
      15             : //# with this program; if not, write to the Free Software Foundation, Inc.,
      16             : //# 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      17             : //#
      18             : //# Correspondence concerning AIPS++ should be addressed as follows:
      19             : //#        Internet email: casa-feedback@nrao.edu.
      20             : //#        Postal address: AIPS++ Project Office
      21             : //#                        National Radio Astronomy Observatory
      22             : //#                        520 Edgemont Road
      23             : //#                        Charlottesville, VA 22903-2475 USA
      24             : //#
      25             : //# $Id: $
      26             : 
      27             : #include <imageanalysis/ImageAnalysis/ImageRegridder.h>
      28             : 
      29             : #include <imageanalysis/ImageAnalysis/ImageFactory.h>
      30             : #include <imageanalysis/ImageAnalysis/ImageMetaData.h>
      31             : #include <imageanalysis/ImageAnalysis/SubImageFactory.h>
      32             : #include <casacore/images/Images/ImageConcat.h>
      33             : #include <casacore/images/Images/ImageRegrid.h>
      34             : #include <casacore/scimath/Mathematics/Geometry.h>
      35             : 
      36             : #include <casacore/casa/BasicSL/STLIO.h>
      37             : #include <memory>
      38             : 
      39             : namespace casa {
      40             : 
      41             : template<class T> const String  ImageRegridder<T>::_class = "ImageRegridder";
      42             : 
      43           0 : template<class T> ImageRegridder<T>::ImageRegridder(
      44             :     const SPCIIT image, const casacore::Record *const regionRec,
      45             :     const casacore::String& maskInp, const casacore::String& outname,
      46             :     casacore::Bool overwrite, const casacore::CoordinateSystem& csysTo,
      47             :     const casacore::IPosition& axes, const casacore::IPosition& shape
      48             : ) : ImageRegridderBase<T>(
      49             :         image, regionRec, maskInp, outname,
      50             :         overwrite, csysTo, axes, shape
      51           0 :     ), _debug(0) {}
      52             : 
      53           0 : template<class T> ImageRegridder<T>::ImageRegridder(
      54             :     const SPCIIT image, const casacore::String& outname,
      55             :     const SPCIIT templateIm, const casacore::IPosition& axes,
      56             :     const casacore::Record *const regionRec, const casacore::String& maskInp,
      57             :     casacore::Bool overwrite, const casacore::IPosition& shape
      58             : )  : ImageRegridderBase<T>(
      59             :         image, regionRec, maskInp, outname, overwrite,
      60             :         templateIm->coordinates(), axes, shape
      61             :     ),
      62           0 :     _debug(0) {}
      63             : 
      64           0 : template<class T> ImageRegridder<T>::~ImageRegridder() {}
      65             : 
      66           0 : template<class T> SPIIT ImageRegridder<T>::regrid() const {
      67           0 :     _subimage = SubImageFactory<T>::createImage(
      68           0 :         *this->_getImage(), "", *this->_getRegion(), this->_getMask(),
      69           0 :         this->_getDropDegen(), false, false, this->_getStretch()
      70             :     );
      71           0 :     auto regridByVel = false;
      72           0 :     const auto axes = this->_getAxes();
      73           0 :     auto hasMultipleBeams = this->_getImage()->imageInfo().hasMultipleBeams();
      74           0 :     const auto& csys = this->_getImage()->coordinates();
      75           0 :     if (
      76           0 :         (this->_getSpecAsVelocity() || hasMultipleBeams)
      77           0 :         && csys.hasSpectralAxis()
      78           0 :         && this->_getTemplateCoords().hasSpectralAxis()
      79             :     ) {
      80           0 :         auto inputSpecAxis = csys.spectralAxisNumber(false);
      81           0 :         auto isInputSpecDegen = _subimage->shape()[inputSpecAxis] == 1;
      82           0 :         if (axes.empty()) {
      83           0 :             ThrowIf(
      84             :                 hasMultipleBeams,
      85             :                 "An image with multiple beams cannot be regridded along the "
      86             :                 "spectral axis. You may wish to convolve all channels to a "
      87             :                 "common resolution and retry"
      88             :             );
      89           0 :             if (! isInputSpecDegen && this->_getSpecAsVelocity()) {
      90           0 :                 regridByVel = true;
      91             :             }
      92             :         }
      93             :         else {
      94           0 :             auto specAxis = csys.spectralAxisNumber();
      95           0 :             for (uInt i=0; i<axes.size(); ++i) {
      96           0 :                 if (axes[i] == specAxis) {
      97           0 :                     ThrowIf(
      98             :                         hasMultipleBeams,
      99             :                         "An image with multiple beams cannot be regridded "
     100             :                         "along the spectral axis. You may wish to convolve all "
     101             :                         "channels to a common resolution and retry"
     102             :                     );
     103           0 :                     if (! isInputSpecDegen && this->_getSpecAsVelocity()) {
     104           0 :                         regridByVel = true;
     105             :                     }
     106           0 :                     break;
     107             :                 }
     108             :             }
     109             :         }
     110             :     }
     111           0 :     auto workIm = regridByVel ? this->_regridByVelocity() : this->_regrid();
     112           0 :     return this->_prepareOutputImage(*workIm);
     113           0 : }
     114             : 
     115           0 : template<class T> SPIIT ImageRegridder<T>::_regrid() const {
     116           0 :     if (! _subimage) {
     117             :         // for when this method is called directly by regridByVelocity
     118           0 :         _subimage = SubImageFactory<T>::createImage(
     119           0 :             *this->_getImage(), "", *this->_getRegion(), this->_getMask(),
     120           0 :             this->_getDropDegen(), false, false, this->_getStretch()
     121             :         );
     122             :     }
     123           0 :     *this->_getLog() << LogOrigin(_class, __func__);
     124           0 :     ThrowIf(
     125             :         ImageMask::isAllMaskFalse(*_subimage),
     126             :         "All selected pixels are masked"
     127             :     );
     128           0 :     const auto csysFrom = _subimage->coordinates();
     129           0 :     CoordinateSystem csysTo = this->_getTemplateCoords();
     130           0 :     csysTo.setObsInfo(csysFrom.obsInfo());
     131           0 :     std::set<Coordinate::Type> coordsToRegrid;
     132           0 :     auto csys = ImageRegrid<T>::makeCoordinateSystem(
     133           0 :         *this->_getLog(), coordsToRegrid, csysTo, csysFrom, this->_getAxes(),
     134           0 :         _subimage->shape(), false
     135             :     );
     136           0 :     ThrowIf(
     137             :         csys.nPixelAxes() != this->_getShape().size(),
     138             :         "The number of pixel axes in the output shape and Coordinate System "
     139             :         "must be the same. Shape has size "
     140             :         + casacore::String::toString(this->_getShape().size()) + ". Output "
     141             :         "coordinate system has "
     142             :         + casacore::String::toString(csys.nPixelAxes()) + " axes"
     143             :     );
     144           0 :     this->_checkOutputShape(*_subimage, coordsToRegrid);
     145           0 :     SPIIT workIm(new TempImage<T>(this->_getKludgedShape(), csys));
     146           0 :     ImageUtilities::copyMiscellaneous(*workIm, *_subimage);
     147           0 :     String maskName("");
     148           0 :     ImageMaskAttacher::makeMask(
     149           0 :         *workIm, maskName, true, true, *this->_getLog(), true
     150             :     );
     151           0 :     ThrowIf (
     152             :         ! this->_doImagesOverlap(_subimage, workIm),
     153             :         "There is no overlap between the (region chosen in) the input image"
     154             :         " and the output image with respect to the axes being regridded."
     155             :     );
     156           0 :     if (
     157           0 :         coordsToRegrid.find(Coordinate::SPECTRAL) != coordsToRegrid.end()
     158           0 :         && fabs(csys.spectralCoordinate().increment()[0])
     159           0 :             > fabs(csysFrom.spectralCoordinate().increment()[0])
     160             :     ) {
     161           0 :         *this->_getLog() << LogOrigin(getClass(), __func__) << LogIO::WARN
     162             :             << "Warning: template/imagename relative channel size is "
     163             :             << fabs(
     164           0 :                 csys.spectralCoordinate().increment()[0]
     165           0 :                 /csysFrom.spectralCoordinate().increment()[0]
     166           0 :             ) << LogIO::POST;
     167           0 :         *this->_getLog() << LogOrigin(getClass(), __func__)
     168             :             << LogIO::WARN << "imregrid/ia.regrid() interpolates over spectral "
     169             :             << "channels and does not average channels together. Noise in your "
     170             :             << "resulting image will be the noise in the original individual "
     171             :             << "channels, not the averaged channel noise. To average output "
     172             :             << "channels together, use specsmooth (or ia.boxcar() or "
     173             :             << "ia.hanning() to smooth the spectral axis of your input cube to "
     174             :             << "close to desired resolution and use imregrid/ia.regrid() to "
     175             :             << "regrid it to the desired spectral coordinate grid."
     176           0 :             << LogIO::POST;
     177             :     }
     178           0 :     ImageRegrid<T> ir;
     179           0 :     ir.showDebugInfo(_debug);
     180           0 :     ir.disableReferenceConversions(! this->_getDoRefChange());
     181           0 :     ir.regrid(
     182           0 :         *workIm, this->_getMethod(), this->_getAxes(), *_subimage,
     183           0 :         this->_getReplicate(), this->_getDecimate(), true,
     184           0 :         this->_getForceRegrid()
     185             :     );
     186           0 :     if (! this->_getOutputStokes().empty()) {
     187           0 :         workIm = this->_decimateStokes(workIm);
     188             :     }
     189           0 :     ThrowIf(
     190             :         workIm->hasPixelMask() && ImageMask::isAllMaskFalse(*workIm),
     191             :         "All output pixels are masked"
     192             :         + casacore::String(
     193             :             this->_getDecimate() > 1 && this->_regriddingDirectionAxes()
     194             :             ? ". You might want to try decreasing the value of decimate if you "
     195             :                 "are regridding direction axes"
     196             :             : ""
     197             :         )
     198             :     );
     199           0 :     if (this->_getNReplicatedChans() > 1) {
     200             :         // spectral channel needs to be replicated _nReplicatedChans times,
     201             :         // and spectral coordinate of the template needs to be copied to the
     202             :         // output.
     203           0 :         IPosition finalShape = this->_getKludgedShape();
     204           0 :         Int specAxisNumber = workIm->coordinates().spectralAxisNumber(false);
     205           0 :         auto fillerPixels = workIm->get();
     206           0 :         const auto fillerMask = workIm->pixelMask().get();
     207           0 :         finalShape[specAxisNumber] = this->_getNReplicatedChans();
     208           0 :         SPIIT replicatedIm(new casacore::TempImage<T>(finalShape, csys));
     209           0 :         std::dynamic_pointer_cast<casacore::TempImage<T>>(replicatedIm)->attachMask(
     210           0 :             casacore::ArrayLattice<Bool>(finalShape)
     211             :         );
     212           0 :         auto& pixelMask = replicatedIm->pixelMask();
     213           0 :         auto n = this->_getNReplicatedChans();
     214           0 :         IPosition where(finalShape.size(), 0);
     215           0 :         for (uInt i=0; i<n; ++i) {
     216           0 :             where[specAxisNumber] = i;
     217           0 :             replicatedIm->putSlice(fillerPixels, where);
     218           0 :             pixelMask.putSlice(fillerMask, where);
     219             :         }
     220           0 :         auto spTo = this->_getTemplateCoords().spectralCoordinate();
     221           0 :         auto csysFinal = replicatedIm->coordinates();
     222           0 :         csysFinal.replaceCoordinate(spTo, csysFinal.spectralCoordinateNumber());
     223           0 :         replicatedIm->setCoordinateInfo(csysFinal);
     224           0 :         workIm = replicatedIm;
     225           0 :     }
     226           0 :     return workIm;
     227           0 : }
     228             : 
     229           0 : template<class T> SPIIT ImageRegridder<T>::_decimateStokes(SPIIT workIm) const {
     230           0 :     ImageMetaData<T> md(workIm);
     231           0 :     if (this->_getOutputStokes().size() >= md.nStokes()) {
     232           0 :         return workIm;
     233             :     }
     234           0 :     CasacRegionManager rm(workIm->coordinates());
     235           0 :     casacore::String diagnostics;
     236           0 :     casacore::uInt nSelectedChannels = 0;
     237           0 :     if (this->_getOutputStokes().size() == 1) {
     238           0 :         auto stokes = this->_getOutputStokes()[0];
     239           0 :         auto region = rm.fromBCS(
     240             :             diagnostics, nSelectedChannels, stokes,
     241             :             "", CasacRegionManager::USE_FIRST_STOKES,
     242           0 :             "", workIm->shape()
     243             :         ).toRecord("");
     244             :         return SubImageFactory<T>::createImage(
     245           0 :             *workIm, "", region, "", false, false, false, false
     246           0 :         );
     247           0 :     }
     248             :     else {
     249             :         // Only include the wanted stokes
     250           0 :         std::shared_ptr<casacore::ImageConcat<T> > concat(
     251           0 :             new casacore::ImageConcat<T>(
     252           0 :                 workIm->coordinates().polarizationAxisNumber(false)
     253             :             )
     254             :         );
     255           0 :         for(casacore::String stokes: this->_getOutputStokes()) {
     256           0 :             auto region = rm.fromBCS(
     257             :                 diagnostics, nSelectedChannels, stokes,
     258             :                 "", CasacRegionManager::USE_FIRST_STOKES,
     259           0 :                 "", workIm->shape()
     260             :             ).toRecord("");
     261           0 :             concat->setImage(
     262           0 :                 *SubImageFactory<T>::createImage(
     263           0 :                     *workIm, "", region, "", false, false, false, false
     264             :                 ), true
     265             :             );
     266             :         }
     267           0 :         return concat;
     268           0 :     }
     269           0 : }
     270             : 
     271           0 : template<class T> void ImageRegridder<T>::_checkOutputShape(
     272             :     const casacore::SubImage<T>& subImage,
     273             :     const std::set<casacore::Coordinate::Type>& coordsToRegrid
     274             : ) const {
     275           0 :     const auto& csysFrom = subImage.coordinates();
     276           0 :     std::set<casacore::Coordinate::Type> coordsNotToRegrid;
     277           0 :     auto nCoordinates = csysFrom.nCoordinates();
     278           0 :     auto inputShape = subImage.shape();
     279           0 :     auto axes = this->_getAxes();
     280           0 :     auto outputAxisOrder = axes;
     281           0 :     for (uInt i=axes.size(); i<this->_getKludgedShape().size(); ++i) {
     282           0 :         outputAxisOrder.append(casacore::IPosition(1, i));
     283             :     }
     284           0 :     const auto coordsToRegridEnd = coordsToRegrid.end();
     285           0 :     for (uInt i=0; i<nCoordinates; ++i) {
     286           0 :         const auto coordType = csysFrom.coordinate(i).type();
     287           0 :         if (coordsToRegrid.find(coordType) == coordsToRegridEnd) {
     288           0 :             auto coordAxes = csysFrom.worldAxes(i);
     289           0 :             for(casacore::uInt oldAxis: coordAxes) {
     290           0 :                 casacore::uInt count = 0;
     291           0 :                 for(casacore::uInt newAxis: outputAxisOrder) {
     292           0 :                     ThrowIf(
     293             :                         newAxis == oldAxis
     294             :                         && inputShape[oldAxis]
     295             :                             != this->_getKludgedShape()[count],
     296             :                         "Input axis " + casacore::String::toString(oldAxis)
     297             :                         + " (coordinate type "
     298             :                         + casacore::Coordinate::typeToString(coordType)
     299             :                         + "), which will not be regridded and corresponds to"
     300             :                         + "output axis casacore::String::toString(newAxis), "
     301             :                         + "has length "
     302             :                         + casacore::String::toString(inputShape[oldAxis])
     303             :                         + " where as the specified length of the corresponding "
     304             :                         + "output axis is "
     305             :                         + casacore::String::toString(
     306             :                             this->_getKludgedShape()[count]
     307             :                         ) + ". If a coordinate is not regridded, its input and "
     308             :                         + "output axes must have the same length."
     309             :                     );
     310           0 :                     ++count;
     311             :                 }
     312             :             }
     313           0 :         }
     314             :     }
     315           0 : }
     316             : 
     317           0 : template<class T> SPIIT ImageRegridder<T>::_regridByVelocity() const {
     318           0 :     const auto csysTo = this->_getTemplateCoords();
     319           0 :     const auto specCoordTo = csysTo.spectralCoordinate();
     320           0 :     const auto specCoordFrom
     321           0 :         = this->_getImage()->coordinates().spectralCoordinate();
     322           0 :     ThrowIf(
     323             :         specCoordTo.frequencySystem(true)
     324             :         != specCoordFrom.frequencySystem(true),
     325             :         "Image to be regridded has different frequency system from template "
     326             :         "coordinate system."
     327             :     );
     328           0 :     ThrowIf(
     329             :         specCoordTo.restFrequency() == 0,
     330             :         "Template spectral coordinate rest frequency is 0, "
     331             :         "so cannot regrid by velocity."
     332             :     );
     333           0 :     ThrowIf(
     334             :         specCoordFrom.restFrequency() == 0,
     335             :         "Input image spectral coordinate rest frequency is 0, so cannot regrid "
     336             :         "by velocity."
     337             :     );
     338           0 :     std::unique_ptr<casacore::CoordinateSystem> csys(
     339           0 :         dynamic_cast<casacore::CoordinateSystem *>(csysTo.clone())
     340             :     );
     341           0 :     auto templateSpecCoord = csys->spectralCoordinate();
     342           0 :     std::unique_ptr<casacore::CoordinateSystem> coordClone(
     343           0 :         dynamic_cast<casacore::CoordinateSystem *>(
     344           0 :             _subimage->coordinates().clone()
     345             :         )
     346             :     );
     347           0 :     auto newSpecCoord = coordClone->spectralCoordinate();
     348           0 :     casacore::Double newVelRefVal = 0;
     349           0 :     std::pair<casacore::Double, casacore::Double> toVelLimits;
     350           0 :     auto inSpecAxis = coordClone->spectralAxisNumber(false);
     351           0 :     casacore::Double newVelInc = 0.0;
     352           0 :     for (casacore::uInt i=0; i<2; ++i) {
     353             :         // i == 0 => csysTo, i == 1 => csysFrom
     354           0 :         auto *cs = i == 0 ? csys.get() : coordClone.get();
     355             :         // create and replace the coordinate system's spectral coordinate with
     356             :         // a linear coordinate which describes the velocity axis. In this way
     357             :         // we can regrid by velocity.
     358           0 :         Int specCoordNum = cs->spectralCoordinateNumber();
     359           0 :         auto specCoord = cs->spectralCoordinate();
     360           0 :         if (
     361           0 :             specCoord.frequencySystem(false) != specCoord.frequencySystem(true)
     362             :         ) {
     363             :             // the underlying conversion system is different from the overlying
     364             :             // one, so this is pretty confusing. We want the underlying one also
     365             :             // be the overlying one before we regrid.
     366           0 :             casacore::Vector<casacore::Double> newRefVal;
     367           0 :             auto newRefPix = specCoord.referencePixel()[0];
     368           0 :             specCoord.toWorld(
     369             :                 newRefVal,
     370           0 :                 casacore::Vector<casacore::Double>(1, newRefPix)
     371             :             );
     372           0 :             casacore::Vector<casacore::Double> newVal;
     373           0 :             specCoord.toWorld(
     374           0 :                 newVal, casacore::Vector<casacore::Double>(1, newRefPix+1)
     375             :             );
     376           0 :             specCoord = SpectralCoordinate(
     377             :                 specCoord.frequencySystem(true), newRefVal[0],
     378           0 :                 (newVal[0] - newRefVal[0]), newRefPix, specCoord.restFrequency()
     379             :             );
     380           0 :             if (cs == coordClone.get()) {
     381           0 :                 newSpecCoord = specCoord;
     382             :             }
     383           0 :         }
     384           0 :         casacore::Double freqRefVal = specCoord.referenceValue()[0];
     385             :         casacore::Double velRefVal;
     386           0 :         ThrowIf(
     387             :             ! specCoord.frequencyToVelocity(velRefVal, freqRefVal),
     388             :             "Unable to determine reference velocity"
     389             :         );
     390           0 :         casacore::Double vel0 = 0;
     391           0 :         casacore::Double vel1 = 0;
     392           0 :         ThrowIf(
     393             :             ! specCoord.pixelToVelocity(vel0, 0.0)
     394             :             || ! specCoord.pixelToVelocity(vel1, 1.0),
     395             :             "Unable to determine velocity increment"
     396             :         );
     397           0 :         if (i == 0) {
     398           0 :             toVelLimits.first = vel0;
     399           0 :             specCoord.pixelToVelocity(
     400           0 :                 toVelLimits.second, this->_getShape()[inSpecAxis] - 1
     401             :             );
     402           0 :             if (toVelLimits.first > toVelLimits.second) {
     403           0 :                 std::swap(toVelLimits.first, toVelLimits.second);
     404             :             }
     405             :         }
     406           0 :         if (i == 1) {
     407           0 :             std::pair<casacore::Double, casacore::Double> fromVelLimits;
     408           0 :             specCoord.pixelToVelocity(fromVelLimits.first, 0);
     409           0 :             specCoord.pixelToVelocity(
     410           0 :                 fromVelLimits.second, _subimage->shape()[inSpecAxis] - 1
     411             :             );
     412           0 :             if (fromVelLimits.first > fromVelLimits.second) {
     413           0 :                 std::swap(fromVelLimits.first, fromVelLimits.second);
     414             :             }
     415           0 :             ThrowIf(
     416             :                 (
     417             :                     fromVelLimits.first > toVelLimits.second
     418             :                     && ! casacore::near(fromVelLimits.first, toVelLimits.second)
     419             :                 )
     420             :                 || (
     421             :                     fromVelLimits.second < toVelLimits.first
     422             :                     && ! casacore::near(fromVelLimits.second, toVelLimits.first)
     423             :                 ),
     424             :                 "Request to regrid by velocity, but input and output velocity "
     425             :                 "coordinates do not overlap"
     426             :             );
     427             :         }
     428           0 :         casacore::Matrix<casacore::Double> pc(1, 1, 0);
     429           0 :         pc.diagonal() = 1.0;
     430           0 :         casacore::LinearCoordinate lin(
     431           0 :             casacore::Vector<casacore::String>(1, "velocity"),
     432             :             specCoord.worldAxisUnits(),
     433           0 :             casacore::Vector<casacore::Double>(1, velRefVal),
     434           0 :             casacore::Vector<casacore::Double>(1, vel1 - vel0),
     435             :             pc, specCoord.referencePixel()
     436             :         );
     437             :         // don't bother checking the return value of the replaceCoordinate call
     438             :         // as it will always be false because the replaced and replacement
     439             :         // coordinate types differ, but the coordinate will be replaced anyway.
     440             :         // Yes I find it nonintuitive and am scratching my head regarding the
     441             :         // usefulness of the return value as well. Just check that replacement
     442             :         // coordinate is equal to the coordinate we expect.
     443           0 :         cs->replaceCoordinate(lin, specCoordNum);
     444           0 :         ThrowIf(
     445             :             ! lin.near(cs->linearCoordinate(specCoordNum)),
     446             :             "Replacement linear coordinate does not match "
     447             :             "original linear coordinate because "
     448             :             + lin.errorMessage()
     449             :         );
     450           0 :         if (cs == csys.get()) {
     451           0 :             newVelRefVal = velRefVal;
     452           0 :             newVelInc = vel1 - vel0;
     453             :         }
     454             :         else {
     455           0 :             _subimage->setCoordinateInfo(*cs);
     456             :         }
     457             :     }
     458             :     // do not pass the region or the mask, the maskedClone has already had the
     459             :     // region and mask applied
     460           0 :     ImageRegridder regridder(
     461           0 :         _subimage, 0, "", this->_getOutname(), this->_getOverwrite(), *csys,
     462             :         this->_getAxes(), this->_getShape()
     463             :     );
     464           0 :     regridder.setConfiguration(*this);
     465           0 :     auto outImage = regridder._regrid();
     466             :     // replace the temporary linear coordinate with the saved spectral
     467             :     // coordinate
     468           0 :     std::unique_ptr<casacore::CoordinateSystem> newCoords(
     469           0 :         dynamic_cast<casacore::CoordinateSystem *>(
     470           0 :             outImage->coordinates().clone()
     471             :         )
     472             :     );
     473             :     // make frequencies correct
     474             :     casacore::Double newRefFreq;
     475           0 :     ThrowIf(
     476             :         ! newSpecCoord.velocityToFrequency(
     477             :             newRefFreq, newVelRefVal
     478             :         ),
     479             :         "Unable to determine new reference frequency"
     480             :     );
     481             :     // get the new frequency increment
     482             :     casacore::Double newFreq;
     483           0 :     ThrowIf (
     484             :         ! newSpecCoord.velocityToFrequency(newFreq, newVelRefVal + newVelInc),
     485             :         "Unable to determine new frequency increment"
     486             :     );
     487           0 :     ThrowIf (
     488             :         ! newSpecCoord.setReferenceValue(Vector<Double>(1, newRefFreq)),
     489             :         "Unable to set new reference frequency"
     490             :     );
     491           0 :     ThrowIf (
     492             :         ! newSpecCoord.setIncrement((Vector<Double>(1, newFreq - newRefFreq))),
     493             :         "Unable to set new frequency increment"
     494             :     );
     495           0 :     ThrowIf(
     496             :         ! newSpecCoord.setReferencePixel(
     497             :             templateSpecCoord.referencePixel()
     498             :         ), "Unable to set new reference pixel"
     499             :     );
     500           0 :     ThrowIf(
     501             :         ! newCoords->replaceCoordinate(
     502             :             newSpecCoord,
     503             :             _subimage->coordinates().linearCoordinateNumber())
     504             :         && ! newSpecCoord.near(newCoords->spectralCoordinate()),
     505             :         "Unable to replace coordinate for velocity regridding"
     506             :     );
     507           0 :     outImage->setCoordinateInfo(*newCoords);
     508           0 :     return outImage;
     509           0 : }
     510             : 
     511           0 : template<class T> Bool ImageRegridder<T>::_doImagesOverlap(
     512             :     SPCIIT image0, SPCIIT image1
     513             : ) const {
     514           0 :     const auto csys0 = image0->coordinates();
     515           0 :     const auto csys1 = image1->coordinates();
     516           0 :     auto shape0 = image0->shape();
     517           0 :     auto shape1 = image1->shape();
     518           0 :     ImageMetaData<T> md0(image0);
     519           0 :     ImageMetaData<T> md1(image1);
     520           0 :     Bool overlap = false;
     521           0 :     if (
     522           0 :         csys0.hasDirectionCoordinate()
     523           0 :         && csys1.hasDirectionCoordinate()
     524             :     ) {
     525           0 :         const auto dc0 = csys0.directionCoordinate();
     526           0 :         auto dc1 = csys1.directionCoordinate();
     527           0 :         auto dirShape0 = md0.directionShape();
     528           0 :         auto dirShape1 = md1.directionShape();
     529           0 :         auto inc0 = dc0.increment();
     530           0 :         auto inc1 = dc1.increment();
     531           0 :         auto units0 = dc0.worldAxisUnits();
     532           0 :         auto units1 = dc1.worldAxisUnits();
     533           0 :         auto reallyBig = false;
     534           0 :         casacore::Quantity extent;
     535           0 :         casacore::Quantity oneDeg(1, "deg");
     536           0 :         for (uInt i=0; i<2; ++i) {
     537           0 :             extent = casacore::Quantity(dirShape0[i]*abs(inc0[i]), units0[i]);
     538           0 :             if (extent > oneDeg) {
     539           0 :                 reallyBig = true;
     540           0 :                 break;
     541             :             }
     542           0 :             extent = casacore::Quantity(dirShape1[i]*abs(inc1[i]), units1[i]);
     543           0 :             if (extent > oneDeg) {
     544           0 :                 reallyBig = true;
     545           0 :                 break;
     546             :             }
     547             :         }
     548           0 :         if (reallyBig) {
     549           0 :             *this->_getLog() << LogOrigin("ImageRegridder", __func__)
     550             :                 << LogIO::WARN << "At least one of the images "
     551             :                 << "exceeds one degree on at one side, not checking "
     552           0 :                 << "for direction plane overlap." << LogIO::POST;
     553             :         }
     554             :         else {
     555           0 :             auto sameFrame = dc0.directionType(true) == dc1.directionType(true);
     556           0 :             if (!sameFrame) {
     557           0 :                 dc1.setReferenceConversion(dc0.directionType(true));
     558             :             }
     559           0 :             auto corners0 = _getDirectionCorners(dc0, dirShape0);
     560           0 :             auto corners1 = _getDirectionCorners(dc1, dirShape1);
     561           0 :             overlap = _doRectanglesIntersect(corners0, corners1);
     562           0 :             if (! overlap) {
     563           0 :                 return false;
     564             :             }
     565           0 :         }
     566           0 :     }
     567           0 :     if (
     568           0 :         csys0.hasSpectralAxis()
     569           0 :         && csys1.hasSpectralAxis()
     570             :     ) {
     571           0 :         const auto sp0 = csys0.spectralCoordinate();
     572           0 :         const auto sp1 = csys1.spectralCoordinate();
     573           0 :         auto nChan0 = md0.nChannels();
     574           0 :         auto nChan1 = md1.nChannels();
     575             :         casacore::Double world;
     576           0 :         sp0.toWorld(world, 0);
     577           0 :         auto end00 = world;
     578           0 :         sp0.toWorld(world, nChan0 - 1);
     579           0 :         auto end01 = world;
     580           0 :         sp1.toWorld(world, 0);
     581           0 :         auto end10 = world;
     582           0 :         sp1.toWorld(world, nChan1 - 1);
     583           0 :         auto end11 = world;
     584           0 :         auto minmax0 = minmax(end00, end01);
     585           0 :         auto minmax1 = minmax(end10, end11);
     586           0 :         if (
     587             :             (
     588           0 :                 minmax0.second < minmax1.first
     589           0 :                 && ! casacore::near(minmax0.second, minmax1.first)
     590             :             )
     591           0 :             || (
     592           0 :                 minmax1.second < minmax0.first
     593           0 :                 && ! casacore::near(minmax1.second, minmax0.first)
     594             :             )
     595             :         ) {
     596           0 :             return false;
     597             :         }
     598           0 :     }
     599           0 :     return true;
     600           0 : }
     601             : 
     602             : template<class T>
     603             : casacore::Vector<std::pair<casacore::Double, casacore::Double>>
     604           0 : ImageRegridder<T>::_getDirectionCorners(
     605             :     const casacore::DirectionCoordinate& dc,
     606             :     const casacore::IPosition& directionShape
     607             : ) {
     608           0 :     casacore::Vector<casacore::Double> world;
     609           0 :     casacore::Vector<casacore::Double> pixel(2, 0);
     610           0 :     auto units = dc.worldAxisUnits();
     611           0 :     dc.toWorld(world, pixel);
     612           0 :     casacore::Vector<std::pair<casacore::Double, casacore::Double> > corners(4);
     613           0 :     for (uInt i=0; i<4; ++i) {
     614           0 :         switch(i) {
     615           0 :         case 0:
     616             :             // blcx, blcy
     617           0 :             pixel.set(0);
     618           0 :             break;
     619           0 :         case 1:
     620             :             // trcx, blcy
     621           0 :             pixel[0] = directionShape[0];
     622           0 :             pixel[1] = 0;
     623           0 :             break;
     624           0 :         case 2:
     625             :             // trcx, trcy
     626           0 :             pixel[0] = directionShape[0];
     627           0 :             pixel[1] = directionShape[1];
     628           0 :             break;
     629           0 :         case 3:
     630             :             // blcx, trcy
     631           0 :             pixel[0] = 0;
     632           0 :             pixel[1] = directionShape[1];
     633           0 :             break;
     634           0 :         default:
     635           0 :             ThrowCc("Logic Error: This code should never be reached");
     636             :             break;
     637             :         }
     638           0 :         dc.toWorld(world, pixel);
     639           0 :         auto x = casacore::Quantity(world[0], units[0]).getValue("rad");
     640           0 :         if (fabs(x) >= casacore::C::_2pi) {
     641             :             // resolve 2pi ambiguities for x (longitude) coordinate
     642           0 :             x = fmod(x, C::_2pi);
     643             :         }
     644           0 :         if (x < 0) {
     645             :             // ensure longitude is > 0
     646           0 :             x += casacore::C::_2pi;
     647             :         }
     648           0 :         corners[i].first = x;
     649           0 :         corners[i].second = casacore::Quantity(
     650             :             world[1], units[1]
     651           0 :         ).getValue("rad");
     652             :     }
     653           0 :     auto diff0 = abs(corners[1].first - corners[0].first);  
     654           0 :     auto diff1 = abs(corners[1].first - C::_2pi - corners[0].first);
     655           0 :     if (diff0 > diff1) {
     656             :         // image straddles longitude 0 and we have to rationalize the
     657             :         // longitude trc coordinate
     658           0 :         corners[1].first -= casacore::C::_2pi;
     659           0 :         corners[2].first -= casacore::C::_2pi;
     660             :     }
     661           0 :     return corners;
     662           0 : }
     663             : 
     664           0 : template<class T> casacore::Bool ImageRegridder<T>::_doRectanglesIntersect(
     665             :     const casacore::Vector<
     666             :         std::pair<casacore::Double, casacore::Double>
     667             :     >& corners0,
     668             :     const casacore::Vector<
     669             :         std::pair<casacore::Double, casacore::Double>
     670             :     >& corners1
     671             : ) {
     672           0 :     auto minx0 = corners0[0].first;
     673           0 :     auto maxx0 = minx0;
     674           0 :     auto miny0 = corners0[0].second;
     675           0 :     auto maxy0 = miny0;
     676           0 :     auto minx1 = corners1[0].first;
     677           0 :     auto maxx1 = minx1;
     678           0 :     auto miny1 = corners1[0].second;
     679           0 :     auto maxy1 = miny1;
     680           0 :     for (casacore::uInt i=1; i<4; ++i) {
     681           0 :         minx0 = min(minx0, corners0[i].first);
     682           0 :         maxx0 = max(maxx0, corners0[i].first);
     683           0 :         miny0 = min(miny0, corners0[i].second);
     684           0 :         maxy0 = max(maxy0, corners0[i].second);
     685             : 
     686           0 :         minx1 = min(minx1, corners1[i].first);
     687           0 :         maxx1 = max(maxx1, corners1[i].first);
     688           0 :         miny1 = min(miny1, corners1[i].second);
     689           0 :         maxy1 = max(maxy1, corners1[i].second);
     690             :     }
     691           0 :     if (
     692           0 :         minx0 > maxx1 || maxx0 < minx1
     693           0 :         || miny0 > maxy1 || maxy0 < miny1
     694             :     ) {
     695             :         // bounds check shows images do not intersect
     696           0 :         return false;
     697             :     }
     698           0 :     else if (
     699           0 :         (minx0 >= minx1 && maxx0 <= maxx1 && miny0 >= miny1 && maxy0 <= maxy1)
     700           0 :         || (minx0 < minx1 && maxx0 > maxx1 && miny0 < miny1 && maxy0 > maxy1)
     701             :     ) {
     702             :         // one image lies completely inside the other
     703           0 :         return true;
     704             :     }
     705             :     else {
     706             :         // determine intersection
     707             :         // FIXME There are more efficient algorithms. See eg
     708             :         // the Shamos-Hoey Algorithm
     709             :         // http://geomalgorithms.com/a09-_intersect-3.html#Pseudo-Code%3a%20S-H
     710           0 :         for (casacore::uInt i=0; i<4; ++i) {
     711           0 :             casacore::Vector<casacore::Double> start0(2, corners0[i].first);
     712           0 :             start0[1] = corners0[i].second;
     713           0 :             casacore::Vector<casacore::Double> end0(
     714             :                 2,
     715           0 :                 i == 3 ? corners0[0].first
     716           0 :                     : corners0[i+1].first
     717             :             );
     718           0 :             end0[1] = i == 3 ? corners0[0].second : corners0[i+1].second;
     719             : 
     720           0 :             for (uInt j=0; j<4; ++j) {
     721           0 :                 casacore::Vector<casacore::Double> start1(2, corners1[j].first);
     722           0 :                 start1[1] = corners1[j].second;
     723           0 :                 casacore::Vector<casacore::Double> end1(
     724             :                     2,
     725           0 :                     j == 3 ? corners1[0].first
     726           0 :                         : corners1[j+1].first
     727             :                 );
     728           0 :                 end1[1] = j == 3 ? corners1[0].second : corners1[j+1].second;
     729           0 :                 if (
     730           0 :                     casacore::Geometry::doLineSegmentsIntersect(
     731             :                         start0[0], start0[1], end0[0], end0[1],
     732             :                         start1[0], start1[1], end1[0], end1[1]
     733             :                     )
     734             :                 ) {
     735           0 :                     return true;
     736             :                     break;
     737             :                 }
     738             :             }
     739             :         }
     740             :     }
     741           0 :     return false;
     742             : }
     743             : 
     744             : }

Generated by: LCOV version 1.16