LCOV - code coverage report
Current view: top level - imageanalysis/Regions - CasacRegionManager.cc (source / functions) Hit Total Coverage
Test: ctest_coverage.info Lines: 0 565 0.0 %
Date: 2023-11-02 14:27:30 Functions: 0 20 0.0 %

          Line data    Source code
       1             : //# RegionManager.cc: framework independent class that provides 
       2             : //# functionality to tool of same name
       3             : //# Copyright (C) 2007
       4             : //# Associated Universities, Inc. Washington DC, USA.
       5             : //#
       6             : //# This program is free software; you can redistribute it and/or modify it
       7             : //# under the terms of the GNU General Public License as published by the Free
       8             : //# Software Foundation; either version 2 of the License, or (at your option)
       9             : //# any later version.
      10             : //#
      11             : //# This program is distributed in the hope that it will be useful, but WITHOUT
      12             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      14             : //# more details.
      15             : //#
      16             : //# You should have received a copy of the GNU General Public License along
      17             : //# with this program; if not, write to the Free Software Foundation, Inc.,
      18             : //# 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      19             : //#
      20             : //# Correspondence concerning AIPS++ should be addressed as follows:
      21             : //#        Internet email: aips2-request@nrao.edu.
      22             : //#        Postal address: AIPS++ Project Office
      23             : //#                        National Radio Astronomy Observatory
      24             : //#                        520 Edgemont Road
      25             : //#                        Charlottesville, VA 22903-2475 USA
      26             : 
      27             : #include <imageanalysis/Regions/CasacRegionManager.h>
      28             : 
      29             : #include <casacore/casa/Containers/Record.h>
      30             : #include <casacore/casa/OS/File.h>
      31             : #include <casacore/images/Images/TempImage.h>
      32             : #include <casacore/images/Images/SubImage.h>
      33             : #include <casacore/casa/Utilities/Regex.h>
      34             : 
      35             : #include <casacore/images/Regions/ImageRegion.h>
      36             : #include <casacore/images/Regions/WCBox.h>
      37             : #include <casacore/lattices/LRegions/LCBox.h>
      38             : #include <casacore/measures/Measures/Stokes.h>
      39             : #include <casacore/tables/Tables/TableRecord.h>
      40             : 
      41             : #include <imageanalysis/Annotations/AnnRegion.h>
      42             : #include <imageanalysis/Annotations/RegionTextList.h>
      43             : #include <imageanalysis/IO/ParameterParser.h>
      44             : #include <imageanalysis/ImageAnalysis/ImageMetaData.h>
      45             : #include <imageanalysis/ImageAnalysis/SubImageFactory.h>
      46             : 
      47             : #include <casacore/lattices/LRegions/LCSlicer.h>
      48             : 
      49             : #include <casacore/casa/namespace.h>
      50             : #include <memory>
      51             : 
      52             : using namespace casacore;
      53             : namespace casa { //# name space casa begins
      54             : 
      55             : const String CasacRegionManager::ALL = "ALL";
      56             : 
      57           0 : CasacRegionManager::CasacRegionManager() : RegionManager() {}
      58             : 
      59           0 : CasacRegionManager::CasacRegionManager(
      60             :     const CoordinateSystem& csys
      61           0 : ) : RegionManager(csys) {}
      62             : 
      63           0 : CasacRegionManager::~CasacRegionManager() {}
      64             : 
      65           0 : vector<uInt> CasacRegionManager::_setPolarizationRanges(
      66             :     String& specification, const String& firstStokes, const uInt nStokes,
      67             :     const StokesControl stokesControl
      68             : ) const {
      69           0 :     LogOrigin origin("CasacRegionManager", __func__);
      70             :     //const LogIO *myLog = _getLog();
      71           0 :     *_getLog() << origin;
      72             : 
      73           0 :     vector<uInt> ranges(0);
      74           0 :     CoordinateSystem csys = getcoordsys();
      75           0 :     if (! csys.hasPolarizationCoordinate()) {
      76           0 :         return ranges;
      77             :     }
      78           0 :     specification.trim();
      79           0 :     specification.ltrim('[');
      80           0 :     specification.rtrim(']');
      81             : 
      82           0 :     specification.upcase();
      83           0 :     if (specification == ALL) {
      84           0 :         ranges.push_back(0);
      85           0 :         ranges.push_back(nStokes - 1);
      86           0 :         return ranges;
      87             :     }
      88           0 :     if (specification.empty()) {
      89             :         // ranges.resize(2);
      90             :         // ranges[0] = 0;
      91           0 :         ranges.push_back(0);
      92           0 :         switch (stokesControl) {
      93           0 :         case USE_FIRST_STOKES:
      94           0 :             ranges.push_back(0);
      95           0 :             specification = firstStokes;
      96           0 :             break;
      97           0 :         case USE_ALL_STOKES:
      98           0 :             ranges.push_back(nStokes - 1);
      99           0 :             specification = ALL;
     100           0 :             break;
     101           0 :         default:
     102             :             // bug if we get here
     103           0 :             ThrowCc("Logic error, unhandled stokes control");
     104             :             break;
     105             :         };
     106           0 :         return ranges;
     107             :     }
     108             :     // First split on commas and semi-colons.
     109             :     // in the past for polarization specification.
     110             : 
     111           0 :     Vector<String> parts = stringToVector(specification, std::regex("[,;]"));
     112           0 :     Vector<String> polNames = Stokes::allNames(false);
     113           0 :     uInt nNames = polNames.size();
     114           0 :     Vector<uInt> nameLengths(nNames);
     115           0 :     for (uInt i=0; i<nNames; i++) {
     116           0 :         nameLengths[i] = polNames[i].length();
     117             :     }
     118           0 :     uInt *lengthData = nameLengths.data();
     119             : 
     120           0 :     Vector<uInt> idx(nNames);
     121           0 :     Sort sorter;
     122           0 :     sorter.sortKey(lengthData, TpUInt, 0, Sort::Descending);
     123           0 :     sorter.sort(idx, nNames);
     124             : 
     125           0 :     Vector<String> sortedNames(nNames);
     126           0 :     for (uInt i=0; i<nNames; i++) {
     127           0 :         sortedNames[i] = polNames[idx[i]];
     128           0 :         sortedNames[i].upcase();
     129             :     }
     130           0 :     for (uInt i=0; i<parts.size(); i++) {
     131           0 :         String part = parts[i];
     132           0 :         part.trim();
     133           0 :         Vector<String>::iterator iter = sortedNames.begin();
     134           0 :         while (iter != sortedNames.end() && ! part.empty()) {
     135           0 :             if (part.startsWith(*iter)) {
     136           0 :                 Int stokesPix = csys.stokesPixelNumber(*iter);
     137           0 :                 if (stokesPix >= int(nStokes)) {
     138           0 :                     *_getLog() << "Polarization " << *iter << " specified in "
     139           0 :                         << parts[i] << " does not exist in the specified "
     140             :                         << "coordinate system for the specified number of "
     141           0 :                         << "polarization parameters" << LogIO::EXCEPTION;
     142             :                 }
     143           0 :                 ranges.push_back(stokesPix);
     144           0 :                 ranges.push_back(stokesPix);
     145             :                 // consume the string
     146           0 :                 part = part.substr(iter->length());
     147           0 :                 if (! part.empty()) {
     148             :                     // reset the iterator to start over at the beginning of the list for
     149             :                     // the next specified polarization
     150           0 :                     iter = sortedNames.begin();
     151             :                 }
     152             :             }
     153             :             else {
     154           0 :                 iter++;
     155             :             }
     156             :         }
     157           0 :         if (! part.empty()) {
     158           0 :             *_getLog() << "(Sub)String " << part << " in stokes specification part " << parts[i]
     159           0 :                 << " does not match a known polarization." << LogIO::EXCEPTION;
     160             :         }
     161             :     }
     162             :     uInt nSel;
     163           0 :     return ParameterParser::consolidateAndOrderRanges(nSel, ranges);
     164             : }
     165             : 
     166           0 : Bool CasacRegionManager::_supports2DBox(Bool except) const {
     167           0 :     Bool ok = true;
     168           0 :     const CoordinateSystem& csys = getcoordsys();
     169           0 :     Vector<Int> axes;
     170           0 :     if (csys.hasDirectionCoordinate()) {
     171           0 :         axes = csys.directionAxesNumbers();
     172             :     }
     173           0 :     else if (csys.hasLinearCoordinate()) {
     174           0 :         axes = csys.linearAxesNumbers();
     175             :     }
     176             :     else {
     177           0 :         ok = false;
     178             :     }
     179           0 :     if (ok) {
     180           0 :         uInt nGood = 0;
     181           0 :         for (uInt i=0; i<axes.size(); i++) {
     182           0 :             if (axes[i] >= 0) {
     183           0 :                 nGood++;
     184             :             }
     185             :         }
     186           0 :         if (nGood != 2) {
     187           0 :             ok = false;
     188             :         }
     189             :     }
     190           0 :     if (except && ! ok) {
     191           0 :         *_getLog() << LogOrigin("CasacRegionManager", __func__)
     192           0 :             << "This image does not have a 2-D direction or linear coordinate";
     193             :     }
     194           0 :     return ok;
     195             : }
     196             : 
     197           0 : vector<Double> CasacRegionManager::_setBoxCorners(const String& box) const {
     198           0 :     if (! box.empty()) {
     199           0 :         _supports2DBox(true);
     200             :     }
     201           0 :     Vector<String> boxParts = stringToVector(box);
     202           0 :     AlwaysAssert(boxParts.size() % 4 == 0, AipsError);
     203           0 :     vector<Double> corners(boxParts.size());
     204           0 :     for(uInt i=0; i<boxParts.size()/4; i++) {
     205           0 :         uInt index = 4*i;
     206           0 :         for (uInt j=0; j<4; j++) {
     207           0 :             boxParts[index + j].trim();
     208           0 :             if (! boxParts[index + j].matches(RXdouble)) {
     209           0 :                 *_getLog() << "Box spec contains non numeric characters and so is invalid"
     210           0 :                         << LogIO::EXCEPTION;
     211             :             }
     212           0 :             corners[index + j] = String::toDouble(boxParts[index + j]);
     213             :         }
     214             :     }
     215           0 :     return corners;
     216             : }
     217             : 
     218           0 : Record CasacRegionManager::fromBCS(
     219             :     String& diagnostics, uInt& nSelectedChannels, String& stokes,
     220             :     const Record  * const &regionPtr, const String& regionName,
     221             :     const String& chans, const StokesControl stokesControl,
     222             :     const String& box, const IPosition& imShape, const String& imageName,
     223             :     Bool verbose
     224             : ) {
     225           0 :     LogOrigin origin("CasacRegionManager", __func__);
     226           0 :     Record regionRecord;
     227           0 :     if (! box.empty()) {
     228           0 :         ThrowIf(
     229             :             regionPtr != nullptr,
     230             :             "box and regionPtr cannot be simultaneously specified"
     231             :         );
     232           0 :         ThrowIf(
     233             :             box.freq(",") % 4 != 3,
     234             :              "box not specified correctly"
     235             :         );
     236           0 :         if (regionName.empty()) {
     237           0 :             regionRecord = fromBCS(
     238             :                 diagnostics, nSelectedChannels, stokes,
     239             :                 chans, stokesControl, box, imShape
     240           0 :             ).toRecord("");
     241           0 :             if (verbose) {
     242           0 :                 *_getLog() << origin;
     243           0 :                 *_getLog() << LogIO::NORMAL << "Using specified box(es) "
     244           0 :                     << box << LogIO::POST;
     245             :             }
     246             :         }
     247             :         else {
     248           0 :             auto corners = stringToVector(box, ',');
     249           0 :             auto iter = corners.begin();
     250           0 :             auto end = corners.end();
     251           0 :             String crtfBoxString = "box[[";
     252           0 :             auto count = 0;
     253           0 :             for (; iter != end; ++iter, ++count) {
     254           0 :                 iter->trim();
     255           0 :                 if (count > 0 && count % 4 == 0) {
     256           0 :                     crtfBoxString += "\nbox[[";
     257             :                 }
     258           0 :                 crtfBoxString += *iter + "pix";
     259           0 :                 if (count % 2 == 0) {
     260           0 :                     crtfBoxString += ",";
     261             :                 }
     262             :                 else {
     263             :                     // close pixel pair
     264           0 :                     crtfBoxString += "]";
     265           0 :                     if (count - 1 % 4 == 0) {
     266             :                         // first pixel pair done, start second pixel pair
     267           0 :                         crtfBoxString += ",[";
     268             :                     }
     269             :                     else {
     270             :                         // second pixel pair done, close box
     271           0 :                         crtfBoxString += "]";
     272             :                     }
     273             :                 }
     274             :             }
     275           0 :             _setRegion(
     276             :                 regionRecord, diagnostics, regionName, imShape,
     277             :                 imageName, crtfBoxString, chans, stokes, verbose
     278             :             );
     279             :         }
     280             :     }
     281           0 :     else if (regionPtr) {
     282           0 :         ThrowIf(
     283             :             ! (regionName.empty() && chans.empty() && stokes.empty()),
     284             :             "regionPtr and regionName, chans, and/or stokes cannot "
     285             :             "be simultaneously specified"
     286             :         );
     287           0 :         _setRegion(regionRecord, diagnostics, regionPtr);
     288           0 :         stokes = _stokesFromRecord(regionRecord, stokesControl, imShape);
     289             :     }
     290           0 :     else if (! regionName.empty()) {
     291           0 :         _setRegion(
     292             :             regionRecord, diagnostics, regionName,
     293             :             imShape, imageName, "", chans, stokes, verbose
     294             :         );
     295           0 :         if (verbose) {
     296           0 :             *_getLog() << origin;
     297           0 :             *_getLog() << LogIO::NORMAL << diagnostics << LogIO::POST;
     298             :         }
     299           0 :         stokes = _stokesFromRecord(regionRecord, stokesControl, imShape);
     300             :     }
     301             :     else {
     302           0 :         vector<uInt> chanEndPts, polEndPts;
     303           0 :         regionRecord = fromBCS(
     304             :             diagnostics, nSelectedChannels, stokes,
     305             :             chans, stokesControl, box, imShape
     306           0 :         ).toRecord("");
     307           0 :         if (verbose) {
     308           0 :             *_getLog() << origin;
     309           0 :             *_getLog() << LogIO::NORMAL << "No directional region specified. Using full positional plane."
     310           0 :                 << LogIO::POST;
     311             :         }
     312           0 :         const CoordinateSystem& csys = getcoordsys();
     313           0 :         if (csys.hasSpectralAxis()) {
     314           0 :             if (verbose) {
     315           0 :                 if (chans.empty()) {
     316           0 :                     *_getLog() << LogIO::NORMAL << "Using all spectral channels."
     317           0 :                         << LogIO::POST;
     318             :                 }
     319             :                 else {
     320           0 :                     *_getLog() << LogIO::NORMAL << "Using channel range(s) "
     321           0 :                         << _pairsToString(chanEndPts) << LogIO::POST;
     322             :                 }
     323             :             }
     324             :         }
     325           0 :         if (csys.hasPolarizationCoordinate() && verbose) {
     326           0 :             if (stokes.empty()) {
     327           0 :                 switch (stokesControl) {
     328           0 :                 case USE_ALL_STOKES:
     329           0 :                     *_getLog() << LogIO::NORMAL << "Using all polarizations " << LogIO::POST;
     330           0 :                     break;
     331           0 :                 case USE_FIRST_STOKES:
     332           0 :                     *_getLog() << LogIO::NORMAL << "polarization "
     333           0 :                         << csys.stokesAtPixel(0) << LogIO::POST;
     334           0 :                     break;
     335           0 :                 default:
     336           0 :                     break;
     337             :                 }
     338             :             }
     339             :             else {
     340           0 :                 *_getLog() << LogIO::NORMAL << "Using polarizations " << stokes << LogIO::POST;
     341             :             }
     342             :         }
     343             :     }
     344           0 :     return regionRecord;
     345             : }
     346             : 
     347           0 : Record CasacRegionManager::regionFromString(
     348             :     const CoordinateSystem& csys, const String& regionStr,
     349             :     const String& imageName, const IPosition& imShape,
     350             :     Bool verbose
     351             : ) {
     352           0 :     CasacRegionManager mgr(csys);
     353           0 :     Record reg;
     354           0 :     String diag;
     355           0 :     mgr._setRegion(
     356             :         reg, diag, regionStr, imShape, imageName, "", "", "", verbose
     357             :     );
     358           0 :     return reg;
     359             : }
     360             : 
     361           0 : void CasacRegionManager::_setRegion(
     362             :     Record& regionRecord, String& diagnostics,
     363             :     const Record* regionPtr
     364             : )  {
     365             :     // region record pointer provided
     366           0 :     regionRecord = *(regionPtr);
     367             :     // set stokes from the region record
     368           0 :     diagnostics = "used provided region record";
     369           0 : }
     370             : 
     371           0 : void CasacRegionManager::_setRegion(
     372             :     Record& regionRecord, String& diagnostics,
     373             :     const String& regionName, const IPosition& imShape,
     374             :     const String& imageName,
     375             :     const String& prependBox,
     376             :     const String& globalOverrideChans,
     377             :     const String& globalStokesOverride,
     378             :     Bool verbose
     379             : ) {
     380           0 :     if (regionName.empty() && imageName.empty()) {
     381           0 :         regionRecord = Record();
     382           0 :         diagnostics = "No region string";
     383           0 :         return;
     384             :     }
     385             :     // region name provided
     386           0 :     const static Regex image("(.+):(.+)");
     387             : // OSX no longer seems to like this one
     388             : //    const static Regex regionText(
     389             : //        "^[[:space:]]*[[:alpha:]]+[[:space:]]*\\[(.*)+,(.*)+\\]"
     390             : //    );
     391             :     const static Regex regionText(
     392             :         "^[[:space:]]*[[:alpha:]]+[[:space:]]*\\["
     393           0 :     );
     394           0 :     File myFile(regionName);
     395           0 :     const CoordinateSystem csys = getcoordsys();
     396           0 :     if (myFile.exists()) {
     397           0 :         ThrowIf(
     398             :             ! myFile.isReadable(),
     399             :             "File " + regionName + " exists but is not readable."
     400             :         );
     401           0 :         std::unique_ptr<Record> rec;
     402             :         try {
     403           0 :             rec.reset(readImageFile(regionName, ""));
     404             :         }
     405           0 :         catch(const AipsError& x) {
     406             :         }
     407           0 :         if (rec) {
     408           0 :             ThrowIf(
     409             :                 ! globalOverrideChans.empty() || ! globalStokesOverride.empty()
     410             :                 || ! prependBox.empty(),
     411             :                 "a binary region file and any of box, chans and/or stokes cannot "
     412             :                 "be specified simultaneously"
     413             :             );
     414           0 :             regionRecord = *rec;
     415           0 :             diagnostics = "Region read from binary region file " + regionName;
     416           0 :             return;
     417             :         }
     418             :         try {
     419             :             // CRTF file attempt
     420             :             RegionTextList annList(
     421             :                 regionName, csys, imShape, prependBox,
     422             :                 globalOverrideChans, globalStokesOverride
     423           0 :             );
     424           0 :             regionRecord = annList.regionAsRecord();
     425           0 :             diagnostics = "Region read from CRTF file " + regionName;
     426             :         }
     427           0 :         catch (const AipsError& x) {
     428           0 :             ThrowCc(
     429             :                 regionName + " is neither a valid binary region file, "
     430             :                 "nor a valid region text file."
     431             :             );
     432             :         }
     433             :     }
     434           0 :     else if (regionName.contains(regionText)) {
     435             :         // region spec is raw CASA region plaintext
     436             :         try {
     437             :             RegionTextList annList(
     438             :                 csys, regionName, imShape, prependBox, globalOverrideChans,
     439             :                 globalStokesOverride, verbose
     440           0 :             );
     441           0 :             regionRecord = annList.regionAsRecord();
     442           0 :             diagnostics = "Region read from text string " + regionName;
     443             :         }
     444           0 :         catch (const AipsError& x) {
     445           0 :             ThrowCc(x.getMesg());
     446             :         }
     447             :     }
     448           0 :     else if (regionName.matches(image) || ! imageName.empty()) {
     449           0 :         ImageRegion imRegion;
     450           0 :         String imagename, region;
     451           0 :         if (regionName.matches(image)) {
     452           0 :             String res[2];
     453           0 :             casacore::split(regionName, res, 2, ":");
     454           0 :             imagename = res[0];
     455           0 :             region = res[1];
     456             :         }
     457             :         else {
     458             :             // imageName is not empty if we get here
     459           0 :             imagename = imageName;
     460           0 :             region = regionName;
     461             :         }
     462             :         try {
     463           0 :             Record *myRec = tableToRecord(imagename, region);
     464           0 :             ThrowIf(
     465             :                 ! globalOverrideChans.empty() || ! globalStokesOverride.empty()
     466             :                 || ! prependBox.empty(),
     467             :                 "a region-in-image and any of box, chans and/or stokes cannot "
     468             :                 "be specified simultaneously"
     469             :             );
     470           0 :             if (Table::isReadable(imagename)) {
     471           0 :                 ThrowIf(
     472             :                     myRec == 0,
     473             :                     "Region " + region + " not found in image "
     474             :                     + imagename
     475             :                 );
     476           0 :                 regionRecord = *myRec;
     477           0 :                 diagnostics = "Used region " + region + " from image "
     478           0 :                     + imagename + " table description";
     479             :             }
     480             :             else {
     481           0 :                 *_getLog() << "Cannot read image " << imagename
     482           0 :                         << " to get region " << region << LogIO::EXCEPTION;
     483             :             }
     484             :         }
     485           0 :         catch (const AipsError&) {
     486           0 :             ThrowCc(
     487             :                 "Unable to open region file or region table description "
     488             :                 + region + " in image " + imagename
     489             :             );
     490             :         }
     491             :     }
     492             :     else {
     493           0 :         ostringstream oss;
     494           0 :         oss << "Unable to open region file or region table description "
     495           0 :             << regionName << "." << endl
     496           0 :             << "If it is supposed to be a text string its format is incorrect";
     497           0 :         ThrowCc(oss.str());
     498             :     }
     499             : }
     500             : 
     501           0 : ImageRegion CasacRegionManager::fromBCS(
     502             :     String& diagnostics, uInt& nSelectedChannels, String& stokes,
     503             :     const String& chans,
     504             :     const StokesControl stokesControl, const String& box,
     505             :     const IPosition& imShape
     506             : ) const {
     507           0 :     const CoordinateSystem& csys = getcoordsys();
     508             :     vector<uInt> chanEndPts = setSpectralRanges(
     509             :         chans, nSelectedChannels, imShape
     510           0 :     );
     511           0 :     Int polAxisNumber = csys.polarizationAxisNumber();
     512           0 :     uInt nTotalPolarizations = polAxisNumber >= 0 ? imShape[polAxisNumber] : 0;
     513           0 :     String firstStokes = polAxisNumber >= 0 ? csys.stokesAtPixel(0) : "";
     514             :     vector<uInt> polEndPts = _setPolarizationRanges(
     515             :         stokes, firstStokes,
     516             :         nTotalPolarizations, stokesControl
     517           0 :     );
     518           0 :     vector<Double> boxCorners;
     519           0 :     if (box.empty()) {
     520           0 :         if (_supports2DBox(false)) {
     521           0 :             if (
     522           0 :                 csys.hasDirectionCoordinate()
     523           0 :                 || csys.hasLinearCoordinate()
     524             :             ) {
     525           0 :                 Vector<Int> dirAxesNumbers;
     526           0 :                 if (csys.hasDirectionCoordinate()) {
     527           0 :                     dirAxesNumbers = csys.directionAxesNumbers();
     528             :                 }
     529             :                 else {
     530           0 :                     dirAxesNumbers = csys.linearAxesNumbers();
     531             :                 }
     532           0 :                 Vector<Int> dirShape(2);
     533           0 :                 dirShape[0] = imShape[dirAxesNumbers[0]];
     534           0 :                 dirShape[1] = imShape[dirAxesNumbers[1]];
     535           0 :                 boxCorners.resize(4);
     536           0 :                 boxCorners[0] = 0;
     537           0 :                 boxCorners[1] = 0;
     538           0 :                 boxCorners[2] = dirShape[0] - 1;
     539           0 :                 boxCorners[3] = dirShape[1] - 1;
     540             :             }
     541             :         }
     542             :     }
     543             :     else {
     544           0 :         boxCorners = _setBoxCorners(box);
     545             :     }
     546             :     return _fromBCS(
     547             :         diagnostics, boxCorners,
     548             :         chanEndPts, polEndPts, imShape
     549           0 :     );
     550             : }
     551             : 
     552           0 : ImageRegion CasacRegionManager::_fromBCS(
     553             :         String& diagnostics, const vector<Double>& boxCorners,
     554             :         const vector<uInt>& chanEndPts, const vector<uInt>& polEndPts,
     555             :         const IPosition imShape
     556             : ) const {
     557           0 :     LogOrigin origin("CasacRegionManager", __func__);
     558           0 :     *_getLog() << origin;
     559           0 :     Vector<Double> blc(imShape.nelements(), 0);
     560           0 :     Vector<Double> trc(imShape.nelements(), 0);
     561           0 :     const CoordinateSystem csys = getcoordsys();
     562           0 :     Vector<Int> directionAxisNumbers = csys.directionAxesNumbers();
     563           0 :     vector<Int> linearAxisNumbers = csys.linearAxesNumbers().tovector();
     564             :     // Stupidly, sometimes the values returned by linearAxesNumbers can be less than 0
     565             :     // This needs to be fixed in the implementation of that method
     566           0 :     vector<Int>::iterator iter = linearAxisNumbers.begin();
     567           0 :     vector<Int>::iterator end = linearAxisNumbers.end();
     568           0 :     while(iter != end) {
     569           0 :         if (*iter < 0) {
     570           0 :             iter = linearAxisNumbers.erase(iter);
     571             :         }
     572           0 :         ++iter;
     573             :     }
     574           0 :     Int spectralAxisNumber = csys.spectralAxisNumber();
     575           0 :     Int polarizationAxisNumber = csys.polarizationAxisNumber();
     576             : 
     577           0 :     Vector<Double> xCorners(boxCorners.size()/2);
     578           0 :     Vector<Double> yCorners(xCorners.size());
     579           0 :     for (uInt i=0; i<xCorners.size(); i++) {
     580           0 :         Double x = boxCorners[2*i];
     581           0 :         Double y = boxCorners[2*i + 1];
     582             : 
     583           0 :         if (x < 0 || y < 0 ) {
     584           0 :             *_getLog() << "blc in box spec is less than 0" << LogIO::EXCEPTION;
     585             :         }
     586           0 :         if (csys.hasDirectionCoordinate()) {
     587           0 :             if (
     588           0 :                 x >= imShape[directionAxisNumbers[0]]
     589           0 :                 || y >= imShape[directionAxisNumbers[1]]
     590             :             ) {
     591           0 :                 *_getLog() << "dAxisNum0=" <<directionAxisNumbers[0] <<" dAxisNum1="<<directionAxisNumbers[1];
     592           0 :                 *_getLog() << "x="<<x<<" imShape[0]="<<imShape[directionAxisNumbers[0]]<< " y="<<y<<" imShape[1]="<<imShape[directionAxisNumbers[1]]<<LogIO::POST;
     593           0 :                 *_getLog() << "trc in box spec is greater than or equal to number "
     594           0 :                     << "of direction coordinate pixels in the image" << LogIO::EXCEPTION;
     595             :             }
     596             :         }
     597           0 :         else if (
     598           0 :             csys.hasLinearCoordinate()
     599           0 :             && (
     600           0 :                 x >= imShape[linearAxisNumbers[0]]
     601           0 :                 || y >= imShape[linearAxisNumbers[1]]
     602             :             )
     603             :         ) {
     604           0 :             *_getLog() << "trc in box spec is greater than or equal to number "
     605           0 :                 << "of linear coordinate pixels in the image" << LogIO::EXCEPTION;
     606             :         }
     607           0 :         xCorners[i] = x;
     608           0 :         yCorners[i] = y;
     609             :     }
     610           0 :     Vector<Double> polEndPtsDouble(polEndPts.size());
     611           0 :     for (uInt i=0; i<polEndPts.size(); ++i) {
     612           0 :         polEndPtsDouble[i] = (Double)polEndPts[i];
     613             :     }
     614             : 
     615           0 :     Bool csysSupports2DBox = _supports2DBox(false);
     616           0 :     uInt nRegions = 1;
     617           0 :     if (csysSupports2DBox) {
     618           0 :         if (csys.hasDirectionCoordinate())  {
     619           0 :             nRegions *= boxCorners.size()/4;
     620             :         }
     621           0 :         if (csys.hasLinearCoordinate())  {
     622           0 :             nRegions *= boxCorners.size()/4;
     623             :         }
     624             :     }
     625           0 :     if (csys.hasPolarizationCoordinate()) {
     626           0 :         nRegions *= polEndPts.size()/2;
     627             :     }
     628           0 :     if (csys.hasSpectralAxis()) {
     629           0 :         nRegions *= chanEndPts.size()/2;
     630             :     }
     631           0 :     Vector<Double> extXCorners(2*nRegions, 0);
     632           0 :     Vector<Double> extYCorners(2*nRegions, 0);
     633           0 :     Vector<Double> extPolEndPts(2*nRegions, 0);
     634           0 :     Vector<Double> extChanEndPts(2*nRegions, 0);
     635             : 
     636           0 :     uInt count = 0;
     637             : 
     638           0 :     if (csysSupports2DBox) {
     639           0 :         for (uInt i=0; i<max(uInt(1), xCorners.size()/2); i++) {
     640           0 :             for (uInt j=0; j<max((uInt)1, polEndPts.size()/2); j++) {
     641           0 :                 for (uInt k=0; k<max(uInt(1), chanEndPts.size()/2); k++) {
     642           0 :                     if (
     643           0 :                         csys.hasDirectionCoordinate()
     644           0 :                         || csys.hasLinearCoordinate()
     645             :                     ) {
     646           0 :                         extXCorners[2*count] = xCorners[2*i];
     647           0 :                         extXCorners[2*count + 1] = xCorners[2*i + 1];
     648           0 :                         extYCorners[2*count] = yCorners[2*i];
     649           0 :                         extYCorners[2*count + 1] = yCorners[2*i + 1];
     650             :                     }
     651           0 :                     if (csys.hasPolarizationCoordinate()) {
     652           0 :                         extPolEndPts[2*count] = polEndPtsDouble[2*j];
     653           0 :                         extPolEndPts[2*count + 1] = polEndPtsDouble[2*j + 1];
     654             : 
     655             :                     }
     656           0 :                     if (csys.hasSpectralAxis()) {
     657           0 :                         extChanEndPts[2*count] = chanEndPts[2*k];
     658           0 :                         extChanEndPts[2*count + 1] = chanEndPts[2*k + 1];
     659             :                     }
     660           0 :                     count++;
     661             :                 }
     662             :             }
     663             :         }
     664             :     }
     665             :     else {
     666             :         // here we have neither a direction nor linear coordinate with two
     667             :         // pixel axes which are greater than 0
     668           0 :         for (uInt j=0; j<max((uInt)1, polEndPts.size()/2); j++) {
     669           0 :             for (uInt k=0; k<max(uInt(1), chanEndPts.size()/2); k++) {
     670           0 :                 if (csys.hasPolarizationCoordinate()) {
     671           0 :                     extPolEndPts[2*count] = polEndPtsDouble[2*j];
     672           0 :                     extPolEndPts[2*count + 1] = polEndPtsDouble[2*j + 1];
     673             :                 }
     674           0 :                 if (csys.hasSpectralAxis()) {
     675           0 :                     extChanEndPts[2*count] = chanEndPts[2*k];
     676           0 :                     extChanEndPts[2*count + 1] = chanEndPts[2*k + 1];
     677             :                 }
     678           0 :                 count++;
     679             :             }
     680             :         }
     681             :     }
     682           0 :     map<uInt, Vector<Double> > axisCornerMap;
     683           0 :     for (uInt i=0; i<nRegions; i++) {
     684           0 :         for (uInt axisNumber=0; axisNumber<csys.nPixelAxes(); axisNumber++) {
     685           0 :             if (
     686             :                 (
     687           0 :                     directionAxisNumbers.size() > 1
     688           0 :                     && (Int)axisNumber == directionAxisNumbers[0]
     689             :                 )
     690           0 :                 || (
     691           0 :                     ! csys.hasDirectionCoordinate()
     692           0 :                     && linearAxisNumbers.size() > 1
     693           0 :                     && (Int)axisNumber == linearAxisNumbers[0]
     694             :                 )
     695             :             ) {
     696           0 :                 axisCornerMap[axisNumber] = extXCorners;
     697             :             }
     698           0 :             else if (
     699             :                 (
     700           0 :                     directionAxisNumbers.size() > 1
     701           0 :                     && (Int)axisNumber == directionAxisNumbers[1]
     702             :                 )
     703           0 :                 || (
     704           0 :                     ! csys.hasDirectionCoordinate()
     705           0 :                     && linearAxisNumbers.size() > 1
     706           0 :                     && (Int)axisNumber == linearAxisNumbers[1]
     707             :                 )
     708             :             ) {
     709           0 :                 axisCornerMap[axisNumber] = extYCorners;
     710             :             }
     711           0 :             else if ((Int)axisNumber == spectralAxisNumber) {
     712           0 :                 axisCornerMap[axisNumber] = extChanEndPts;
     713             :             }
     714           0 :             else if ((Int)axisNumber == polarizationAxisNumber) {
     715           0 :                 axisCornerMap[axisNumber] = extPolEndPts;
     716             :             }
     717             :             else {
     718           0 :                 Vector<Double> range(2, 0);
     719           0 :                 range[1] = imShape[axisNumber] - 1;
     720           0 :                 axisCornerMap[axisNumber] = range;
     721             :             }
     722             :         }
     723             :     }
     724           0 :     ImageRegion imRegion;
     725           0 :     for (uInt i=0; i<nRegions; i++) {
     726           0 :         for (uInt axisNumber=0; axisNumber<csys.nPixelAxes(); axisNumber++) {
     727           0 :             blc(axisNumber) = axisCornerMap[axisNumber][2*i];
     728           0 :             trc(axisNumber) = axisCornerMap[axisNumber][2*i + 1];
     729             :         }
     730           0 :         LCBox lcBox(blc, trc, imShape);
     731           0 :         WCBox wcBox(lcBox, csys);
     732           0 :         ImageRegion thisRegion(wcBox);
     733             :         imRegion = (i == 0)
     734             :               ? thisRegion
     735           0 :               : imRegion = *(doUnion(imRegion, thisRegion));
     736             :     }
     737           0 :     ostringstream os;
     738           0 :     os << "Used image region from " << endl;
     739           0 :     if (csys.hasDirectionCoordinate()) {
     740           0 :         os << "    position box corners: ";
     741           0 :         for (uInt i=0; i<boxCorners.size()/4; i++) {
     742           0 :             os << boxCorners[4*i] << ", " << boxCorners[4*i + 1]
     743           0 :                 << ", " << boxCorners[4*i + 2] << ", " << boxCorners[4*i + 3];
     744           0 :             if (i < boxCorners.size()/4 - 1) {
     745           0 :                 os << "; ";
     746             :             }
     747             :         }
     748             :     }
     749           0 :     if (getcoordsys().hasSpectralAxis()) {
     750           0 :         os << "    spectral channel ranges: " << _pairsToString(chanEndPts);
     751             :     }
     752           0 :     if (getcoordsys().hasPolarizationCoordinate()) {
     753           0 :         os << "    polarization pixel ranges: " << _pairsToString(polEndPts);
     754             :     }
     755           0 :     diagnostics = os.str();
     756           0 :     return imRegion;
     757             : }
     758             : 
     759           0 : String CasacRegionManager::_pairsToString(const vector<uInt>& pairs) const {
     760           0 :     ostringstream os;
     761           0 :     uInt nPairs = pairs.size()/2;
     762           0 :     for (uInt i=0; i<nPairs; i++) {
     763           0 :         os << pairs[2*i] << " - " << pairs[2*i + 1];
     764           0 :         if (i < nPairs - 1) {
     765           0 :             os << "; ";
     766             :         }
     767             :     }
     768           0 :     return os.str();
     769             : }
     770             : 
     771           0 : String CasacRegionManager::_stokesFromRecord(
     772             :         const Record& region, const StokesControl stokesControl, const IPosition& shape
     773             : ) const {
     774             :     // FIXME This implementation is incorrect for complex, recursive records
     775           0 :     String stokes = "";
     776           0 :     CoordinateSystem csys = getcoordsys();
     777           0 :     if(! csys.hasPolarizationCoordinate()) {
     778           0 :         return stokes;
     779             :     }
     780           0 :     Int polAxis = csys.polarizationAxisNumber();
     781           0 :     if (shape[polAxis] == 1) {
     782             :         // degenerate stokes axis
     783           0 :         return csys.stokesAtPixel(0);
     784             :     }
     785           0 :     uInt stokesBegin = 0;
     786           0 :     uInt stokesEnd = 0;
     787           0 :     if (region.empty()) {
     788           0 :         stokesEnd = stokesControl == USE_FIRST_STOKES
     789             :             ? 0
     790           0 :             : csys.stokesCoordinate().stokes().size() - 1;
     791             :     }
     792             :     else {
     793           0 :         std::unique_ptr<ImageRegion> imreg(ImageRegion::fromRecord(region, ""));
     794           0 :         Array<Float> blc, trc;
     795           0 :         Bool oneRelAccountedFor = false;
     796           0 :         if (imreg->isLCSlicer()) {
     797           0 :             blc = imreg->asLCSlicer().blc();
     798           0 :             if ((Int)blc.size() <= polAxis) {
     799           0 :                 *_getLog() << LogIO::WARN << "Cannot determine stokes. "
     800             :                     << "blc of input region does not include the polarization coordinate."
     801           0 :                     << LogIO::POST;
     802           0 :                 return stokes;
     803             :             }
     804           0 :             trc = imreg->asLCSlicer().trc();
     805           0 :             if ((Int)trc.size() <= polAxis) {
     806           0 :                 *_getLog() << LogIO::WARN << "Cannot determine stokes. "
     807             :                     << "trc of input region does not include the polarization coordinate."
     808           0 :                     << LogIO::POST;
     809           0 :                 return stokes;
     810             :             }
     811           0 :             stokesBegin = (uInt)((Vector<Float>)blc)[polAxis];
     812           0 :             stokesEnd = (uInt)((Vector<Float>)trc)[polAxis];
     813           0 :             oneRelAccountedFor = true;
     814             :         }
     815           0 :         else if (
     816           0 :                 RegionManager::isPixelRegion(
     817           0 :                     *(ImageRegion::fromRecord(region, ""))
     818             :                 )
     819             :         ) {
     820           0 :             region.toArray("blc", blc);
     821           0 :             region.toArray("trc", trc);
     822           0 :             stokesBegin = (uInt)((Vector<Float>)blc)[polAxis];
     823           0 :             stokesEnd = (uInt)((Vector<Float>)trc)[polAxis];
     824             :         }
     825           0 :         else if (region.fieldNumber("x") >= 0 && region.fieldNumber("y") >= 0) {
     826             :             // world polygon
     827           0 :             oneRelAccountedFor = true;
     828           0 :             stokesBegin = 0;
     829           0 :             stokesEnd = stokesControl == USE_FIRST_STOKES
     830           0 :                 ? 0 : shape[polAxis];
     831             :         }
     832           0 :         else if (region.fieldNumber("blc") >= 0 && region.fieldNumber("blc") >= 0) {
     833             :             // world box
     834           0 :             Record blcRec = region.asRecord("blc");
     835           0 :             Record trcRec = region.asRecord("trc");
     836           0 :             String polField = "*" + String::toString(polAxis + 1);
     837           0 :             stokesBegin = blcRec.isDefined(polField)
     838           0 :                 ? (Int)blcRec.asRecord(
     839           0 :                     String(polField)
     840           0 :                 ).asDouble("value")
     841             :                 : 0;
     842           0 :             stokesEnd = trcRec.isDefined(polField)
     843           0 :                 ? (Int)blcRec.asRecord(
     844           0 :                     String(polField)
     845           0 :                 ).asDouble("value")
     846             :                 : stokesControl == USE_FIRST_STOKES
     847             :                   ? 0
     848           0 :                   : shape[polAxis];
     849           0 :             if (! blcRec.isDefined(polField)) {
     850           0 :                 oneRelAccountedFor = true;
     851             :             }
     852             :         }
     853             :         else {
     854             :             // FIXME not very nice, but until all can be implemented this will have to do
     855           0 :             *_getLog() << LogIO::WARN << "Stokes cannot be determined because this "
     856             :                 << "region type is not handled yet. But chances are very good this is no need to be alarmed."
     857           0 :                 << LogIO::POST;
     858           0 :             return stokes;
     859             :         }
     860           0 :         if (
     861           0 :             ! oneRelAccountedFor
     862           0 :             && region.isDefined("oneRel")
     863           0 :             && region.asBool("oneRel")
     864             :         ) {
     865           0 :             stokesBegin--;
     866           0 :             stokesEnd--;
     867             :         }
     868             :     }
     869           0 :     for (uInt i=stokesBegin; i<=stokesEnd; i++) {
     870           0 :         stokes += csys.stokesAtPixel(i);
     871             :     }
     872           0 :     return stokes;
     873             : }
     874             : 
     875           0 : vector<uInt> CasacRegionManager::_initSpectralRanges(
     876             :     uInt& nSelectedChannels, const IPosition& imShape
     877             : ) const {
     878           0 :     vector<uInt> ranges(0);
     879           0 :     if (! getcoordsys().hasSpectralAxis()) {
     880           0 :         nSelectedChannels = 0;
     881           0 :         return ranges;
     882             :     }
     883           0 :     uInt specNum = getcoordsys().spectralAxisNumber();
     884           0 :     ThrowIf(
     885             :         specNum >= imShape.size(),
     886             :         "Spectral axis number " + String::toString(specNum)
     887             :         + " must be less than number of shape dimensions "
     888             :         + String::toString(imShape.size())
     889             :     );
     890           0 :     uInt nChannels = imShape[getcoordsys().spectralAxisNumber()];
     891           0 :     ranges.push_back(0);
     892           0 :     ranges.push_back(nChannels - 1);
     893           0 :     nSelectedChannels = nChannels;
     894           0 :     return ranges;
     895             : }
     896             : 
     897           0 : vector<uInt> CasacRegionManager::setSpectralRanges(
     898             :     uInt& nSelectedChannels,
     899             :     const Record *const regionRec, const IPosition& imShape
     900             : ) const {
     901           0 :     if (regionRec == 0 || ! getcoordsys().hasSpectralAxis()) {
     902           0 :         return _initSpectralRanges(nSelectedChannels, imShape);
     903             :     }
     904             :     else {
     905           0 :         return _spectralRangeFromRegionRecord(nSelectedChannels, regionRec, imShape);
     906             :     }
     907             : }
     908             : 
     909           0 : vector<uInt> CasacRegionManager::setSpectralRanges(
     910             :     String specification, uInt& nSelectedChannels,
     911             :     /*const String& globalChannelOverride,
     912             :     const String& globalStokesOverride, */ const IPosition& imShape
     913             : ) const {
     914           0 :     LogOrigin origin("CasacRegionManager", __func__);
     915           0 :     *_getLog() << origin;
     916           0 :     specification.trim();
     917           0 :     String x = specification;
     918           0 :     x.upcase();
     919           0 :     if (x.empty() || x == ALL) {
     920           0 :         return _initSpectralRanges(nSelectedChannels, imShape);
     921             :     }
     922           0 :     else if (! getcoordsys().hasSpectralAxis()) {
     923           0 :         *_getLog() << LogIO::WARN << "Channel specification is "
     924             :             << "not empty but the coordinate system has no spectral axis."
     925           0 :             << "Channel specification will be ignored" << LogIO::POST;
     926           0 :         nSelectedChannels = 0;
     927           0 :         return vector<uInt>(0);
     928             :     }
     929           0 :     else if (specification.contains("range")) {
     930             :         // this is a specification in the "new" ASCII region format
     931           0 :         return _spectralRangeFromRangeFormat(nSelectedChannels, specification, imShape);
     932             :     }
     933             :     else {
     934           0 :         uInt nChannels = imShape[getcoordsys().spectralAxisNumber()];
     935             :         return ParameterParser::spectralRangesFromChans(
     936             :             nSelectedChannels, specification, nChannels
     937           0 :         );
     938             :     }
     939             : }
     940             : 
     941           0 : vector<uInt> CasacRegionManager::_spectralRangeFromRegionRecord(
     942             :     uInt& nSelectedChannels, const Record *const regionRec,
     943             :     const IPosition& imShape
     944             : ) const {
     945           0 :     const CoordinateSystem& csys = getcoordsys();
     946           0 :     TempImage<Float> x(imShape, csys);
     947           0 :     x.set(0);
     948           0 :     SPCIIF subimage = SubImageFactory<Float>::createSubImageRO(
     949           0 :         x, *regionRec, "", _getLog(), AxesSpecifier(), false, true
     950           0 :     );
     951           0 :     uInt nChan = 0;
     952             :     {
     953           0 :         ImageMetaData<Float> md(subimage);
     954           0 :         nChan = md.nChannels();
     955             :     }
     956           0 :     const SpectralCoordinate& subsp = subimage->coordinates().spectralCoordinate();
     957             :     Double subworld;
     958           0 :     subsp.toWorld(subworld, 0);
     959           0 :     const SpectralCoordinate& imsp = csys.spectralCoordinate();
     960             :     Double pixOff;
     961           0 :     imsp.toPixel(pixOff, subworld);
     962           0 :     Int specAxisNumber = csys.spectralAxisNumber();
     963           0 :     IPosition start(subimage->ndim(), 0);
     964           0 :     ArrayLattice<Bool> mask(subimage->getMask());
     965           0 :     IPosition planeShape = subimage->shape();
     966           0 :     vector<uInt> myList;
     967           0 :     planeShape[specAxisNumber] = 1;
     968           0 :     for (uInt i=0; i<nChan; i++) {
     969           0 :         start[specAxisNumber] = i;
     970           0 :         Array<Bool> maskSlice;
     971           0 :         mask.getSlice(maskSlice, start, planeShape);
     972           0 :         if (anyTrue(maskSlice)) {
     973           0 :             uInt real = i + (uInt)rint(pixOff);
     974           0 :             myList.push_back(real);
     975           0 :             myList.push_back(real);
     976             :         }
     977             :     }
     978           0 :     return ParameterParser::consolidateAndOrderRanges(nSelectedChannels, myList);
     979             : }
     980             : 
     981           0 : vector<uInt> CasacRegionManager::_spectralRangeFromRangeFormat(
     982             :     uInt& nSelectedChannels, const String& specification,
     983             :     const IPosition& imShape /*, const String& globalChannelOverride,
     984             :     const String& globalStokesOverride */
     985             : ) const {
     986             :     Bool spectralParmsUpdated;
     987             :     // check and make sure there are no disallowed parameters
     988           0 :     const CoordinateSystem csys = getcoordsys();
     989             :     RegionTextParser::ParamSet parms = RegionTextParser::getParamSet(
     990           0 :         spectralParmsUpdated, *_getLog(),
     991           0 :         specification, "", csys, std::shared_ptr<std::pair<MFrequency, MFrequency> >(nullptr),
     992           0 :         std::shared_ptr<Vector<Stokes::StokesTypes> >(nullptr)
     993           0 :     );
     994           0 :     RegionTextParser::ParamSet::const_iterator end = parms.end();
     995           0 :     for (
     996           0 :         RegionTextParser::ParamSet::const_iterator iter=parms.begin();
     997           0 :         iter!=end; iter++
     998             :     ) {
     999           0 :         AnnotationBase::Keyword key = iter->first;
    1000           0 :         ThrowIf(
    1001             :             key != AnnotationBase::FRAME && key != AnnotationBase::RANGE
    1002             :             && key != AnnotationBase::VELTYPE && key != AnnotationBase::RESTFREQ,
    1003             :             "Non-frequency related keyword '"
    1004             :             + AnnotationBase::keywordToString(key)
    1005             :             + "' found."
    1006             :         );
    1007             :     }
    1008             :     // Parameters OK. We need to modify the input string so we can construct an AnnRegion
    1009             :     // from which to get the spectral range information
    1010           0 :     String regSpec = "box[[0pix, 0pix], [1pix, 1pix]] " + specification;
    1011           0 :     RegionTextParser parser(csys, imShape, regSpec);
    1012           0 :     vector<uInt> range(2);
    1013           0 :     ThrowIf(
    1014             :         parser.getLines().empty(),
    1015             :         "The specified spectral range " + specification
    1016             :         + " does not intersect the image spectral range."
    1017             :     );
    1018           0 :     auto ann = parser.getLines()[0].getAnnotationBase();
    1019           0 :     /*const AnnRegion*/ auto *reg = dynamic_cast<const AnnRegion*>(ann.get());
    1020           0 :     ThrowIf(! reg, "Dynamic cast failed");
    1021           0 :     /*vector<Double>*/ const auto drange = reg->getSpectralPixelRange();
    1022           0 :     range[0] = uInt(max(0.0, floor(drange[0] + 0.5)));
    1023           0 :     range[1] = uInt(floor(drange[1] + 0.5));
    1024           0 :     nSelectedChannels = range[1] - range[0] + 1;
    1025           0 :     return range;
    1026             : }
    1027             : 
    1028             : }

Generated by: LCOV version 1.16