LCOV - code coverage report
Current view: top level - imageanalysis/Regions - CasacRegionManager.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 555 599 92.7 %
Date: 2024-12-11 20:54:31 Functions: 20 20 100.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: casa-feedback@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         846 : CasacRegionManager::CasacRegionManager() : RegionManager() {}
      58             : 
      59       29238 : CasacRegionManager::CasacRegionManager(
      60             :     const CoordinateSystem& csys
      61       29238 : ) : RegionManager(csys) {}
      62             : 
      63       30930 : CasacRegionManager::~CasacRegionManager() {}
      64             : 
      65       22342 : vector<uInt> CasacRegionManager::_setPolarizationRanges(
      66             :     String& specification, const String& firstStokes, const uInt nStokes,
      67             :     const StokesControl stokesControl
      68             : ) const {
      69       44684 :     LogOrigin origin("CasacRegionManager", __func__);
      70             :     //const LogIO *myLog = _getLog();
      71       22342 :     *_getLog() << origin;
      72             : 
      73       22342 :     vector<uInt> ranges(0);
      74       22342 :     CoordinateSystem csys = getcoordsys();
      75       22342 :     if (! csys.hasPolarizationCoordinate()) {
      76       18190 :         return ranges;
      77             :     }
      78        4152 :     specification.trim();
      79        4152 :     specification.ltrim('[');
      80        4152 :     specification.rtrim(']');
      81             : 
      82        4152 :     specification.upcase();
      83        4152 :     if (specification == ALL) {
      84           0 :         ranges.push_back(0);
      85           0 :         ranges.push_back(nStokes - 1);
      86           0 :         return ranges;
      87             :     }
      88        4152 :     if (specification.empty()) {
      89             :         // ranges.resize(2);
      90             :         // ranges[0] = 0;
      91        3999 :         ranges.push_back(0);
      92        3999 :         switch (stokesControl) {
      93          85 :         case USE_FIRST_STOKES:
      94          85 :             ranges.push_back(0);
      95          85 :             specification = firstStokes;
      96          85 :             break;
      97        3914 :         case USE_ALL_STOKES:
      98        3914 :             ranges.push_back(nStokes - 1);
      99        3914 :             specification = ALL;
     100        3914 :             break;
     101           0 :         default:
     102             :             // bug if we get here
     103           0 :             ThrowCc("Logic error, unhandled stokes control");
     104             :             break;
     105             :         };
     106        3999 :         return ranges;
     107             :     }
     108             :     // First split on commas and semi-colons.
     109             :     // in the past for polarization specification.
     110             : 
     111         306 :     Vector<String> parts = stringToVector(specification, std::regex("[,;]"));
     112         306 :     Vector<String> polNames = Stokes::allNames(false);
     113         153 :     uInt nNames = polNames.size();
     114         306 :     Vector<uInt> nameLengths(nNames);
     115        5049 :     for (uInt i=0; i<nNames; i++) {
     116        4896 :         nameLengths[i] = polNames[i].length();
     117             :     }
     118         153 :     uInt *lengthData = nameLengths.data();
     119             : 
     120         306 :     Vector<uInt> idx(nNames);
     121         306 :     Sort sorter;
     122         153 :     sorter.sortKey(lengthData, TpUInt, 0, Sort::Descending);
     123         153 :     sorter.sort(idx, nNames);
     124             : 
     125         306 :     Vector<String> sortedNames(nNames);
     126        5049 :     for (uInt i=0; i<nNames; i++) {
     127        4896 :         sortedNames[i] = polNames[idx[i]];
     128        4896 :         sortedNames[i].upcase();
     129             :     }
     130         301 :     for (uInt i=0; i<parts.size(); i++) {
     131         153 :         String part = parts[i];
     132         153 :         part.trim();
     133         153 :         Vector<String>::iterator iter = sortedNames.begin();
     134        5092 :         while (iter != sortedNames.end() && ! part.empty()) {
     135        4939 :             if (part.startsWith(*iter)) {
     136         151 :                 Int stokesPix = csys.stokesPixelNumber(*iter);
     137         151 :                 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         151 :                 ranges.push_back(stokesPix);
     144         151 :                 ranges.push_back(stokesPix);
     145             :                 // consume the string
     146         151 :                 part = part.substr(iter->length());
     147         151 :                 if (! part.empty()) {
     148             :                     // reset the iterator to start over at the beginning of the list for
     149             :                     // the next specified polarization
     150           3 :                     iter = sortedNames.begin();
     151             :                 }
     152             :             }
     153             :             else {
     154        4788 :                 iter++;
     155             :             }
     156             :         }
     157         153 :         if (! part.empty()) {
     158           5 :             *_getLog() << "(Sub)String " << part << " in stokes specification part " << parts[i]
     159           5 :                 << " does not match a known polarization." << LogIO::EXCEPTION;
     160             :         }
     161         158 :     }
     162             :     uInt nSel;
     163         148 :     return ParameterParser::consolidateAndOrderRanges(nSel, ranges);
     164       22352 : }
     165             : 
     166       44647 : Bool CasacRegionManager::_supports2DBox(Bool except) const {
     167       44647 :     Bool ok = true;
     168       44647 :     const CoordinateSystem& csys = getcoordsys();
     169       44647 :     Vector<Int> axes;
     170       44647 :     if (csys.hasDirectionCoordinate()) {
     171       44601 :         axes = csys.directionAxesNumbers();
     172             :     }
     173          46 :     else if (csys.hasLinearCoordinate()) {
     174          14 :         axes = csys.linearAxesNumbers();
     175             :     }
     176             :     else {
     177          32 :         ok = false;
     178             :     }
     179       44647 :     if (ok) {
     180       44615 :         uInt nGood = 0;
     181      133845 :         for (uInt i=0; i<axes.size(); i++) {
     182       89230 :             if (axes[i] >= 0) {
     183       89218 :                 nGood++;
     184             :             }
     185             :         }
     186       44615 :         if (nGood != 2) {
     187          12 :             ok = false;
     188             :         }
     189             :     }
     190       44647 :     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       44647 :     return ok;
     195       44647 : }
     196             : 
     197         260 : vector<Double> CasacRegionManager::_setBoxCorners(const String& box) const {
     198         260 :     if (! box.empty()) {
     199         260 :         _supports2DBox(true);
     200             :     }
     201         260 :     Vector<String> boxParts = stringToVector(box);
     202         260 :     AlwaysAssert(boxParts.size() % 4 == 0, AipsError);
     203         260 :     vector<Double> corners(boxParts.size());
     204         537 :     for(uInt i=0; i<boxParts.size()/4; i++) {
     205         277 :         uInt index = 4*i;
     206        1385 :         for (uInt j=0; j<4; j++) {
     207        1108 :             boxParts[index + j].trim();
     208        1108 :             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        1108 :             corners[index + j] = String::toDouble(boxParts[index + j]);
     213             :         }
     214             :     }
     215         520 :     return corners;
     216         260 : }
     217             : 
     218       29716 : 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       59432 :     LogOrigin origin("CasacRegionManager", __func__);
     226       29716 :     Record regionRecord;
     227       29716 :     if (! box.empty()) {
     228         266 :         ThrowIf(
     229             :             regionPtr != nullptr,
     230             :             "box and regionPtr cannot be simultaneously specified"
     231             :         );
     232         267 :         ThrowIf(
     233             :             box.freq(",") % 4 != 3,
     234             :              "box not specified correctly"
     235             :         );
     236         265 :         if (regionName.empty()) {
     237         263 :             regionRecord = fromBCS(
     238             :                 diagnostics, nSelectedChannels, stokes,
     239             :                 chans, stokesControl, box, imShape
     240         231 :             ).toRecord("");
     241         231 :             if (verbose) {
     242         228 :                 *_getLog() << origin;
     243         228 :                 *_getLog() << LogIO::NORMAL << "Using specified box(es) "
     244         228 :                     << box << LogIO::POST;
     245             :             }
     246             :         }
     247             :         else {
     248           2 :             auto corners = stringToVector(box, ',');
     249           2 :             auto iter = corners.begin();
     250           2 :             auto end = corners.end();
     251           2 :             String crtfBoxString = "box[[";
     252           2 :             auto count = 0;
     253          10 :             for (; iter != end; ++iter, ++count) {
     254           8 :                 iter->trim();
     255           8 :                 if (count > 0 && count % 4 == 0) {
     256           0 :                     crtfBoxString += "\nbox[[";
     257             :                 }
     258           8 :                 crtfBoxString += *iter + "pix";
     259           8 :                 if (count % 2 == 0) {
     260           4 :                     crtfBoxString += ",";
     261             :                 }
     262             :                 else {
     263             :                     // close pixel pair
     264           4 :                     crtfBoxString += "]";
     265           4 :                     if (count - 1 % 4 == 0) {
     266             :                         // first pixel pair done, start second pixel pair
     267           2 :                         crtfBoxString += ",[";
     268             :                     }
     269             :                     else {
     270             :                         // second pixel pair done, close box
     271           2 :                         crtfBoxString += "]";
     272             :                     }
     273             :                 }
     274             :             }
     275           2 :             _setRegion(
     276             :                 regionRecord, diagnostics, regionName, imShape,
     277             :                 imageName, crtfBoxString, chans, stokes, verbose
     278             :             );
     279           2 :         }
     280             :     }
     281       29450 :     else if (regionPtr) {
     282        7336 :         ThrowIf(
     283             :             ! (regionName.empty() && chans.empty() && stokes.empty()),
     284             :             "regionPtr and regionName, chans, and/or stokes cannot "
     285             :             "be simultaneously specified"
     286             :         );
     287        7336 :         _setRegion(regionRecord, diagnostics, regionPtr);
     288        7336 :         stokes = _stokesFromRecord(regionRecord, stokesControl, imShape);
     289             :     }
     290       22114 :     else if (! regionName.empty()) {
     291          33 :         _setRegion(
     292             :             regionRecord, diagnostics, regionName,
     293             :             imShape, imageName, "", chans, stokes, verbose
     294             :         );
     295          11 :         if (verbose) {
     296          11 :             *_getLog() << origin;
     297          11 :             *_getLog() << LogIO::NORMAL << diagnostics << LogIO::POST;
     298             :         }
     299          11 :         stokes = _stokesFromRecord(regionRecord, stokesControl, imShape);
     300             :     }
     301             :     else {
     302       22092 :         vector<uInt> chanEndPts, polEndPts;
     303       22092 :         regionRecord = fromBCS(
     304             :             diagnostics, nSelectedChannels, stokes,
     305             :             chans, stokesControl, box, imShape
     306       22069 :         ).toRecord("");
     307       22069 :         if (verbose) {
     308        1485 :             *_getLog() << origin;
     309        1485 :             *_getLog() << LogIO::NORMAL << "No directional region specified. Using full positional plane."
     310        1485 :                 << LogIO::POST;
     311             :         }
     312       22069 :         const CoordinateSystem& csys = getcoordsys();
     313       22069 :         if (csys.hasSpectralAxis()) {
     314        4126 :             if (verbose) {
     315        1349 :                 if (chans.empty()) {
     316        1096 :                     *_getLog() << LogIO::NORMAL << "Using all spectral channels."
     317        1096 :                         << LogIO::POST;
     318             :                 }
     319             :                 else {
     320         253 :                     *_getLog() << LogIO::NORMAL << "Using channel range(s) "
     321         253 :                         << _pairsToString(chanEndPts) << LogIO::POST;
     322             :                 }
     323             :             }
     324             :         }
     325       22069 :         if (csys.hasPolarizationCoordinate() && verbose) {
     326        1222 :             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        1222 :                 *_getLog() << LogIO::NORMAL << "Using polarizations " << stokes << LogIO::POST;
     341             :             }
     342             :         }
     343       22115 :     }
     344       59298 :     return regionRecord;
     345       29783 : }
     346             : 
     347           7 : Record CasacRegionManager::regionFromString(
     348             :     const CoordinateSystem& csys, const String& regionStr,
     349             :     const String& imageName, const IPosition& imShape,
     350             :     Bool verbose
     351             : ) {
     352           7 :     CasacRegionManager mgr(csys);
     353           7 :     Record reg;
     354           7 :     String diag;
     355          10 :     mgr._setRegion(
     356             :         reg, diag, regionStr, imShape, imageName, "", "", "", verbose
     357             :     );
     358          12 :     return reg;
     359           9 : }
     360             : 
     361        7336 : void CasacRegionManager::_setRegion(
     362             :     Record& regionRecord, String& diagnostics,
     363             :     const Record* regionPtr
     364             : )  {
     365             :     // region record pointer provided
     366        7336 :     regionRecord = *(regionPtr);
     367             :     // set stokes from the region record
     368        7336 :     diagnostics = "used provided region record";
     369        7336 : }
     370             : 
     371          31 : 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          31 :     if (regionName.empty() && imageName.empty()) {
     381           0 :         regionRecord = Record();
     382           0 :         diagnostics = "No region string";
     383           4 :         return;
     384             :     }
     385             :     // region name provided
     386          31 :     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          31 :     );
     394          31 :     File myFile(regionName);
     395          31 :     const CoordinateSystem csys = getcoordsys();
     396          31 :     if (myFile.exists()) {
     397          10 :         ThrowIf(
     398             :             ! myFile.isReadable(),
     399             :             "File " + regionName + " exists but is not readable."
     400             :         );
     401          10 :         std::unique_ptr<Record> rec;
     402             :         try {
     403          22 :             rec.reset(readImageFile(regionName, ""));
     404             :         }
     405           6 :         catch(const AipsError& x) {
     406           6 :         }
     407          10 :         if (rec) {
     408           4 :             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           4 :             regionRecord = *rec;
     415           4 :             diagnostics = "Region read from binary region file " + regionName;
     416           4 :             return;
     417             :         }
     418             :         try {
     419             :             // CRTF file attempt
     420             :             RegionTextList annList(
     421             :                 regionName, csys, imShape, prependBox,
     422             :                 globalOverrideChans, globalStokesOverride
     423          11 :             );
     424           1 :             regionRecord = annList.regionAsRecord();
     425           1 :             diagnostics = "Region read from CRTF file " + regionName;
     426           1 :         }
     427           5 :         catch (const AipsError& x) {
     428           5 :             ThrowCc(
     429             :                 regionName + " is neither a valid binary region file, "
     430             :                 "nor a valid region text file."
     431             :             );
     432           5 :         }
     433          10 :     }
     434          21 :     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          13 :             );
     441          11 :             regionRecord = annList.regionAsRecord();
     442          11 :             diagnostics = "Region read from text string " + regionName;
     443          11 :         }
     444           1 :         catch (const AipsError& x) {
     445           1 :             ThrowCc(x.getMesg());
     446           1 :         }
     447             :     }
     448           9 :     else if (regionName.matches(image) || ! imageName.empty()) {
     449           4 :         ImageRegion imRegion;
     450           4 :         String imagename, region;
     451           4 :         if (regionName.matches(image)) {
     452           6 :             String res[2];
     453           1 :             casacore::split(regionName, res, 2, ":");
     454           1 :             imagename = res[0];
     455           1 :             region = res[1];
     456           3 :         }
     457             :         else {
     458             :             // imageName is not empty if we get here
     459           3 :             imagename = imageName;
     460           3 :             region = regionName;
     461             :         }
     462             :         try {
     463           4 :             Record *myRec = tableToRecord(imagename, region);
     464           4 :             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           4 :             if (Table::isReadable(imagename)) {
     471           3 :                 ThrowIf(
     472             :                     myRec == 0,
     473             :                     "Region " + region + " not found in image "
     474             :                     + imagename
     475             :                 );
     476           3 :                 regionRecord = *myRec;
     477           6 :                 diagnostics = "Used region " + region + " from image "
     478           3 :                     + imagename + " table description";
     479             :             }
     480             :             else {
     481           1 :                 *_getLog() << "Cannot read image " << imagename
     482           1 :                         << " to get region " << region << LogIO::EXCEPTION;
     483             :             }
     484             :         }
     485           1 :         catch (const AipsError&) {
     486           1 :             ThrowCc(
     487             :                 "Unable to open region file or region table description "
     488             :                 + region + " in image " + imagename
     489             :             );
     490           1 :         }
     491           6 :     }
     492             :     else {
     493           5 :         ostringstream oss;
     494           5 :         oss << "Unable to open region file or region table description "
     495           5 :             << regionName << "." << endl
     496           5 :             << "If it is supposed to be a text string its format is incorrect";
     497           5 :         ThrowCc(oss.str());
     498           5 :     }
     499          47 : }
     500             : 
     501       22358 : 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       22358 :     const CoordinateSystem& csys = getcoordsys();
     508             :     vector<uInt> chanEndPts = setSpectralRanges(
     509             :         chans, nSelectedChannels, imShape
     510       22374 :     );
     511       22342 :     Int polAxisNumber = csys.polarizationAxisNumber();
     512       22342 :     uInt nTotalPolarizations = polAxisNumber >= 0 ? imShape[polAxisNumber] : 0;
     513       22342 :     String firstStokes = polAxisNumber >= 0 ? csys.stokesAtPixel(0) : "";
     514             :     vector<uInt> polEndPts = _setPolarizationRanges(
     515             :         stokes, firstStokes,
     516             :         nTotalPolarizations, stokesControl
     517       22342 :     );
     518       22337 :     vector<Double> boxCorners;
     519       22337 :     if (box.empty()) {
     520       22077 :         if (_supports2DBox(false)) {
     521       22055 :             if (
     522       22055 :                 csys.hasDirectionCoordinate()
     523       22055 :                 || csys.hasLinearCoordinate()
     524             :             ) {
     525       22055 :                 Vector<Int> dirAxesNumbers;
     526       22055 :                 if (csys.hasDirectionCoordinate()) {
     527       22054 :                     dirAxesNumbers = csys.directionAxesNumbers();
     528             :                 }
     529             :                 else {
     530           1 :                     dirAxesNumbers = csys.linearAxesNumbers();
     531             :                 }
     532       22055 :                 Vector<Int> dirShape(2);
     533       22055 :                 dirShape[0] = imShape[dirAxesNumbers[0]];
     534       22055 :                 dirShape[1] = imShape[dirAxesNumbers[1]];
     535       22055 :                 boxCorners.resize(4);
     536       22055 :                 boxCorners[0] = 0;
     537       22055 :                 boxCorners[1] = 0;
     538       22055 :                 boxCorners[2] = dirShape[0] - 1;
     539       22055 :                 boxCorners[3] = dirShape[1] - 1;
     540       22055 :             }
     541             :         }
     542             :     }
     543             :     else {
     544         260 :         boxCorners = _setBoxCorners(box);
     545             :     }
     546             :     return _fromBCS(
     547             :         diagnostics, boxCorners,
     548             :         chanEndPts, polEndPts, imShape
     549       44674 :     );
     550       22449 : }
     551             : 
     552       22337 : 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       44674 :     LogOrigin origin("CasacRegionManager", __func__);
     558       22337 :     *_getLog() << origin;
     559       22337 :     Vector<Double> blc(imShape.nelements(), 0);
     560       22337 :     Vector<Double> trc(imShape.nelements(), 0);
     561       22337 :     const CoordinateSystem csys = getcoordsys();
     562       22337 :     Vector<Int> directionAxisNumbers = csys.directionAxesNumbers();
     563       22337 :     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       22337 :     vector<Int>::iterator iter = linearAxisNumbers.begin();
     567       22337 :     vector<Int>::iterator end = linearAxisNumbers.end();
     568       22373 :     while(iter != end) {
     569          36 :         if (*iter < 0) {
     570           6 :             iter = linearAxisNumbers.erase(iter);
     571             :         }
     572          36 :         ++iter;
     573             :     }
     574       22337 :     Int spectralAxisNumber = csys.spectralAxisNumber();
     575       22337 :     Int polarizationAxisNumber = csys.polarizationAxisNumber();
     576             : 
     577       22337 :     Vector<Double> xCorners(boxCorners.size()/2);
     578       22337 :     Vector<Double> yCorners(xCorners.size());
     579       66958 :     for (uInt i=0; i<xCorners.size(); i++) {
     580       44648 :         Double x = boxCorners[2*i];
     581       44648 :         Double y = boxCorners[2*i + 1];
     582             : 
     583       44648 :         if (x < 0 || y < 0 ) {
     584          15 :             *_getLog() << "blc in box spec is less than 0" << LogIO::EXCEPTION;
     585             :         }
     586       44633 :         if (csys.hasDirectionCoordinate()) {
     587       44631 :             if (
     588       44631 :                 x >= imShape[directionAxisNumbers[0]]
     589       44631 :                 || y >= imShape[directionAxisNumbers[1]]
     590             :             ) {
     591          12 :                 *_getLog() << "dAxisNum0=" <<directionAxisNumbers[0] <<" dAxisNum1="<<directionAxisNumbers[1];
     592          12 :                 *_getLog() << "x="<<x<<" imShape[0]="<<imShape[directionAxisNumbers[0]]<< " y="<<y<<" imShape[1]="<<imShape[directionAxisNumbers[1]]<<LogIO::POST;
     593          12 :                 *_getLog() << "trc in box spec is greater than or equal to number "
     594          12 :                     << "of direction coordinate pixels in the image" << LogIO::EXCEPTION;
     595             :             }
     596             :         }
     597           2 :         else if (
     598           2 :             csys.hasLinearCoordinate()
     599           4 :             && (
     600           2 :                 x >= imShape[linearAxisNumbers[0]]
     601           2 :                 || 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       44621 :         xCorners[i] = x;
     608       44621 :         yCorners[i] = y;
     609             :     }
     610       22310 :     Vector<Double> polEndPtsDouble(polEndPts.size());
     611       30552 :     for (uInt i=0; i<polEndPts.size(); ++i) {
     612        8242 :         polEndPtsDouble[i] = (Double)polEndPts[i];
     613             :     }
     614             : 
     615       22310 :     Bool csysSupports2DBox = _supports2DBox(false);
     616       22310 :     uInt nRegions = 1;
     617       22310 :     if (csysSupports2DBox) {
     618       22288 :         if (csys.hasDirectionCoordinate())  {
     619       22287 :             nRegions *= boxCorners.size()/4;
     620             :         }
     621       22288 :         if (csys.hasLinearCoordinate())  {
     622          23 :             nRegions *= boxCorners.size()/4;
     623             :         }
     624             :     }
     625       22310 :     if (csys.hasPolarizationCoordinate()) {
     626        4120 :         nRegions *= polEndPts.size()/2;
     627             :     }
     628       22310 :     if (csys.hasSpectralAxis()) {
     629        4239 :         nRegions *= chanEndPts.size()/2;
     630             :     }
     631       22310 :     Vector<Double> extXCorners(2*nRegions, 0);
     632       22310 :     Vector<Double> extYCorners(2*nRegions, 0);
     633       22310 :     Vector<Double> extPolEndPts(2*nRegions, 0);
     634       22310 :     Vector<Double> extChanEndPts(2*nRegions, 0);
     635             : 
     636       22310 :     uInt count = 0;
     637             : 
     638       22310 :     if (csysSupports2DBox) {
     639       44593 :         for (uInt i=0; i<max(uInt(1), xCorners.size()/2); i++) {
     640       44611 :             for (uInt j=0; j<max((uInt)1, polEndPts.size()/2); j++) {
     641       44618 :                 for (uInt k=0; k<max(uInt(1), chanEndPts.size()/2); k++) {
     642       22312 :                     if (
     643       22312 :                         csys.hasDirectionCoordinate()
     644       22312 :                         || csys.hasLinearCoordinate()
     645             :                     ) {
     646       22312 :                         extXCorners[2*count] = xCorners[2*i];
     647       22312 :                         extXCorners[2*count + 1] = xCorners[2*i + 1];
     648       22312 :                         extYCorners[2*count] = yCorners[2*i];
     649       22312 :                         extYCorners[2*count + 1] = yCorners[2*i + 1];
     650             :                     }
     651       22312 :                     if (csys.hasPolarizationCoordinate()) {
     652        4133 :                         extPolEndPts[2*count] = polEndPtsDouble[2*j];
     653        4133 :                         extPolEndPts[2*count + 1] = polEndPtsDouble[2*j + 1];
     654             : 
     655             :                     }
     656       22312 :                     if (csys.hasSpectralAxis()) {
     657        4239 :                         extChanEndPts[2*count] = chanEndPts[2*k];
     658        4239 :                         extChanEndPts[2*count + 1] = chanEndPts[2*k + 1];
     659             :                     }
     660       22312 :                     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          44 :         for (uInt j=0; j<max((uInt)1, polEndPts.size()/2); j++) {
     669          44 :             for (uInt k=0; k<max(uInt(1), chanEndPts.size()/2); k++) {
     670          22 :                 if (csys.hasPolarizationCoordinate()) {
     671           4 :                     extPolEndPts[2*count] = polEndPtsDouble[2*j];
     672           4 :                     extPolEndPts[2*count + 1] = polEndPtsDouble[2*j + 1];
     673             :                 }
     674          22 :                 if (csys.hasSpectralAxis()) {
     675          22 :                     extChanEndPts[2*count] = chanEndPts[2*k];
     676          22 :                     extChanEndPts[2*count + 1] = chanEndPts[2*k + 1];
     677             :                 }
     678          22 :                 count++;
     679             :             }
     680             :         }
     681             :     }
     682       22310 :     map<uInt, Vector<Double> > axisCornerMap;
     683       44644 :     for (uInt i=0; i<nRegions; i++) {
     684       75384 :         for (uInt axisNumber=0; axisNumber<csys.nPixelAxes(); axisNumber++) {
     685       53050 :             if (
     686             :                 (
     687       53050 :                     directionAxisNumbers.size() > 1
     688       53016 :                     && (Int)axisNumber == directionAxisNumbers[0]
     689             :                 )
     690      106100 :                 || (
     691       30739 :                     ! csys.hasDirectionCoordinate()
     692          34 :                     && linearAxisNumbers.size() > 1
     693           2 :                     && (Int)axisNumber == linearAxisNumbers[0]
     694             :                 )
     695             :             ) {
     696       22312 :                 axisCornerMap[axisNumber] = extXCorners;
     697             :             }
     698       30738 :             else if (
     699             :                 (
     700       30738 :                     directionAxisNumbers.size() > 1
     701       30705 :                     && (Int)axisNumber == directionAxisNumbers[1]
     702             :                 )
     703       61476 :                 || (
     704        8427 :                     ! csys.hasDirectionCoordinate()
     705          33 :                     && linearAxisNumbers.size() > 1
     706           1 :                     && (Int)axisNumber == linearAxisNumbers[1]
     707             :                 )
     708             :             ) {
     709       22312 :                 axisCornerMap[axisNumber] = extYCorners;
     710             :             }
     711        8426 :             else if ((Int)axisNumber == spectralAxisNumber) {
     712        4261 :                 axisCornerMap[axisNumber] = extChanEndPts;
     713             :             }
     714        4165 :             else if ((Int)axisNumber == polarizationAxisNumber) {
     715        4137 :                 axisCornerMap[axisNumber] = extPolEndPts;
     716             :             }
     717             :             else {
     718          28 :                 Vector<Double> range(2, 0);
     719          28 :                 range[1] = imShape[axisNumber] - 1;
     720          28 :                 axisCornerMap[axisNumber] = range;
     721          28 :             }
     722             :         }
     723             :     }
     724       22310 :     ImageRegion imRegion;
     725       44637 :     for (uInt i=0; i<nRegions; i++) {
     726       75384 :         for (uInt axisNumber=0; axisNumber<csys.nPixelAxes(); axisNumber++) {
     727       53050 :             blc(axisNumber) = axisCornerMap[axisNumber][2*i];
     728       53050 :             trc(axisNumber) = axisCornerMap[axisNumber][2*i + 1];
     729             :         }
     730       22334 :         LCBox lcBox(blc, trc, imShape);
     731       22327 :         WCBox wcBox(lcBox, csys);
     732       22327 :         ImageRegion thisRegion(wcBox);
     733             :         imRegion = (i == 0)
     734             :               ? thisRegion
     735       22327 :               : imRegion = *(doUnion(imRegion, thisRegion));
     736       22327 :     }
     737       22303 :     ostringstream os;
     738       22303 :     os << "Used image region from " << endl;
     739       22303 :     if (csys.hasDirectionCoordinate()) {
     740       22280 :         os << "    position box corners: ";
     741       44577 :         for (uInt i=0; i<boxCorners.size()/4; i++) {
     742       22297 :             os << boxCorners[4*i] << ", " << boxCorners[4*i + 1]
     743       22297 :                 << ", " << boxCorners[4*i + 2] << ", " << boxCorners[4*i + 3];
     744       22297 :             if (i < boxCorners.size()/4 - 1) {
     745          17 :                 os << "; ";
     746             :             }
     747             :         }
     748             :     }
     749       22303 :     if (getcoordsys().hasSpectralAxis()) {
     750        4232 :         os << "    spectral channel ranges: " << _pairsToString(chanEndPts);
     751             :     }
     752       22303 :     if (getcoordsys().hasPolarizationCoordinate()) {
     753        4113 :         os << "    polarization pixel ranges: " << _pairsToString(polEndPts);
     754             :     }
     755       22303 :     diagnostics = os.str();
     756       44606 :     return imRegion;
     757       22624 : }
     758             : 
     759        8598 : String CasacRegionManager::_pairsToString(const vector<uInt>& pairs) const {
     760        8598 :     ostringstream os;
     761        8598 :     uInt nPairs = pairs.size()/2;
     762       16947 :     for (uInt i=0; i<nPairs; i++) {
     763        8349 :         os << pairs[2*i] << " - " << pairs[2*i + 1];
     764        8349 :         if (i < nPairs - 1) {
     765           4 :             os << "; ";
     766             :         }
     767             :     }
     768       25794 :     return os.str();
     769        8598 : }
     770             : 
     771        7347 : 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        7347 :     String stokes = "";
     776        7347 :     CoordinateSystem csys = getcoordsys();
     777        7347 :     if(! csys.hasPolarizationCoordinate()) {
     778        1143 :         return stokes;
     779             :     }
     780        6204 :     Int polAxis = csys.polarizationAxisNumber();
     781        6204 :     if (shape[polAxis] == 1) {
     782             :         // degenerate stokes axis
     783        6040 :         return csys.stokesAtPixel(0);
     784             :     }
     785         164 :     uInt stokesBegin = 0;
     786         164 :     uInt stokesEnd = 0;
     787         164 :     if (region.empty()) {
     788          24 :         stokesEnd = stokesControl == USE_FIRST_STOKES
     789             :             ? 0
     790          24 :             : csys.stokesCoordinate().stokes().size() - 1;
     791             :     }
     792             :     else {
     793         280 :         std::unique_ptr<ImageRegion> imreg(ImageRegion::fromRecord(region, ""));
     794         140 :         Array<Float> blc, trc;
     795         140 :         Bool oneRelAccountedFor = false;
     796         140 :         if (imreg->isLCSlicer()) {
     797          74 :             blc = imreg->asLCSlicer().blc();
     798          74 :             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          74 :             trc = imreg->asLCSlicer().trc();
     805          74 :             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          74 :             stokesBegin = (uInt)((Vector<Float>)blc)[polAxis];
     812          74 :             stokesEnd = (uInt)((Vector<Float>)trc)[polAxis];
     813          74 :             oneRelAccountedFor = true;
     814             :         }
     815          66 :         else if (
     816         132 :                 RegionManager::isPixelRegion(
     817          66 :                     *(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          66 :         else if (region.fieldNumber("x") >= 0 && region.fieldNumber("y") >= 0) {
     826             :             // world polygon
     827           1 :             oneRelAccountedFor = true;
     828           1 :             stokesBegin = 0;
     829           2 :             stokesEnd = stokesControl == USE_FIRST_STOKES
     830           1 :                 ? 0 : shape[polAxis];
     831             :         }
     832          65 :         else if (region.fieldNumber("blc") >= 0 && region.fieldNumber("blc") >= 0) {
     833             :             // world box
     834          59 :             Record blcRec = region.asRecord("blc");
     835          59 :             Record trcRec = region.asRecord("trc");
     836          59 :             String polField = "*" + String::toString(polAxis + 1);
     837          59 :             stokesBegin = blcRec.isDefined(polField)
     838         177 :                 ? (Int)blcRec.asRecord(
     839         118 :                     String(polField)
     840          59 :                 ).asDouble("value")
     841             :                 : 0;
     842          59 :             stokesEnd = trcRec.isDefined(polField)
     843         118 :                 ? (Int)blcRec.asRecord(
     844         118 :                     String(polField)
     845          59 :                 ).asDouble("value")
     846             :                 : stokesControl == USE_FIRST_STOKES
     847             :                   ? 0
     848           0 :                   : shape[polAxis];
     849          59 :             if (! blcRec.isDefined(polField)) {
     850           0 :                 oneRelAccountedFor = true;
     851             :             }
     852          59 :         }
     853             :         else {
     854             :             // FIXME not very nice, but until all can be implemented this will have to do
     855           6 :             *_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           6 :                 << LogIO::POST;
     858           6 :             return stokes;
     859             :         }
     860         134 :         if (
     861         134 :             ! oneRelAccountedFor
     862         193 :             && region.isDefined("oneRel")
     863         193 :             && region.asBool("oneRel")
     864             :         ) {
     865          59 :             stokesBegin--;
     866          59 :             stokesEnd--;
     867             :         }
     868         152 :     }
     869         488 :     for (uInt i=stokesBegin; i<=stokesEnd; i++) {
     870         330 :         stokes += csys.stokesAtPixel(i);
     871             :     }
     872         158 :     return stokes;
     873        7347 : }
     874             : 
     875       22214 : vector<uInt> CasacRegionManager::_initSpectralRanges(
     876             :     uInt& nSelectedChannels, const IPosition& imShape
     877             : ) const {
     878       22214 :     vector<uInt> ranges(0);
     879       22214 :     if (! getcoordsys().hasSpectralAxis()) {
     880       18248 :         nSelectedChannels = 0;
     881       18248 :         return ranges;
     882             :     }
     883        3966 :     uInt specNum = getcoordsys().spectralAxisNumber();
     884        3966 :     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        3966 :     uInt nChannels = imShape[getcoordsys().spectralAxisNumber()];
     891        3966 :     ranges.push_back(0);
     892        3966 :     ranges.push_back(nChannels - 1);
     893        3966 :     nSelectedChannels = nChannels;
     894        3966 :     return ranges;
     895           0 : }
     896             : 
     897         326 : vector<uInt> CasacRegionManager::setSpectralRanges(
     898             :     uInt& nSelectedChannels,
     899             :     const Record *const regionRec, const IPosition& imShape
     900             : ) const {
     901         326 :     if (regionRec == 0 || ! getcoordsys().hasSpectralAxis()) {
     902         177 :         return _initSpectralRanges(nSelectedChannels, imShape);
     903             :     }
     904             :     else {
     905         149 :         return _spectralRangeFromRegionRecord(nSelectedChannels, regionRec, imShape);
     906             :     }
     907             : }
     908             : 
     909       22388 : vector<uInt> CasacRegionManager::setSpectralRanges(
     910             :     String specification, uInt& nSelectedChannels,
     911             :     /*const String& globalChannelOverride,
     912             :     const String& globalStokesOverride, */ const IPosition& imShape
     913             : ) const {
     914       44776 :     LogOrigin origin("CasacRegionManager", __func__);
     915       22388 :     *_getLog() << origin;
     916       22388 :     specification.trim();
     917       22388 :     String x = specification;
     918       22388 :     x.upcase();
     919       22388 :     if (x.empty() || x == ALL) {
     920       22037 :         return _initSpectralRanges(nSelectedChannels, imShape);
     921             :     }
     922         351 :     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         351 :     else if (specification.contains("range")) {
     930             :         // this is a specification in the "new" ASCII region format
     931           1 :         return _spectralRangeFromRangeFormat(nSelectedChannels, specification, imShape);
     932             :     }
     933             :     else {
     934         350 :         uInt nChannels = imShape[getcoordsys().spectralAxisNumber()];
     935             :         return ParameterParser::spectralRangesFromChans(
     936             :             nSelectedChannels, specification, nChannels
     937         350 :         );
     938             :     }
     939       22409 : }
     940             : 
     941         149 : vector<uInt> CasacRegionManager::_spectralRangeFromRegionRecord(
     942             :     uInt& nSelectedChannels, const Record *const regionRec,
     943             :     const IPosition& imShape
     944             : ) const {
     945         149 :     const CoordinateSystem& csys = getcoordsys();
     946         149 :     TempImage<Float> x(imShape, csys);
     947         149 :     x.set(0);
     948         298 :     SPCIIF subimage = SubImageFactory<Float>::createSubImageRO(
     949         298 :         x, *regionRec, "", _getLog(), AxesSpecifier(), false, true
     950         149 :     );
     951         149 :     uInt nChan = 0;
     952             :     {
     953         149 :         ImageMetaData<Float> md(subimage);
     954         149 :         nChan = md.nChannels();
     955         149 :     }
     956         149 :     const SpectralCoordinate& subsp = subimage->coordinates().spectralCoordinate();
     957             :     Double subworld;
     958         149 :     subsp.toWorld(subworld, 0);
     959         149 :     const SpectralCoordinate& imsp = csys.spectralCoordinate();
     960             :     Double pixOff;
     961         149 :     imsp.toPixel(pixOff, subworld);
     962         149 :     Int specAxisNumber = csys.spectralAxisNumber();
     963         149 :     IPosition start(subimage->ndim(), 0);
     964         149 :     ArrayLattice<Bool> mask(subimage->getMask());
     965         149 :     IPosition planeShape = subimage->shape();
     966         149 :     vector<uInt> myList;
     967         149 :     planeShape[specAxisNumber] = 1;
     968         313 :     for (uInt i=0; i<nChan; i++) {
     969         164 :         start[specAxisNumber] = i;
     970         164 :         Array<Bool> maskSlice;
     971         164 :         mask.getSlice(maskSlice, start, planeShape);
     972         164 :         if (anyTrue(maskSlice)) {
     973         164 :             uInt real = i + (uInt)rint(pixOff);
     974         164 :             myList.push_back(real);
     975         164 :             myList.push_back(real);
     976             :         }
     977         164 :     }
     978         298 :     return ParameterParser::consolidateAndOrderRanges(nSelectedChannels, myList);
     979         149 : }
     980             : 
     981           1 : 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           1 :     const CoordinateSystem csys = getcoordsys();
     989             :     RegionTextParser::ParamSet parms = RegionTextParser::getParamSet(
     990           1 :         spectralParmsUpdated, *_getLog(),
     991           2 :         specification, "", csys, std::shared_ptr<std::pair<MFrequency, MFrequency> >(nullptr),
     992           1 :         std::shared_ptr<Vector<Stokes::StokesTypes> >(nullptr)
     993           3 :     );
     994           1 :     RegionTextParser::ParamSet::const_iterator end = parms.end();
     995           1 :     for (
     996           1 :         RegionTextParser::ParamSet::const_iterator iter=parms.begin();
     997           2 :         iter!=end; iter++
     998             :     ) {
     999           1 :         AnnotationBase::Keyword key = iter->first;
    1000           1 :         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           1 :     String regSpec = "box[[0pix, 0pix], [1pix, 1pix]] " + specification;
    1011           2 :     RegionTextParser parser(csys, imShape, regSpec);
    1012           1 :     vector<uInt> range(2);
    1013           1 :     ThrowIf(
    1014             :         parser.getLines().empty(),
    1015             :         "The specified spectral range " + specification
    1016             :         + " does not intersect the image spectral range."
    1017             :     );
    1018           1 :     auto ann = parser.getLines()[0].getAnnotationBase();
    1019           1 :     /*const AnnRegion*/ auto *reg = dynamic_cast<const AnnRegion*>(ann.get());
    1020           1 :     ThrowIf(! reg, "Dynamic cast failed");
    1021           1 :     /*vector<Double>*/ const auto drange = reg->getSpectralPixelRange();
    1022           1 :     range[0] = uInt(max(0.0, floor(drange[0] + 0.5)));
    1023           1 :     range[1] = uInt(floor(drange[1] + 0.5));
    1024           1 :     nSelectedChannels = range[1] - range[0] + 1;
    1025           2 :     return range;
    1026           1 : }
    1027             : 
    1028             : }

Generated by: LCOV version 1.16