diff --git a/carta/cpp/CartaLib/CartaLib.pro b/carta/cpp/CartaLib/CartaLib.pro index 8b333d20..ffd41960 100755 --- a/carta/cpp/CartaLib/CartaLib.pro +++ b/carta/cpp/CartaLib/CartaLib.pro @@ -22,6 +22,7 @@ SOURCES += \ Hooks/ImageStatisticsHook.cpp \ Hooks/LoadRegion.cpp \ Hooks/Plot2DResult.cpp \ + Hooks/ProfileResult.cpp \ IImage.cpp \ PixelType.cpp \ Slice.cpp \ @@ -60,6 +61,7 @@ HEADERS += \ Hooks/ImageStatisticsHook.h \ Hooks/LoadRegion.h \ Hooks/Plot2DResult.h \ + Hooks/ProfileResult.h \ IPlugin.h \ IImage.h \ PixelType.h \ diff --git a/carta/cpp/CartaLib/Hooks/ProfileHook.h b/carta/cpp/CartaLib/Hooks/ProfileHook.h index 2bca25bd..6a6468b1 100755 --- a/carta/cpp/CartaLib/Hooks/ProfileHook.h +++ b/carta/cpp/CartaLib/Hooks/ProfileHook.h @@ -4,11 +4,12 @@ **/ #pragma once - +#include "ProfileResult.h" #include "CartaLib/CartaLib.h" #include "CartaLib/IPlugin.h" #include "CartaLib/RegionInfo.h" #include "CartaLib/ProfileInfo.h" +#include "CartaLib/Hooks/ProfileResult.h" namespace Carta { @@ -19,13 +20,16 @@ class ImageInterface; } namespace Hooks { + + + class ProfileHook : public BaseHook { CARTA_HOOK_BOILER1( ProfileHook ); public: - //The intensity counts - typedef std::vector ResultType; + //The results from generating a profile. + typedef Carta::Lib::Hooks::ProfileResult ResultType; /** * @brief Params diff --git a/carta/cpp/CartaLib/Hooks/ProfileResult.cpp b/carta/cpp/CartaLib/Hooks/ProfileResult.cpp new file mode 100755 index 00000000..a909e86c --- /dev/null +++ b/carta/cpp/CartaLib/Hooks/ProfileResult.cpp @@ -0,0 +1,83 @@ +#include +#include + +namespace Carta { + namespace Lib { + namespace Hooks { + +ProfileResult::ProfileResult( double restFrequency, const QString& restUnits, + const std::vector< std::pair > data){ + m_data = data; + m_restUnits = restUnits; + m_restFrequency = restFrequency; +} + +std::vector< std::pair > ProfileResult::getData() const { + return m_data; +} + +QString ProfileResult::getError() const { + return m_errorMessage; +} + +QString ProfileResult::getRestUnits() const { + return m_restUnits; +} + +double ProfileResult::getRestFrequency() const { + return m_restFrequency; +} + + +void ProfileResult::setData( const std::vector< std::pair >& data ){ + m_data = data; +} + +void ProfileResult::setRestFrequency( double restFreq ){ + m_restFrequency = restFreq; +} + +void ProfileResult::setRestUnits( const QString& restUnits ){ + m_restUnits = restUnits; +} + +void ProfileResult::setError( const QString& errorMsg ){ + m_errorMessage = errorMsg; +} + +QDataStream &operator<<(QDataStream& out, const ProfileResult& result ){ + out << result.getRestUnits()<< result.getRestFrequency(); + std::vector> data = result.getData(); + int dataCount = data.size(); + out << dataCount; + for ( int i = 0; i < dataCount; i++ ){ + out << data[i].first << data[i].second; + } + return out; +} + + +QDataStream &operator>>(QDataStream& in, ProfileResult& result ){ + QString name; + QString unitsX; + QString unitsY; + + double restFrequency; + QString restUnits; + int dataCount; + in >> restUnits >> restFrequency; + in >> dataCount; + std::vector > data( dataCount ); + for ( int i = 0; i < dataCount; i++ ){ + double firstEle; + double secondEle; + in >> firstEle >> secondEle; + data[i] = std::pair( firstEle, secondEle ); + } + result = ProfileResult( restFrequency, restUnits, data ); + return in; +} + + } + } +} diff --git a/carta/cpp/CartaLib/Hooks/ProfileResult.h b/carta/cpp/CartaLib/Hooks/ProfileResult.h new file mode 100755 index 00000000..8cf964ee --- /dev/null +++ b/carta/cpp/CartaLib/Hooks/ProfileResult.h @@ -0,0 +1,85 @@ +/** + * Stores profile data and associated information needed to display a profile and + * information about the profile. + */ +#pragma once +#include +#include +#include "CartaLib/ProfileInfo.h" + +namespace Carta{ +namespace Lib{ + +namespace Hooks { + +class ProfileResult { + + + public: + ProfileResult( double imageRest = 0, const QString& restUnits = "", + const std::vector< std::pair > data = std::vector< std::pair >()); + + /** + * Return (x,y) data pairs that comprise a profile. + * @return - (x,y) data pairs that comprise a profile curve. + */ + std::vector< std::pair > getData() const; + + /** + * Return information about any errors that prevented the computation of a profile. + * @return - an error message or an empty string if no errors were encountered. + */ + QString getError() const; + + /** + * Returns the image rest frequency units. + * @return - the image rest frequency units. + */ + QString getRestUnits() const; + + /** + * Returns the image rest frequency. + * @return - the image rest frequency. + */ + double getRestFrequency() const; + + /** + * Store the (x,y) data pairs that comprise a profile. + * @param data - the (x,y) data pairs that make up a profile. + */ + void setData( const std::vector< std::pair >& data ); + + /** + * Set an error message if there was a problem computing the profile. + * @param errorMessage - a description of the problem. + */ + void setError( const QString& errorMsg ); + + /** + * Store the image rest frequency. + * @param restFreq - the image rest frequency. + */ + void setRestFrequency( double restFreq ); + + /** + * Store the image rest units. + * @param restUnits - the image rest units. + */ + void setRestUnits( const QString& restUnits ); + + virtual ~ProfileResult(){} + + private: + std::vector< std::pair > m_data; + double m_restFrequency; + QString m_restUnits; + QString m_errorMessage; +}; + +//Serialization so that the profile result can be generated in a separate process. +QDataStream &operator<<(QDataStream& out, const Carta::Lib::Hooks::ProfileResult& result ); +QDataStream &operator>>(QDataStream& in, Carta::Lib::Hooks::ProfileResult& result ); + +} +} +} diff --git a/carta/cpp/CartaLib/ProfileInfo.cpp b/carta/cpp/CartaLib/ProfileInfo.cpp index e304f882..96114a42 100644 --- a/carta/cpp/CartaLib/ProfileInfo.cpp +++ b/carta/cpp/CartaLib/ProfileInfo.cpp @@ -1,11 +1,15 @@ #include "ProfileInfo.h" +#include namespace Carta { namespace Lib { ProfileInfo::ProfileInfo(){ + m_aggregateType = AggregateType::MEAN; + m_spectralType = "default"; m_restFrequency = 0; - m_restUnit = "GHz"; + m_restUnit = ""; + m_spectralUnit = "pixel"; } ProfileInfo::AggregateType ProfileInfo::getAggregateType() const { @@ -20,10 +24,54 @@ QString ProfileInfo::getRestUnit() const { return m_restUnit; } +QString ProfileInfo::getSpectralType() const { + return m_spectralType; +} + +QString ProfileInfo::getSpectralUnit() const { + return m_spectralUnit; +} + +bool ProfileInfo::operator==( const ProfileInfo& rhs ) { + bool equalProfiles = false; + if ( m_aggregateType == rhs.m_aggregateType ){ + if ( m_restUnit == rhs.m_restUnit ){ + if ( m_spectralType == rhs.m_spectralType ){ + if ( m_spectralUnit == rhs.m_spectralUnit ){ + const double ERROR_MARGIN = 0.000001; + if ( fabs( m_restFrequency - rhs.m_restFrequency ) < ERROR_MARGIN ){ + equalProfiles = true; + } + } + } + } + } + return equalProfiles; +} + +bool ProfileInfo::operator!=( const ProfileInfo& rhs ) { + return !( *this== rhs ); +} -ProfileInfo & ProfileInfo::setAggregateType( const ProfileInfo::AggregateType & knownType ){ +void ProfileInfo::setAggregateType( const ProfileInfo::AggregateType & knownType ){ m_aggregateType = knownType; - return * this; +} + +void ProfileInfo::setRestFrequency( double freq ) { + m_restFrequency = freq; +} + + +void ProfileInfo::setRestUnit( const QString& unit ){ + m_restUnit = unit; +} + +void ProfileInfo::setSpectralType( const QString& specType ){ + m_spectralType = specType; +} + +void ProfileInfo::setSpectralUnit( const QString& specUnit ){ + m_spectralUnit = specUnit; } ProfileInfo::~ProfileInfo(){ diff --git a/carta/cpp/CartaLib/ProfileInfo.h b/carta/cpp/CartaLib/ProfileInfo.h index 5da51d27..fc7f3008 100644 --- a/carta/cpp/CartaLib/ProfileInfo.h +++ b/carta/cpp/CartaLib/ProfileInfo.h @@ -10,8 +10,7 @@ class ProfileInfo { public: /// Methods of summarizing profiles - enum class AggregateType - { + enum class AggregateType { MEAN, MEDIAN, RMS, @@ -35,8 +34,8 @@ class ProfileInfo AggregateType getAggregateType() const; /** - * Return the rest frequency used in generating the profile. - * @return - the rest frequency used in generating the profile. + * Return the rest frequency in the image. + * @return - the rest frequency in the image. */ double getRestFrequency() const; @@ -46,18 +45,76 @@ class ProfileInfo */ QString getRestUnit() const; + /** + * Return a string indicating the profile spectral type + * (for example, 'radio velocity' or 'optical velocity'). + * @return - a string representing the general category + * of spectral units. + */ + QString getSpectralType() const; + + /** + * Return the actual spectral unit such as 'MHz' or 'mm'. + * @return - the spectral unit. + */ + QString getSpectralUnit() const; + + /** + * Equality operator. + * @param rhs - the other ProfileInfo to compare to. + * @return true if the other ProfileInfo matches this one; false otherwise. + */ + bool operator==( const ProfileInfo& rhs ); + + /** + * Inequality operator. + * @param rhs - the other ProfileInfo to compare to. + * @return true if the other ProfileInfo does not match this one; false otherwise. + */ + bool operator!=( const ProfileInfo& rhs ); + /** * Set the method used for aggregating data. * @param aggType - the method used for aggregating data. */ - ProfileInfo & setAggregateType( const AggregateType & aggType ); + void setAggregateType( const AggregateType & aggType ); + + /** + * Set the rest frequency. + * @param freq - the rest frequency used in the profile + * calculation. + */ + void setRestFrequency( double freq ); + + /** + * Set a string indicating the general category of spectral + * units. + * @param specType - a string indicating the general category + * of spectral units. + */ + void setSpectralType( const QString& specType ); + + /** + * Set the rest frequency units. + * @param unit - the rest frequency units. + */ + void setRestUnit( const QString& unit ); + + /** + * Set the spectral units such as "mm" or "GHz". + * @param unit - the actual units to use in calculating the + * profile. + */ + void setSpectralUnit( const QString& unit ); virtual ~ProfileInfo(); protected: - AggregateType m_aggregateType = AggregateType::OTHER; + AggregateType m_aggregateType; double m_restFrequency; QString m_restUnit; + QString m_spectralUnit; + QString m_spectralType; }; } // namespace Lib diff --git a/carta/cpp/CartaLib/RegionInfo.cpp b/carta/cpp/CartaLib/RegionInfo.cpp index 1faa5da1..a1371280 100644 --- a/carta/cpp/CartaLib/RegionInfo.cpp +++ b/carta/cpp/CartaLib/RegionInfo.cpp @@ -58,6 +58,31 @@ RegionInfo::~RegionInfo(){ } +bool RegionInfo::operator==( const RegionInfo& rhs ) { + bool equalRegions = false; + if ( m_regionType == rhs.m_regionType ){ + if ( m_corners.size() == rhs.m_corners.size() ){ + int cornerCount = m_corners.size(); + const double ERROR_MARGIN = 0.000001; + int i = 0; + for ( ; i < cornerCount; i++ ){ + if ( fabs( m_corners[i].first - rhs.m_corners[i].first ) >= ERROR_MARGIN || + fabs( m_corners[i].second - rhs.m_corners[i].second ) >= ERROR_MARGIN ){ + break; + } + } + if ( i == cornerCount ){ + equalRegions = true; + } + } + } + return equalRegions; + +} +bool RegionInfo::operator!=( const RegionInfo& rhs ) { + return !( *this== rhs ); +} + } } diff --git a/carta/cpp/CartaLib/RegionInfo.h b/carta/cpp/CartaLib/RegionInfo.h index 61209968..bdd34292 100644 --- a/carta/cpp/CartaLib/RegionInfo.h +++ b/carta/cpp/CartaLib/RegionInfo.h @@ -56,6 +56,20 @@ class RegionInfo { */ bool isCorner( std::pair pt ) const; + /** + * Equality operator. + * @param rhs - the other RegionInfo to compare to. + * @return true if the other RegionInfo matches this one; false otherwise. + */ + bool operator==( const RegionInfo& rhs ); + + /** + * Inequality operator. + * @param rhs - the other RegionInfo to compare to. + * @return true if the other RegionInfo does not match this one; false otherwise. + */ + bool operator!=( const RegionInfo& rhs ); + /** * Set region corners. * @param corners - a new list or region corners. @@ -74,6 +88,8 @@ class RegionInfo { std::vector > m_corners; }; + + } } diff --git a/carta/cpp/core/Data/Colormap/ColorState.cpp b/carta/cpp/core/Data/Colormap/ColorState.cpp index ee8940be..f53a38ec 100644 --- a/carta/cpp/core/Data/Colormap/ColorState.cpp +++ b/carta/cpp/core/Data/Colormap/ColorState.cpp @@ -1,6 +1,8 @@ #include "ColorState.h" #include "Colormaps.h" #include "TransformsData.h" +#include "TransformsImage.h" + #include "Data/Util.h" #include "State/StateInterface.h" #include "State/UtilState.h" @@ -19,8 +21,8 @@ const QString ColorState::COLOR_MAP_NAME = "colorMapName"; const QString ColorState::COLORED_OBJECT = "coloredObject"; const QString ColorState::REVERSE = "reverse"; const QString ColorState::INVERT = "invert"; -const QString ColorState::GLOBAL = "global"; const QString ColorState::COLOR_MIX = "colorMix"; + const QString ColorState::INTENSITY_MIN = "intensityMin"; const QString ColorState::INTENSITY_MAX = "intensityMax"; const QString ColorState::NAN_COLOR = "nanColor"; @@ -28,12 +30,14 @@ const QString ColorState::NAN_DEFAULT = "nanDefault"; const QString ColorState::SCALE_1 = "scale1"; const QString ColorState::SCALE_2 = "scale2"; const QString ColorState::GAMMA = "gamma"; -const QString ColorState::SIGNIFICANT_DIGITS = "significantDigits"; + const QString ColorState::TRANSFORM_IMAGE = "imageTransform"; const QString ColorState::TRANSFORM_DATA = "dataTransform"; Colormaps* ColorState::m_colors = nullptr; TransformsData* ColorState::m_dataTransforms = nullptr; +TransformsImage* ColorState::m_imageTransforms = nullptr; + class ColorState::Factory : public Carta::State::CartaObjectFactory { @@ -50,9 +54,10 @@ bool ColorState::m_registered = ColorState::ColorState( const QString& path, const QString& id): CartaObject( CLASS_NAME, path, id ){ - _initializeDefaultState( m_state ); _initializeStatics(); - _setErrorMargin(); + _initializeDefaultState( m_state ); + + } int ColorState::_getBorderGreen() const { @@ -76,10 +81,12 @@ int ColorState::_getBorderTransparency() const { return m_state.getValue( alphaLookup ); } + QString ColorState::_getColorMap() const { return m_state.getValue( COLOR_MAP_NAME ); } + QString ColorState::_getDataTransform() const { return m_state.getValue( TRANSFORM_DATA ); } @@ -131,7 +138,6 @@ void ColorState::_initializeDefaultState( Carta::State::StateInterface& state ){ state.insertValue( COLOR_MAP_NAME, "Gray" ); state.insertValue(REVERSE, false); state.insertValue(INVERT, false ); - state.insertValue(GLOBAL, true ); state.insertValue(NAN_DEFAULT, true ); state.insertValue(BORDER_DEFAULT, true ); @@ -148,9 +154,9 @@ void ColorState::_initializeDefaultState( Carta::State::StateInterface& state ){ QString blueKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::BLUE ); state.insertValue( blueKey, 1 ); - state.insertValue(SIGNIFICANT_DIGITS, 6 ); - state.insertValue(TRANSFORM_IMAGE, "Gamma"); - state.insertValue(TRANSFORM_DATA, "None"); + + state.insertValue(TRANSFORM_IMAGE, m_imageTransforms->getDefault()); + state.insertValue(TRANSFORM_DATA, m_dataTransforms->getDefault()); //Nan color state.insertObject( NAN_COLOR ); @@ -173,9 +179,6 @@ void ColorState::_initializeDefaultState( Carta::State::StateInterface& state ){ state.insertValue( greenLookup, 0 ); alphaLookup = Carta::State::UtilState::getLookup( BORDER_COLOR, Util::ALPHA ); state.insertValue( alphaLookup, 255 ); - - //Default Tab - state.insertValue( Util::TAB_INDEX, 0 ); } @@ -190,12 +193,14 @@ void ColorState::_initializeStatics(){ if ( m_dataTransforms == nullptr ){ m_dataTransforms = Util::findSingletonObject(); } -} -bool ColorState::_isGlobal() const { - return m_state.getValue( GLOBAL ); + //Image transforms + if ( m_imageTransforms == nullptr ){ + m_imageTransforms = Util::findSingletonObject(); + } } + bool ColorState::_isBorderDefault() const { return m_state.getValue( BORDER_DEFAULT ); } @@ -212,6 +217,7 @@ bool ColorState::_isInverted() const { return m_state.getValue( INVERT ); } + void ColorState::_replicateTo( ColorState* otherState ){ if ( otherState != nullptr ){ _replicateTo( otherState->m_state ); @@ -222,8 +228,6 @@ void ColorState::_replicateTo( ColorState* otherState ){ void ColorState::_replicateTo( Carta::State::StateInterface& otherState ){ QString colorMapName = m_state.getValue(COLOR_MAP_NAME ); otherState.setValue(COLOR_MAP_NAME, colorMapName ); - bool global = _isGlobal(); - otherState.setValue( GLOBAL, global ); bool inverted = _isInverted(); otherState.setValue( INVERT, inverted ); bool reversed = _isReversed(); @@ -257,6 +261,10 @@ void ColorState::_replicateTo( Carta::State::StateInterface& otherState ){ double gamma = m_state.getValue( GAMMA ); otherState.setValue(GAMMA, gamma ); + double scale1 = m_state.getValue( SCALE_1 ); + otherState.setValue( SCALE_1, scale1 ); + double scale2 = m_state.getValue( SCALE_2 ); + otherState.setValue( SCALE_2, scale2 ); QString dataTransform = m_state.getValue( TRANSFORM_DATA ); otherState.setValue(TRANSFORM_DATA, dataTransform); @@ -273,15 +281,14 @@ void ColorState::_replicateTo( Carta::State::StateInterface& otherState ){ otherState.setValue( greenKey, green ); otherState.setValue( blueKey, blue ); otherState.setValue( alphaKey, alpha ); - - int tabIndex = m_state.getValue( Util::TAB_INDEX ); - otherState.setValue( Util::TAB_INDEX, tabIndex ); } + void ColorState::_resetState( const QString& stateStr ){ m_state.setState( stateStr ); } + QString ColorState::_setBorderAlpha( int alphaValue ){ QString result; const QString USER_ID = "Border background"; @@ -306,6 +313,7 @@ QString ColorState::_setBorderColor( int redValue, int greenValue, int blueValue return result; } + void ColorState::_setBorderDefault( bool useDefault ){ bool oldBorderDefault = m_state.getValue( BORDER_DEFAULT ); if ( useDefault != oldBorderDefault ){ @@ -317,6 +325,7 @@ void ColorState::_setBorderDefault( bool useDefault ){ } } + bool ColorState::_setColor( const QString& key, const QString& majorKey, const QString& userId, int colorAmount, QString& errorMsg ){ bool colorChanged = false; @@ -354,7 +363,8 @@ bool ColorState::_setColorMix( const QString& key, double colorPercent, QString& else { QString mixKey = Carta::State::UtilState::getLookup( COLOR_MIX, key ); double oldColorPercent = m_state.getValue( mixKey ); - if ( abs( colorPercent - oldColorPercent ) >= 0.001f ){ + double diff = fabs( colorPercent - oldColorPercent); + if ( diff >= 0.001f ){ m_state.setValue( mixKey, colorPercent ); colorChanged = true; } @@ -400,29 +410,38 @@ QString ColorState::_setDataTransform( const QString& transformString ){ return result; } -void ColorState::_setErrorMargin(){ - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); - m_errorMargin = 1.0/qPow(10,significantDigits); -} - -QString ColorState::_setGamma( double gamma ){ +QString ColorState::_setGamma( double gamma, double errorMargin, int significantDigits ){ QString result; double oldGamma = m_state.getValue( GAMMA ); - - if ( qAbs( gamma - oldGamma) > m_errorMargin ){ - int digits = m_state.getValue(SIGNIFICANT_DIGITS); - m_state.setValue(GAMMA, Util::roundToDigits(gamma, digits )); + double roundedGamma = Util::roundToDigits(gamma, significantDigits ); + if ( qAbs( roundedGamma - oldGamma) > errorMargin ){ + m_state.setValue(GAMMA, roundedGamma ); emit colorStateChanged(); } return result; } -void ColorState::_setGlobal( bool global ){ - bool oldGlobal = m_state.getValue(GLOBAL); - if ( global != oldGlobal ){ - m_state.setValue(GLOBAL, global); +bool ColorState::_setGammaX( double xValue, double errorMargin, int significantDigits ){ + bool changed = false; + double xValueOld = m_state.getValue( SCALE_1 ); + double xValueRounded = Util::roundToDigits( xValue, significantDigits ); + if ( qAbs( xValueRounded - xValueOld ) > errorMargin ){ + m_state.setValue(SCALE_1, xValueRounded); + changed = true; + } + return changed; +} + +bool ColorState::_setGammaY( double yValue, double errorMargin, int significantDigits ){ + bool changed = false; + double yValueOld = m_state.getValue( SCALE_2 ); + double yValueRounded = Util::roundToDigits( yValue, significantDigits ); + if ( qAbs( yValueRounded - yValueOld ) > errorMargin ){ + m_state.setValue(SCALE_2, yValueRounded); + changed = true; } + return changed; } void ColorState::_setInvert( bool invert ){ @@ -466,37 +485,6 @@ void ColorState::_setReverse( bool reverse ){ } -QString ColorState::_setSignificantDigits( int digits ){ - QString result; - if ( digits <= 0 ){ - result = "Invalid significant digits; must be positive: "+QString::number( digits ); - } - else { - if ( m_state.getValue(SIGNIFICANT_DIGITS) != digits ){ - m_state.setValue(SIGNIFICANT_DIGITS, digits ); - _setErrorMargin(); - emit colorStateChanged(); - } - } - return result; -} - -QString ColorState::_setTabIndex( int index ){ - QString result; - if ( index >= 0 ){ - int oldIndex = m_state.getValue( Util::TAB_INDEX ); - if ( index != oldIndex ){ - m_state.setValue( Util::TAB_INDEX, index ); - emit colorStateChanged(); - } - } - else { - result = "Colormap settings tab index must be nonnegative: "+ QString::number(index); - } - return result; -} - - ColorState::~ColorState(){ } diff --git a/carta/cpp/core/Data/Colormap/ColorState.h b/carta/cpp/core/Data/Colormap/ColorState.h index a5356341..d87e4f99 100644 --- a/carta/cpp/core/Data/Colormap/ColorState.h +++ b/carta/cpp/core/Data/Colormap/ColorState.h @@ -23,6 +23,8 @@ namespace Data { class Colormaps; class Controller; class TransformsData; +class TransformsImage; + class ColorState : public QObject, public Carta::State::CartaObject { @@ -82,7 +84,6 @@ class ColorState : public QObject, public Carta::State::CartaObject { void _initializeStatics(); bool _isBorderDefault() const; - bool _isGlobal() const; bool _isNanDefault() const; /** @@ -150,25 +151,14 @@ class ColorState : public QObject, public Carta::State::CartaObject { */ QString _setDataTransform( const QString& transformString); - - /** - * Set the error margin used to determine when two doubles are equal. - */ - void _setErrorMargin(); - /** * Set the gamma color map parameter. - * @param gamma a parameter for color mapping. + * @param gamma - a parameter for color mapping. + * @param errorMargin - the error margin required to recognize a difference in gamma values. + * @param significantDigits - the number of digits to retain for display purposes. * @return error information if gamma could not be set. */ - QString _setGamma( double gamma ); - - /** - * Set whether or not this is the global color state. - * @param global - true if it is the global color state; false, if it just applies - * to a single layer on the stack. - */ - void _setGlobal( bool global ); + QString _setGamma( double gamma, double errorMargin, int significantDigits ); /** * Invert the current colormap. @@ -182,6 +172,9 @@ class ColorState : public QObject, public Carta::State::CartaObject { bool _setColor( const QString& key, const QString& majorKey, const QString& userId, int colorAmount, QString& errorMsg ); + bool _setGammaX( double xValue, double errorMargin, int significantDigits ); + bool _setGammaY( double xValue, double errorMargin, int significantDigits ); + void _setNanDefault( bool defaultNan ); /** @@ -191,28 +184,10 @@ class ColorState : public QObject, public Carta::State::CartaObject { */ void _setReverse( bool reverse ); - /** - * Set the number of significant digits to use/display in colormap calculations. - * @param digits - the number of significant digits to use in calculations. - * @return an error message if the significant digits could not be sent; an - * empty string otherwise. - */ - QString _setSignificantDigits( int digits ); - - /** - * Set the index of the tab that should be selected. - * @param index - the index of the tab that should be selected. - * @return an error message if the tab index could not be set; an empty string - * otherwise. - */ - QString _setTabIndex( int index ); - - static bool m_registered; const static QString COLOR_MAP_NAME; const static QString REVERSE; const static QString INVERT; - const static QString GLOBAL; const static QString COLORED_OBJECT; const static QString COLOR_MIX; @@ -225,7 +200,7 @@ class ColorState : public QObject, public Carta::State::CartaObject { const static QString BORDER_DEFAULT; const static QString NAN_COLOR; const static QString NAN_DEFAULT; - const static QString SIGNIFICANT_DIGITS; + const static QString TRANSFORM_IMAGE; const static QString TRANSFORM_DATA; @@ -233,15 +208,12 @@ class ColorState : public QObject, public Carta::State::CartaObject { class Factory; - //Supported color maps static Colormaps* m_colors; //Supported data transforms static TransformsData* m_dataTransforms; - - - double m_errorMargin; + static TransformsImage* m_imageTransforms; ColorState( const ColorState& other); ColorState& operator=( const ColorState& other ); diff --git a/carta/cpp/core/Data/Colormap/Colormap.cpp b/carta/cpp/core/Data/Colormap/Colormap.cpp index 92b50979..397cf769 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.cpp +++ b/carta/cpp/core/Data/Colormap/Colormap.cpp @@ -1,13 +1,23 @@ #include "Colormap.h" #include "ColorState.h" #include "Data/Settings.h" +#include "Data/Colormap/Colormaps.h" +#include "Data/Colormap/Gamma.h" +#include "Data/Error/ErrorManager.h" #include "Data/Image/Controller.h" +#include "Data/Image/DataSource.h" #include "Data/Histogram/Histogram.h" +#include "Data/Units/UnitsIntensity.h" +#include "Data/Units/UnitsFrequency.h" #include "Data/Util.h" #include "State/StateInterface.h" #include "State/UtilState.h" #include "ImageView.h" -#include "CartaLib/PixelPipeline/IPixelPipeline.h" +#include "Globals.h" +#include "PluginManager.h" +#include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" +#include "CartaLib/Hooks/ConversionIntensityHook.h" +#include "CartaLib/Hooks/ConversionSpectralHook.h" #include #include @@ -18,8 +28,15 @@ namespace Carta { namespace Data { const QString Colormap::CLASS_NAME = "Colormap"; +const QString Colormap::COLOR_STOPS = "stops"; +const QString Colormap::GLOBAL = "global"; +const QString Colormap::IMAGE_UNITS = "imageUnits"; const QString Colormap::INTENSITY_MIN = "intensityMin"; const QString Colormap::INTENSITY_MAX = "intensityMax"; +const QString Colormap::INTENSITY_MIN_INDEX = "intensityMinIndex"; +const QString Colormap::INTENSITY_MAX_INDEX = "intensityMaxIndex"; +const QString Colormap::SIGNIFICANT_DIGITS = "significantDigits"; +const QString Colormap::TAB_INDEX = "tabIndex"; class Colormap::Factory : public Carta::State::CartaObjectFactory { @@ -31,6 +48,8 @@ class Colormap::Factory : public Carta::State::CartaObjectFactory { bool Colormap::m_registered = Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new Colormap::Factory()); +UnitsIntensity* Colormap::m_intensityUnits = nullptr; +Gamma* Colormap::m_gammaTransform = nullptr; Colormap::Colormap( const QString& path, const QString& id): CartaObject( CLASS_NAME, path, id ), @@ -43,13 +62,15 @@ Colormap::Colormap( const QString& path, const QString& id): m_settings.reset( prefObj ); ColorState* colorStateObj = objMan->createObject(); - m_stateColorGlobal.reset( colorStateObj ); connect( colorStateObj, SIGNAL( colorStateChanged()), this, SLOT( _colorStateChanged())); - m_stateColorGlobal->_initializeDefaultState( m_state ); - m_stateColors.push_back( m_stateColorGlobal); + colorStateObj->_initializeDefaultState( m_state ); + m_stateColors.push_back( std::shared_ptr(colorStateObj)); + _colorStateChanged(); + _initializeStatics(); _initializeDefaultState(); + _setErrorMargin(); _initializeCallbacks(); } @@ -60,9 +81,12 @@ QString Colormap::addLink( CartaObject* cartaObject ){ if ( target != nullptr ){ objAdded = m_linkImpl->addLink( target ); if ( objAdded ){ - target->_setColorMapGlobal( m_stateColorGlobal ); connect( target, SIGNAL(colorChanged(Controller*)), this, SLOT(_setColorStates(Controller*))); _setColorStates( target ); + connect( target, SIGNAL(clipsChanged(double,double)), this, SLOT(_updateIntensityBounds(double,double))); + connect( target, SIGNAL(dataChanged(Controller*)), this, SLOT( _dataChanged(Controller*))); + _dataChanged( target ); + } } else { @@ -72,7 +96,7 @@ QString Colormap::addLink( CartaObject* cartaObject ){ if ( objAdded ){ connect( this, SIGNAL(colorMapChanged()), hist, SLOT( updateColorMap())); hist->updateColorMap(); - connect( hist,SIGNAL(colorIntensityBoundsChanged(double,double)), this, SLOT(_updateIntensityBounds( double, double ))); + //connect( hist,SIGNAL(colorIntensityBoundsChanged(double,double)), this, SLOT(_updateIntensityBounds( double, double ))); } } else { @@ -90,9 +114,47 @@ void Colormap::clear(){ void Colormap::_colorStateChanged(){ if ( m_stateColors.size() > 0 ){ + emit colorMapChanged(); + + //Calculate new stops based on the pixel pipeline + _calculateColorStops(); + + //Copy the latest color information into this state so the + //view will update. m_stateColors[0]->_replicateTo( m_state ); m_state.flushState(); - emit colorMapChanged(); + } +} + +void Colormap::_calculateColorStops(){ + Controller* controller = _getControllerSelected(); + if ( controller ){ + std::shared_ptr dSource = controller->getDataSource(); + if ( dSource ){ + QString nameStr = m_state.getValue(ColorState::COLOR_MAP_NAME); + Colormaps* cMaps = Util::findSingletonObject(); + std::shared_ptr map = cMaps->getColorMap( nameStr ); + + std::shared_ptr pipe = dSource->_getPipeline(); + if ( pipe ){ + QStringList buff; + for ( int i = 0; i < 100; i++ ){ + float val = i / 100.0f; + Carta::Lib::PixelPipeline::NormRgb normRgb; + pipe->convert( val, normRgb ); + QColor mapColor; + if ( normRgb[0] >= 0 && normRgb[1] >= 0 && normRgb[2] >= 0 ){ + mapColor = QColor::fromRgbF( normRgb[0], normRgb[1], normRgb[2] ); + } + QString hexStr = mapColor.name(); + if ( i < 99 ){ + hexStr = hexStr + ","; + } + buff.append( hexStr ); + } + m_state.setValue( COLOR_STOPS, buff.join("") ); + } + } } } @@ -144,7 +206,6 @@ QString Colormap::_commandSetColorMix( const QString& params ){ bool validGreen = false; double greenValue = dataValues[Util::GREEN].toDouble(&validGreen); - if ( validRed && validBlue && validGreen ){ result = setColorMix( redValue, greenValue, blueValue ); } @@ -164,6 +225,119 @@ QString Colormap::_commandSetColorMap( const QString& params ){ return result; } +std::pair Colormap::_convertIntensity( const QString& oldUnit, const QString& newUnit ){ + double minValue = m_stateData.getValue( INTENSITY_MIN ); + double maxValue = m_stateData.getValue( INTENSITY_MAX ); + return _convertIntensity( oldUnit, newUnit, minValue, maxValue ); +} + +std::pair Colormap::_convertIntensity( const QString& oldUnit, const QString& newUnit, + double minValue, double maxValue ){ + std::vector converted(2); + converted[0] = minValue; + converted[1] = maxValue; + std::pair convertedIntensity(converted[0],converted[1]); + + std::vector valuesX(2); + valuesX[0] = m_stateData.getValue( INTENSITY_MIN_INDEX ); + valuesX[1] = m_stateData.getValue( INTENSITY_MAX_INDEX ); + + Controller* controller = _getControllerSelected(); + if ( controller ){ + std::shared_ptr dataSource = controller->getDataSource(); + if ( dataSource ){ + std::shared_ptr image = dataSource->_getImage(); + if ( image ){ + //First, we need to make sure the x-values are in Hertz. + std::vector hertzValues; + auto result = Globals::instance()-> pluginManager() + -> prepare (image, + "", UnitsFrequency::UNIT_HZ, valuesX ); + auto lam = [&hertzValues] ( const Carta::Lib::Hooks::ConversionSpectralHook::ResultType &data ) { + hertzValues = data; + }; + try { + result.forEach( lam ); + } + catch( char*& error ){ + QString errorStr( error ); + ErrorManager* hr = Util::findSingletonObject(); + hr->registerError( errorStr ); + } + + //Now we convert the intensity units + auto result2 = Globals::instance()-> pluginManager() + -> prepare (image, + oldUnit, newUnit, hertzValues, converted, + 1, "" );; + + auto lam2 = [&convertedIntensity] ( const Carta::Lib::Hooks::ConversionIntensityHook::ResultType &data ) { + if ( data.size() == 2 ){ + convertedIntensity.first = data[0]; + convertedIntensity.second = data[1]; + } + }; + try { + result2.forEach( lam2 ); + } + catch( char*& error ){ + QString errorStr( error ); + ErrorManager* hr = Util::findSingletonObject(); + hr->registerError( errorStr ); + } + } + } + } + return convertedIntensity; +} + + +void Colormap::_dataChanged( Controller* controller ){ + if ( controller ){ + double colorMinPercent = controller->getClipPercentileMin(); + double colorMaxPercent = controller->getClipPercentileMax(); + _updateIntensityBounds( colorMinPercent, colorMaxPercent ); + } +} + + + +Controller* Colormap::_getControllerSelected() const { + //We are only supporting one linked controller. + Controller* controller = nullptr; + int linkCount = m_linkImpl->getLinkCount(); + for ( int i = 0; i < linkCount; i++ ){ + CartaObject* obj = m_linkImpl->getLink(i ); + Controller* control = dynamic_cast( obj); + if ( control != nullptr){ + controller = control; + break; + } + } + return controller; +} + +QString Colormap::getImageUnits() const { + return m_state.getValue( IMAGE_UNITS ); +} + +std::pair Colormap::_getIntensityForPercent( double percent, bool* valid ) const { + *valid = false; + std::pair value(0, percent); + Controller* controller = _getControllerSelected(); + if ( controller != nullptr ){ + std::pair bounds(-1,-1); + int index = 0; + double intValue = 0; + bool validIntensity = controller->getIntensity( -1, -1, percent, &intValue, &index ); + if ( validIntensity ){ + value= std::pair( index, intValue ); + *valid = true; + } + } + return value; +} + QList Colormap::getLinks() const { return m_linkImpl->getLinkIds(); } @@ -172,6 +346,10 @@ QString Colormap::_getPreferencesId() const { return m_settings->getPath(); } +int Colormap::getSignificantDigits() const { + return m_state.getValue( SIGNIFICANT_DIGITS ); +} + QString Colormap::getStateString( const QString& sessionId, SnapshotType type ) const{ QString result(""); if ( type == SNAPSHOT_PREFERENCES ){ @@ -188,11 +366,24 @@ QString Colormap::getStateString( const QString& sessionId, SnapshotType type ) } +int Colormap::getTabIndex() const { + return m_state.getValue( Util::TAB_INDEX ); +} + void Colormap::_initializeDefaultState(){ + m_state.insertValue( GLOBAL, true ); + m_state.insertValue( COLOR_STOPS, ""); + m_state.insertValue(SIGNIFICANT_DIGITS, 6 ); + m_state.insertValue(TAB_INDEX, 0 ); + m_state.insertValue( IMAGE_UNITS, m_intensityUnits->getDefault() ); + m_state.flushState(); + //Image dependent intensity bounds m_stateData.insertValue( INTENSITY_MIN, 0 ); m_stateData.insertValue( INTENSITY_MAX, 1 ); + m_stateData.insertValue( INTENSITY_MIN_INDEX, 0 ); + m_stateData.insertValue( INTENSITY_MAX_INDEX, 0 ); m_stateData.flushState(); //Mouse @@ -331,28 +522,17 @@ void Colormap::_initializeCallbacks(){ result = "Invalid color scale: "+params; } else { - double oldScale1 = m_state.getValue(ColorState::SCALE_1); - double oldScale2 = m_state.getValue(ColorState::SCALE_2); - bool changedState = false; - if ( scale1 != oldScale1 ){ - m_state.setValue(ColorState::SCALE_1, scale1 ); - changedState = true; - } - if ( scale2 != oldScale2 ){ - m_state.setValue(ColorState::SCALE_2, scale2 ); - changedState = true; - } - if ( changedState ){ - m_state.flushState(); - //Calculate the new gamma - double maxndig = 10; - double ndig = (scale2 + 1) / 2 * maxndig; - double expo = std::pow( 2.0, ndig); - double xx = std::pow(scale1 * 2, 3) / 8.0; - double gamma = fabs(xx) * expo + 1; - if( scale1 < 0){ - gamma = 1 / gamma; + bool scaleChanged = false; + int stateColorCount = m_stateColors.size(); + for ( int i = 0; i < stateColorCount; i++ ){ + bool scaleChanged1 = m_stateColors[i]->_setGammaX( scale1, m_errorMargin, getSignificantDigits() ); + bool scaleChanged2 = m_stateColors[i]->_setGammaY( scale2, m_errorMargin, getSignificantDigits() ); + if ( scaleChanged1 || scaleChanged2 ){ + scaleChanged = true; } + } + if ( scaleChanged ){ + double gamma = m_gammaTransform->getGamma( scale1, scale2 ); result = setGamma( gamma ); } } @@ -360,12 +540,36 @@ void Colormap::_initializeCallbacks(){ return result; }); + addCommandCallback( "setGamma", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + QString result; + std::set keys = {ColorState::GAMMA}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString gammaStr = dataValues[ColorState::GAMMA]; + bool validDigits = false; + double gamma = gammaStr.toDouble( &validDigits ); + if ( validDigits ){ + std::pair position = m_gammaTransform->find( gamma ); + int stateCount = m_stateColors.size(); + for ( int i = 0; i < stateCount; i++ ){ + m_stateColors[i]->_setGammaX( position.first, m_errorMargin, getSignificantDigits() ); + m_stateColors[i]->_setGammaY( position.second, m_errorMargin, getSignificantDigits() ); + } + result = setGamma( gamma ); + } + else { + result = "Colormap gamma must be a number: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + addCommandCallback( "setSignificantDigits", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result; - std::set keys = {ColorState::SIGNIFICANT_DIGITS}; + std::set keys = {SIGNIFICANT_DIGITS}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString digitsStr = dataValues[ColorState::SIGNIFICANT_DIGITS]; + QString digitsStr = dataValues[SIGNIFICANT_DIGITS]; bool validDigits = false; int digits = digitsStr.toInt( &validDigits ); if ( validDigits ){ @@ -416,7 +620,7 @@ void Colormap::_initializeCallbacks(){ addCommandCallback( "setGlobal", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {ColorState::GLOBAL}; + std::set keys = {GLOBAL}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); QString globalStr = dataValues[*keys.begin()]; bool validBool = false; @@ -432,6 +636,37 @@ void Colormap::_initializeCallbacks(){ return result; }); + addCommandCallback( "setImageUnits", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {IMAGE_UNITS}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString unitsStr = dataValues[*keys.begin()]; + QString result = setImageUnits( unitsStr ); + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setIntensityRange", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {INTENSITY_MIN, INTENSITY_MAX}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString intMinStr = dataValues[INTENSITY_MIN]; + QString intMaxStr = dataValues[INTENSITY_MAX]; + bool validMin = false; + bool validMax = false; + double intMin = intMinStr.toDouble( &validMin ); + double intMax = intMaxStr.toDouble( &validMax ); + QString result; + if ( validMin && validMax ){ + result = setIntensityRange( intMin, intMax ); + } + else { + result = "Color map intensity bounds must be numbers: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + addCommandCallback( "setTabIndex", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result; @@ -451,6 +686,17 @@ void Colormap::_initializeCallbacks(){ }); } +void Colormap::_initializeStatics(){ + //Intensity units + if ( m_intensityUnits == nullptr ){ + m_intensityUnits = Util::findSingletonObject(); + } + //Gamma transform + if ( m_gammaTransform == nullptr ){ + m_gammaTransform = Util::findSingletonObject(); + } +} + bool Colormap::isBorderDefault() const { bool borderDefault = false; if ( m_stateColors.size() > 0 ){ @@ -459,12 +705,8 @@ bool Colormap::isBorderDefault() const { return borderDefault; } -bool Colormap::_isGlobal() const { - bool global = true; - if ( m_stateColors.size() > 0 ){ - global = m_stateColors[0]->_isGlobal(); - } - return global; +bool Colormap::isGlobal() const { + return m_state.getValue( GLOBAL ); } bool Colormap::isInverted() const { @@ -475,14 +717,6 @@ bool Colormap::isInverted() const { return inverted; } -bool Colormap::isNanDefault() const { - bool nanDefault = false; - if ( m_stateColors.size() > 0 ){ - nanDefault = m_stateColors[0]->_isNanDefault(); - } - return nanDefault; -} - bool Colormap::isLinked( const QString& linkId ) const { bool linked = false; CartaObject* obj = m_linkImpl->searchLinks( linkId ); @@ -492,6 +726,14 @@ bool Colormap::isLinked( const QString& linkId ) const { return linked; } +bool Colormap::isNanDefault() const { + bool nanDefault = false; + if ( m_stateColors.size() > 0 ){ + nanDefault = m_stateColors[0]->_isNanDefault(); + } + return nanDefault; +} + bool Colormap::isReversed() const { bool reversed = false; @@ -508,20 +750,6 @@ void Colormap::refreshState(){ m_linkImpl->refreshState(); } -void Colormap::resetState( const QString& state ){ - Carta::State::StateInterface restoredState( ""); - restoredState.setState( state ); - - QString settingStr = restoredState.getValue(Settings::SETTINGS); - m_settings->resetStateString( settingStr ); - - QString prefStr = restoredState.getValue(Util::PREFERENCES); - m_state.setState( prefStr ); - m_state.flushState(); -} - - - QString Colormap::removeLink( CartaObject* cartaObject ){ Controller* controller = dynamic_cast(cartaObject); bool objRemoved = false; @@ -547,6 +775,22 @@ QString Colormap::removeLink( CartaObject* cartaObject ){ return result; } + +void Colormap::resetState( const QString& state ){ + Carta::State::StateInterface restoredState( ""); + restoredState.setState( state ); + + QString settingStr = restoredState.getValue(Settings::SETTINGS); + m_settings->resetStateString( settingStr ); + + QString prefStr = restoredState.getValue(Util::PREFERENCES); + m_state.setState( prefStr ); + m_state.flushState(); +} + + + + QString Colormap::setBorderAlpha( int alphaValue ){ int stateColorCount = m_stateColors.size(); QString result; @@ -578,9 +822,11 @@ QString Colormap::setBorderColor( int redValue, int greenValue, int blueValue){ break; } } - if ( result.isEmpty() ){ - _colorStateChanged(); - } + + if ( result.isEmpty() ){ + _colorStateChanged(); + } + return result; } @@ -596,6 +842,7 @@ QString Colormap::setBorderDefault( bool borderDefault ) { else { _colorStateChanged(); } + return result; } @@ -630,27 +877,30 @@ QString Colormap::setColorMix( double redValue, double greenValue, double blueVa } -QString Colormap::setInvert( bool invert ){ - int stateColorCount = m_stateColors.size(); - QString result; - for ( int i = 0; i < stateColorCount; i++ ){ - m_stateColors[i]->_setInvert( invert ); - } - if ( stateColorCount == 0 ){ - result = "There were no color maps to invert."; - } - else { - _colorStateChanged(); +void Colormap::_setColorStates( Controller* controller ){ + if ( controller ){ + bool global = m_state.getValue( GLOBAL ); + std::vector< std::shared_ptr > selectedColorStates = controller->getSelectedColorStates( global); + int stateColorCount = selectedColorStates.size(); + if ( stateColorCount > 0 ){ + m_stateColors.clear(); + + for ( int i = 0; i < stateColorCount; i++ ){ + m_stateColors.push_back( selectedColorStates[i] ); + } + + //Update the state the client is listening to. + _colorStateChanged(); + } } - return result; } -QString Colormap::setGamma( double gamma ){ +QString Colormap::setDataTransform( const QString& transformString){ int stateColorCount = m_stateColors.size(); QString result; for ( int i = 0; i < stateColorCount; i++ ){ - result = m_stateColors[i]->_setGamma( gamma ); - if ( !result.isEmpty()){ + result = m_stateColors[i]->_setDataTransform( transformString ); + if ( !result.isEmpty() ){ break; } } @@ -660,12 +910,19 @@ QString Colormap::setGamma( double gamma ){ return result; } -QString Colormap::setDataTransform( const QString& transformString){ +void Colormap::_setErrorMargin( ){ + int significantDigits = getSignificantDigits(); + m_errorMargin = 1.0/qPow(10,significantDigits); +} + + +QString Colormap::setGamma( double gamma ){ int stateColorCount = m_stateColors.size(); QString result; + int digits = getSignificantDigits(); for ( int i = 0; i < stateColorCount; i++ ){ - result = m_stateColors[i]->_setDataTransform( transformString ); - if ( !result.isEmpty() ){ + result = m_stateColors[i]->_setGamma( gamma, m_errorMargin, digits ); + if ( !result.isEmpty()){ break; } } @@ -675,35 +932,94 @@ QString Colormap::setDataTransform( const QString& transformString){ return result; } +void Colormap::setGlobal( bool global ){ + //Update the color maps based on whether we should make a change + //to all of them or only the selected ones. + bool oldGlobal = isGlobal(); + if ( global != oldGlobal ){ + m_state.setValue(GLOBAL, global ); + + //Update the list of color states based on the global + //flag. + Controller* controller = _getControllerSelected(); + _setColorStates( controller ); + + _colorStateChanged(); + } +} + + + +QString Colormap::setIntensityRange( double minValue, double maxValue ){ + QString result; + if ( minValue < maxValue ){ + double minRounded = Util::roundToDigits( minValue, getSignificantDigits() ); + double maxRounded = Util::roundToDigits( maxValue, getSignificantDigits() ); + double oldMin = m_stateData.getValue( INTENSITY_MIN ); + double oldMax = m_stateData.getValue( INTENSITY_MAX ); + if ( qAbs( minRounded - oldMin) > m_errorMargin || + qAbs( maxRounded - oldMax) > m_errorMargin ){ + //Store the values. + m_stateData.setValue( INTENSITY_MIN, minRounded ); + m_stateData.setValue( INTENSITY_MAX, maxRounded ); + m_stateData.flushState(); + _updateImageClips(); + _colorStateChanged(); -void Colormap::_setColorStates( Controller* controller ){ - std::vector< std::shared_ptr > selectedColorStates = controller->getSelectedColorStates(); - int stateColorCount = selectedColorStates.size(); - if ( stateColorCount > 0 ){ - m_stateColors.clear(); - for ( int i = 0; i < stateColorCount; i++ ){ - m_stateColors.push_back( selectedColorStates[i] ); } + } + else { + result = "The minimum intensity bound must be less than the maximum"; + } + return result; +} - //Update the state the client is listening to. +QString Colormap::setInvert( bool invert ){ + int stateColorCount = m_stateColors.size(); + QString result; + for ( int i = 0; i < stateColorCount; i++ ){ + m_stateColors[i]->_setInvert( invert ); + } + if ( stateColorCount == 0 ){ + result = "There were no color maps to invert."; + } + else { _colorStateChanged(); } + return result; } -void Colormap::setGlobal( bool global ){ - //Notify all the controllers to replace their global color maps with - //individual ones or vice versa. - int linkCount = m_linkImpl->getLinkCount(); - for ( int i = 0; i < linkCount; i++ ){ - CartaObject* obj = m_linkImpl->getLink( i ); - Controller* controller = dynamic_cast(obj); - if ( controller != nullptr ){ - controller->_setColorMapUseGlobal( global ); + +QString Colormap::setImageUnits( const QString& unitsStr ){ + QString result; + QString actualUnits = m_intensityUnits->getActualUnits( unitsStr); + if ( !actualUnits.isEmpty() ){ + QString oldUnits = m_state.getValue( IMAGE_UNITS ); + if ( oldUnits != actualUnits ){ + + //Convert intensity values + std::pair values = _convertIntensity( oldUnits, actualUnits ); + + //Set the units + m_state.setValue( IMAGE_UNITS, actualUnits ); + m_state.flushState(); + + //Set the converted values + double intMin = Util::roundToDigits( values.first, getSignificantDigits() ); + m_stateData.setValue( INTENSITY_MIN, intMin ); + double intMax = Util::roundToDigits( values.second, getSignificantDigits() ); + m_stateData.setValue( INTENSITY_MAX, intMax ); + m_stateData.flushState(); } } + else { + result = "Unrecognized units: "+unitsStr; + } + return result; } + QString Colormap::setNanColor( int redValue, int greenValue, int blueValue){ int stateColorCount = m_stateColors.size(); QString result; @@ -734,6 +1050,7 @@ QString Colormap::setNanDefault( bool nanDefault ) { else { _colorStateChanged(); } + return result; } @@ -749,20 +1066,21 @@ QString Colormap::setReverse( bool reverse ){ else { _colorStateChanged(); } + return result; } QString Colormap::setSignificantDigits( int digits ){ - int stateColorCount = m_stateColors.size(); QString result; - for ( int i = 0; i < stateColorCount; i++ ){ - result = m_stateColors[i]->_setSignificantDigits( digits ); - if ( !result.isEmpty() ){ - break; - } + if ( digits <= 0 ){ + result = "Invalid colormap significant digits; must be positive: "+QString::number( digits ); } - if ( result.isEmpty() ){ - _colorStateChanged(); + else { + if ( getSignificantDigits() != digits ){ + m_state.setValue(SIGNIFICANT_DIGITS, digits ); + _setErrorMargin(); + //emit colorStateChanged(); + } } return result; } @@ -770,15 +1088,10 @@ QString Colormap::setSignificantDigits( int digits ){ QString Colormap::setTabIndex( int index ){ QString result; if ( index >= 0 ){ - int stateColorCount = m_stateColors.size(); - for ( int i = 0; i < stateColorCount; i++ ){ - result = m_stateColors[i]->_setTabIndex( index ); - if ( !result.isEmpty() ){ - break; - } - } - if ( result.isEmpty()){ - _colorStateChanged(); + int oldTabIndex = getTabIndex(); + if ( oldTabIndex != index ){ + m_state.setValue( Util::TAB_INDEX, index ); + m_state.flushState(); } } else { @@ -787,21 +1100,70 @@ QString Colormap::setTabIndex( int index ){ return result; } -void Colormap::_updateIntensityBounds( double minIntensity, double maxIntensity ){ - double oldMinIntensity = m_stateData.getValue( INTENSITY_MIN ); - bool intensityChanged = false; - if ( oldMinIntensity != minIntensity ){ - intensityChanged = true; - m_stateData.setValue( INTENSITY_MIN, minIntensity ); - } +void Colormap::_updateImageClips(){ + double minClip = m_stateData.getValue( INTENSITY_MIN ); + double maxClip = m_stateData.getValue( INTENSITY_MAX ); + + //Change intensity values back to image units. + Controller* controller = _getControllerSelected(); + if ( controller ){ + QString imageUnits = controller->getPixelUnits(); + QString curUnits = getImageUnits(); + if ( imageUnits != curUnits ){ + //Convert intensity values + std::pair values = _convertIntensity( curUnits, imageUnits ); + minClip = values.first; + maxClip = values.second; + } - double oldMaxIntensity = m_stateData.getValue( INTENSITY_MAX ); - if ( oldMaxIntensity != maxIntensity ){ - intensityChanged = true; - m_stateData.setValue( INTENSITY_MAX, maxIntensity ); + double minClipPercentile = controller->getPercentile( -1, -1, minClip ); + double maxClipPercentile = controller->getPercentile( -1, -1, maxClip ); + controller->applyClips( minClipPercentile, maxClipPercentile ); } - if ( intensityChanged ){ - m_stateData.flushState(); +} + +void Colormap::_updateIntensityBounds( double minPercent, double maxPercent ){ + bool validMin = false; + bool validMax = false; + std::pair minValue = _getIntensityForPercent( minPercent, &validMin ); + std::pair maxValue = _getIntensityForPercent( maxPercent, &validMax ); + if ( validMin && validMax ){ + + double minInt = minValue.second; + double maxInt = maxValue.second; + + //Convert the units if we need to. + Controller* controller = _getControllerSelected(); + if ( controller ){ + QString imageUnits = controller->getPixelUnits(); + QString curUnits = getImageUnits(); + if ( imageUnits != curUnits ){ + std::pair values = + _convertIntensity( imageUnits, curUnits, minInt, maxInt ); + minInt = values.first; + maxInt = values.second; + } + + double minIntensity = Util::roundToDigits( minInt, getSignificantDigits()); + double maxIntensity = Util::roundToDigits( maxInt, getSignificantDigits()); + double oldMinIntensity = m_stateData.getValue( INTENSITY_MIN ); + bool intensityChanged = false; + if ( qAbs( oldMinIntensity - minIntensity ) > m_errorMargin ){ + intensityChanged = true; + m_stateData.setValue( INTENSITY_MIN, minIntensity ); + m_stateData.setValue(INTENSITY_MIN_INDEX, minValue.first ); + } + + double oldMaxIntensity = m_stateData.getValue( INTENSITY_MAX ); + if ( qAbs( oldMaxIntensity - maxIntensity ) > m_errorMargin ){ + intensityChanged = true; + m_stateData.setValue( INTENSITY_MAX, maxIntensity ); + m_stateData.setValue( INTENSITY_MAX_INDEX, maxValue.first ); + } + if ( intensityChanged ){ + m_stateData.flushState(); + } + } } } diff --git a/carta/cpp/core/Data/Colormap/Colormap.h b/carta/cpp/core/Data/Colormap/Colormap.h index 90da49f8..d8edd32e 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.h +++ b/carta/cpp/core/Data/Colormap/Colormap.h @@ -26,7 +26,9 @@ namespace Data { class ColorState; class Controller; +class Gamma; class Settings; +class UnitsIntensity; class Colormap : public QObject, public Carta::State::CartaObject, public ILinkable { @@ -44,8 +46,23 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ void clear(); + /** + * Return the units used to set the intensity range on the colormap. + * @return - units used to set the colormap intensity range. + */ + QString getImageUnits() const; + + /** + * Return the number of significant digits to use in the display. + * @return - the number of significant digits to retain. + */ + int getSignificantDigits() const; - std::shared_ptr getColorMap( ) const; + /** + * Return the current settings tab index. + * @return - the current settings tab index. + */ + int getTabIndex() const; /** * Return a string representing the colormap state of a particular type. @@ -55,6 +72,13 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; + /** + * Returns true if changes to the color map should apply to all color maps; + * false if they only apply to the current color map. + * @return - true if changes to the color map apply globally; false if they apply + * to only the current map. + */ + bool isGlobal() const; /** * Returns whether or not the colormap is inverted. @@ -160,6 +184,21 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ void setGlobal( bool global ); + /** + * Set the units to use for intensity bounds. + * @param unitsStr - an identifier for intensity units. + * @return - an error message if the intensity units could not be set; otherwise, + * an empty string. + */ + QString setImageUnits( const QString& unitsStr ); + + /** + * Set the colormap intensity range. + * @param minValue - the minimum intensity. + * @param maxValue - the maximum intensity. + */ + QString setIntensityRange( double minValue, double maxValue ); + /** * Invert the current colormap. * @param invert - true if the color map should be inverted; false otherwise.. @@ -225,17 +264,25 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka private slots: void _updateIntensityBounds( double minIntensity, double maxIntensity ); void _colorStateChanged(); + void _dataChanged( Controller* controller ); /** * Set the color states managed by this color map. */ void _setColorStates( Controller* target ); private: + void _calculateColorStops(); QString _commandSetColorMap( const QString& params ); QString _commandInvertColorMap( const QString& params ); QString _commandReverseColorMap( const QString& params ); QString _commandSetColorMix( const QString& params ); + std::pair _convertIntensity( const QString& oldUnit, const QString& newUnit ); + std::pair _convertIntensity( const QString& oldUnit, const QString& newUnit, + double minValue, double maxValue ); + + Controller* _getControllerSelected() const; + std::pair _getIntensityForPercent( double percent, bool* valid ) const; /** * Return the server side id of the preferences for this colormap. * @return the server side id of this colormap's preferences. @@ -244,34 +291,50 @@ private slots: void _initializeDefaultState(); void _initializeCallbacks(); - bool _isGlobal() const; + void _initializeStatics(); + + void _setErrorMargin( ); + void _updateImageClips(); static bool m_registered; + const static QString COLOR_STOPS; + const static QString GLOBAL; + const static QString IMAGE_UNITS; const static QString INTENSITY_MIN; const static QString INTENSITY_MAX; + const static QString INTENSITY_MIN_INDEX; + const static QString INTENSITY_MAX_INDEX; + const static QString SIGNIFICANT_DIGITS; + const static QString TAB_INDEX; Colormap( const QString& path, const QString& id ); class Factory; + double m_errorMargin; + //Link management std::unique_ptr m_linkImpl; std::unique_ptr m_settings; + //Image units + static UnitsIntensity* m_intensityUnits; + //Gamma + static Gamma* m_gammaTransform; + Carta::State::StateInterface m_stateData; //Separate state for mouse events since they get updated rapidly and not //everyone wants to listen to them. Carta::State::StateInterface m_stateMouse; - //Holds the current color state + //Holds the current color state. Note that it is a vector because + //multiple images can be selected for applying a color state change. std::vector< std::shared_ptr > m_stateColors; - //Holds the global color state; - std::shared_ptr m_stateColorGlobal; Colormap( const Colormap& other); Colormap& operator=( const Colormap& other ); diff --git a/carta/cpp/core/Data/Colormap/Colormaps.cpp b/carta/cpp/core/Data/Colormap/Colormaps.cpp index a59abeb5..0ad851df 100644 --- a/carta/cpp/core/Data/Colormap/Colormaps.cpp +++ b/carta/cpp/core/Data/Colormap/Colormaps.cpp @@ -42,37 +42,10 @@ bool Colormaps::m_registered = Colormaps::Colormaps( const QString& path, const QString& id): CartaObject( CLASS_NAME, path, id ){ _initializeDefaultState(); - _initializeCallbacks(); } -QString Colormaps::_commandGetColorStops( const QString& params ){ - QString result; - std::set keys = {Util::NAME}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString nameStr = dataValues[*keys.begin()]; - std::shared_ptr map = getColorMap( nameStr ); - if ( map != nullptr ){ - QStringList buff; - for ( int i = 0; i < 100; i++ ){ - float val = i / 100.0f; - Carta::Lib::PixelPipeline::NormRgb normRgb; - map->convert( val, normRgb ); - QColor mapColor = QColor::fromRgbF( normRgb[0], normRgb[1], normRgb[2]); - QString hexStr = mapColor.name(); - if ( i < 99 ){ - hexStr = hexStr + ","; - } - buff.append( hexStr ); - } - result = buff.join( ""); - } - else { - result = "Invalid color map: "+ params; - qWarning() << result; - } - return result; -} + QStringList Colormaps::getColorMaps() const { QStringList buff; @@ -119,13 +92,6 @@ std::shared_ptr Colormaps::getColorM return map; } -void Colormaps::_initializeCallbacks(){ - addCommandCallback( "getColorStops", [=] (const QString & /*cmd*/, - const QString & params, const QString & /*sessionId*/) -> QString { - QString result = _commandGetColorStops( params ); - return result; - }); -} bool Colormaps::isMap( const QString& name ) const { int colorMapCount = m_colormaps.size(); diff --git a/carta/cpp/core/Data/Colormap/Colormaps.h b/carta/cpp/core/Data/Colormap/Colormaps.h index 42effad2..93aaad5e 100644 --- a/carta/cpp/core/Data/Colormap/Colormaps.h +++ b/carta/cpp/core/Data/Colormap/Colormaps.h @@ -52,8 +52,6 @@ class Colormaps : public Carta::State::CartaObject { private: void _initializeDefaultState(); - void _initializeCallbacks(); - QString _commandGetColorStops( const QString& params ); std::vector < std::shared_ptr > m_colormaps; diff --git a/carta/cpp/core/Data/Colormap/Gamma.cpp b/carta/cpp/core/Data/Colormap/Gamma.cpp new file mode 100644 index 00000000..2f1655f7 --- /dev/null +++ b/carta/cpp/core/Data/Colormap/Gamma.cpp @@ -0,0 +1,187 @@ +#include "Gamma.h" +#include "Data/Util.h" +#include "State/UtilState.h" + +#include +#include + +namespace Carta { + +namespace Data { + +const QString Gamma::CLASS_NAME = "Gamma"; +const QString Gamma::LEVEL_CURVES = "levelCurves"; + +const int Gamma::MAX_N_DIG = 10; +const int Gamma::LOWER_BOUND = -1; +const int Gamma::UPPER_BOUND = 1; +const int Gamma::TABLE_SIZE = 20; + +class Gamma::Factory : public Carta::State::CartaObjectFactory { + + public: + + Factory(): + CartaObjectFactory( CLASS_NAME ){}; + + Carta::State::CartaObject * create (const QString & path, const QString & id) + { + return new Gamma (path, id); + } + }; + + +bool Gamma::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new Gamma::Factory()); + + +Gamma::Gamma( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + + _initializeDefaultState(); +} + +std::pair Gamma::find( double gamma ) const { + //Look at positive x values only + std::pair result( 0, 0 ); + double target = gamma; + if ( gamma < 1 ){ + target = 1/gamma; + } + if ( gamma != 1 ){ + //Binary search on main diagonal + double midX = 0; + double midY = 0; + double minX = 0; + double maxX = 1; + double minY = 0; + double maxY = 1; + double maxGamma = getGamma( maxX, maxY); + if ( target > maxGamma ){ + midX = maxX; + midY = maxY; + } + else { + double diff = fabs( 1 - target ); + const double ERROR = 0.001; + while( diff > ERROR ){ + midX = (minX + maxX)/2; + midY = (minY + maxY)/2; + double midGamma = getGamma( midX, midY ); + + if ( midGamma < target ){ + minX = midX; + minY = midY; + } + else { + maxX = midX; + maxY = midY; + } + + diff = fabs( midGamma - target ); + } + if ( gamma < 1 ){ + midX = midX * -1; + } + } + result = std::pair( midX, midY ); + } + return result; +} + + +double Gamma::getGamma( double x, double y ) const{ + double ndig = ( y + 1) / 2 * MAX_N_DIG; + double expo = std::pow( 2.0, ndig); + double xx = std::pow(x, 3); + double gamma = fabs(xx) * expo + 1; + if( x < 0){ + gamma = 1 / gamma; + } + return gamma; +} + +double Gamma::getY( double x, double gamma ) const { + double val = (gamma - 1) / std::pow(x, 3 ); + double valY = 1; + if ( val > 0 ){ + valY = log( val ) / log( 2 ) / 5 - 1; + } + return valY; +} + +double Gamma::getX( double y, double gamma ) const { + double val = (gamma - 1) / std::pow( 2, 5*(y+1)); + val = std::pow( val, 0.33333 ); + return val; +} + +void Gamma::_initializeDefaultState(){ + const int GAMMA_COUNT = 11; + double gammas[GAMMA_COUNT] = {1.1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; + m_state.insertArray( LEVEL_CURVES, GAMMA_COUNT * 2 ); + for ( int i = 0; i < GAMMA_COUNT; i++ ){ + int power = -10; + int base = 10; + double step = std::pow( base, power ); + const int POINT_COUNT = 10; + std::pair points[POINT_COUNT]; + int plotCount = 0; + int plotStartIndex = -1; + for ( int j = 1; j <= POINT_COUNT; j++ ){ + double valX = step+j*.1; + if ( valX > 1 ){ + valX = 1; + } + double valY = getY( valX, gammas[i]); + if ( j == 1 ){ + valY = 1; + valX = getX( valY, gammas[i] ); + } + points[j-1]=std::pair( valX, valY ); + if ( valY <= 1 && valY >= -1 && valX<=1 && valX>= -1 ){ + plotCount++; + if ( plotStartIndex < 0 ){ + plotStartIndex = j - 1; + } + } + } + + QString curveLookup = Carta::State::UtilState::getLookup( LEVEL_CURVES, 2*i); + QString curveLookup2 = Carta::State::UtilState::getLookup( LEVEL_CURVES, 2*i+1 ); + if ( plotCount > 0 ){ + m_state.setArray( curveLookup, plotCount ); + m_state.setArray( curveLookup2, plotCount ); + int pointIndex = 0; + for ( int j = plotStartIndex; j < POINT_COUNT; j++ ){ + if ( points[j].second <= 1 && points[j].second >=-1 && + points[j].first<= 1 && points[j].first >= -1){ + + //Positive x + QString pLookup = Carta::State::UtilState::getLookup( curveLookup, pointIndex ); + QString xValKey = Carta::State::UtilState::getLookup( pLookup, Util::XCOORD); + m_state.insertValue( xValKey, points[j].first ); + QString yValKey = Carta::State::UtilState::getLookup( pLookup, Util::YCOORD); + m_state.insertValue( yValKey, points[j].second ); + + //Negative x + QString pLookup2 = Carta::State::UtilState::getLookup( curveLookup2, pointIndex ); + xValKey = Carta::State::UtilState::getLookup( pLookup2, Util::XCOORD ); + m_state.insertValue( xValKey, -1 * points[j].first ); + yValKey = Carta::State::UtilState::getLookup( pLookup2, Util::YCOORD ); + m_state.insertValue( yValKey, points[j].second ); + + pointIndex++; + } + } + } + } + m_state.flushState(); +} + + +Gamma::~Gamma(){ + +} +} +} diff --git a/carta/cpp/core/Data/Colormap/Gamma.h b/carta/cpp/core/Data/Colormap/Gamma.h new file mode 100644 index 00000000..b35652df --- /dev/null +++ b/carta/cpp/core/Data/Colormap/Gamma.h @@ -0,0 +1,77 @@ +/*** + * Generates value for the gamma image transform. + */ + +#pragma once + +#include "State/ObjectManager.h" +#include "State/StateInterface.h" + +namespace Carta { + +namespace Data { + +class Gamma : public Carta::State::CartaObject { + +public: + + /** + * For a given value of gamma find a reasonable approximate to the (x,y) + * point in [-1,1]x[-1,1] that maps to it. + * @param gamma - a gamma value. + * @return - an (x,y) point that maps to it. + */ + std::pair find( double gamma ) const; + + /** + * Return a gamma value for an (x,y) coordinate. + * @param x - an x-coordinate. + * @param y - a y-coordinate. + * @return - the gamma value that is generated by (x,y). + */ + double getGamma(double x, double y) const; + + /** + * Return an x-value corresponding to the specified y- and + * gamma values. + * @param y - a y-coordinatee. + * @param gamma - a gamma value. + * @return - an x-coordinate corresponding to the values. + */ + double getX( double y, double gamma ) const; + + + /** + * Return a y-value corresponding to the specified x- and + * gamma values. + * @param x - an x-coordinatee. + * @param gamma - a gamma value. + * @return - a y-coordinate corresponding to the values. + */ + double getY( double x, double gamma ) const; + + virtual ~Gamma(); + + const static QString CLASS_NAME; + +private: + + void _initializeDefaultState(); + + static bool m_registered; + + Gamma( const QString& path, const QString& id ); + + class Factory; + + const static int MAX_N_DIG; + const static int LOWER_BOUND; + const static int UPPER_BOUND; + const static int TABLE_SIZE; + const static QString LEVEL_CURVES; + Gamma( const Gamma& other); + Gamma& operator=( const Gamma & other ); +}; + +} +} diff --git a/carta/cpp/core/Data/Colormap/TransformsData.cpp b/carta/cpp/core/Data/Colormap/TransformsData.cpp index bcbc0fc0..b8aa8da6 100644 --- a/carta/cpp/core/Data/Colormap/TransformsData.cpp +++ b/carta/cpp/core/Data/Colormap/TransformsData.cpp @@ -2,9 +2,7 @@ #include "Data/Util.h" #include "State/UtilState.h" - #include -#include namespace Carta { @@ -12,7 +10,6 @@ namespace Data { const QString TransformsData::DATA_TRANSFORMS = "dataTransforms"; const QString TransformsData::CLASS_NAME = "TransformsData"; -const QString TransformsData::TRANSFORM_COUNT = "dataTransformCount"; const QString TransformsData::TRANSFORM_NONE = "None"; const QString TransformsData::TRANSFORM_ROOT = "Square Root"; const QString TransformsData::TRANSFORM_SQUARE = "Square"; @@ -41,7 +38,10 @@ TransformsData::TransformsData( const QString& path, const QString& id): CartaObject( CLASS_NAME, path, id ){ _initializeDefaultState(); - _initializeCallbacks(); +} + +QString TransformsData::getDefault() const { + return TRANSFORM_NONE; } @@ -65,7 +65,6 @@ void TransformsData::_initializeDefaultState(){ m_transforms.push_back(TRANSFORM_POLY ); int transformCount = m_transforms.size(); - m_state.insertValue( TRANSFORM_COUNT, transformCount ); m_state.insertArray( DATA_TRANSFORMS, transformCount ); for ( int i = 0; i < transformCount; i++ ){ QString arrayIndexStr = Carta::State::UtilState::getLookup(DATA_TRANSFORMS, QString::number(i)); @@ -75,15 +74,6 @@ void TransformsData::_initializeDefaultState(){ } -void TransformsData::_initializeCallbacks(){ - addCommandCallback( "getTransformsData", [=] (const QString & /*cmd*/, - const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QStringList dataTransformList = getTransformsData(); - QString result = dataTransformList.join( ","); - return result; - }); -} - bool TransformsData::isTransform( const QString& name, QString& actualName ) const { int transformCount = m_transforms.size(); bool validTransform = false; diff --git a/carta/cpp/core/Data/Colormap/TransformsData.h b/carta/cpp/core/Data/Colormap/TransformsData.h index e2d01510..d9a56433 100644 --- a/carta/cpp/core/Data/Colormap/TransformsData.h +++ b/carta/cpp/core/Data/Colormap/TransformsData.h @@ -20,6 +20,12 @@ class TransformsData : public Carta::State::CartaObject { public: + /** + * Return the default data transform. + * @return - the default data transform. + */ + QString getDefault() const; + /** * Returns true if the name represents a valid data transform; false, otherwise. * @param name a QString identifying a data transform. @@ -41,13 +47,11 @@ class TransformsData : public Carta::State::CartaObject { const static QString CLASS_NAME; private: void _initializeDefaultState(); - void _initializeCallbacks(); std::vector < QString > m_transforms; static bool m_registered; const static QString DATA_TRANSFORMS; - const static QString TRANSFORM_COUNT; const static QString TRANSFORM_NONE; const static QString TRANSFORM_ROOT; const static QString TRANSFORM_POLY; diff --git a/carta/cpp/core/Data/Colormap/TransformsImage.cpp b/carta/cpp/core/Data/Colormap/TransformsImage.cpp index 8be9770e..e364eb35 100644 --- a/carta/cpp/core/Data/Colormap/TransformsImage.cpp +++ b/carta/cpp/core/Data/Colormap/TransformsImage.cpp @@ -3,7 +3,6 @@ #include "State/UtilState.h" #include -#include namespace Carta { @@ -11,7 +10,7 @@ namespace Data { const QString TransformsImage::IMAGE_TRANSFORMS = "imageTransforms"; const QString TransformsImage::CLASS_NAME = "TransformsImage"; -const QString TransformsImage::TRANSFORM_COUNT = "imageTransformCount"; +const QString TransformsImage::GAMMA = "Gamma"; class TransformsImage::Factory : public Carta::State::CartaObjectFactory { @@ -35,7 +34,6 @@ TransformsImage::TransformsImage( const QString& path, const QString& id): CartaObject( CLASS_NAME, path, id ){ _initializeDefaultState(); - _initializeCallbacks(); } @@ -52,10 +50,9 @@ void TransformsImage::_initializeDefaultState(){ // get all TransformsImage provided by core //hard-code the possible transforms until Pavol's code is available. - m_transforms.push_back("Gamma"); + m_transforms.push_back( GAMMA ); int transformCount = m_transforms.size(); - m_state.insertValue( TRANSFORM_COUNT, transformCount ); m_state.insertArray( IMAGE_TRANSFORMS, transformCount ); for ( int i = 0; i < transformCount; i++ ){ QString arrayIndexStr = Carta::State::UtilState::getLookup(IMAGE_TRANSFORMS, QString::number(i)); @@ -65,15 +62,11 @@ void TransformsImage::_initializeDefaultState(){ } -void TransformsImage::_initializeCallbacks(){ - addCommandCallback( "getTransformsImage", [=] (const QString & /*cmd*/, - const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QStringList dataTransformList = getTransformsImage(); - QString result = dataTransformList.join( ","); - return result; - }); +QString TransformsImage::getDefault() const { + return GAMMA; } + bool TransformsImage::isTransform( const QString& name ) const { int transformCount = m_transforms.size(); bool validTransform = false; @@ -87,7 +80,6 @@ bool TransformsImage::isTransform( const QString& name ) const { } - TransformsImage::~TransformsImage(){ } diff --git a/carta/cpp/core/Data/Colormap/TransformsImage.h b/carta/cpp/core/Data/Colormap/TransformsImage.h index 5d94d2aa..383898ad 100644 --- a/carta/cpp/core/Data/Colormap/TransformsImage.h +++ b/carta/cpp/core/Data/Colormap/TransformsImage.h @@ -19,6 +19,12 @@ class TransformsImage : public Carta::State::CartaObject { public: + /** + * Return the default image transform. + * @return - the default image transform. + */ + QString getDefault() const; + /** * Returns true if the name represents a valid image transform; false, otherwise. * @param name a QString identifying an image transform. @@ -43,7 +49,8 @@ class TransformsImage : public Carta::State::CartaObject { static bool m_registered; const static QString IMAGE_TRANSFORMS; - const static QString TRANSFORM_COUNT; + const static QString GAMMA; + TransformsImage( const QString& path, const QString& id ); class Factory; diff --git a/carta/cpp/core/Data/DataLoader.cpp b/carta/cpp/core/Data/DataLoader.cpp index 1594264f..cbcb85e0 100644 --- a/carta/cpp/core/Data/DataLoader.cpp +++ b/carta/cpp/core/Data/DataLoader.cpp @@ -35,6 +35,7 @@ QString DataLoader::fakeRootDirName = "RootDirectory"; const QString DataLoader::CLASS_NAME = "DataLoader"; const QString DataLoader::DIR = "dir"; const QString DataLoader::CRTF = ".crtf"; +const QString DataLoader::REG = ".reg"; bool DataLoader::m_registered = Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, @@ -188,7 +189,7 @@ void DataLoader::_processDirectory(const QDir& rootDir, QJsonObject& rootObj) co } } else if (dit.fileInfo().isFile()) { - if (fileName.endsWith(".fits") || fileName.endsWith( CRTF )) { + if (fileName.endsWith(".fits") || fileName.endsWith( CRTF ) || fileName.endsWith( REG )) { _makeFileNode(dirArray, fileName); } } diff --git a/carta/cpp/core/Data/DataLoader.h b/carta/cpp/core/Data/DataLoader.h index dd97600b..1aab919d 100644 --- a/carta/cpp/core/Data/DataLoader.h +++ b/carta/cpp/core/Data/DataLoader.h @@ -77,6 +77,7 @@ class DataLoader : public Carta::State::CartaObject { static QString fakeRootDirName; const static QString CLASS_NAME; const static QString CRTF; + const static QString REG; virtual ~DataLoader(); diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index ed8a1a74..7e21d71b 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -66,7 +66,6 @@ const QString Histogram::FOOT_PRINT_REGION_ALL = "All Regions"; const QString Histogram::FREQUENCY_UNIT = "rangeUnit"; const QString Histogram::CLIP_MIN_PERCENT = "clipMinPercent"; const QString Histogram::CLIP_MAX_PERCENT = "clipMaxPercent"; -const QString Histogram::SIGNIFICANT_DIGITS = "significantDigits"; const QString Histogram::SIZE_ALL_RESTRICT ="limitCubeSize"; const QString Histogram::RESTRICT_SIZE_MAX = "cubeSizeMax"; @@ -102,8 +101,10 @@ Histogram::Histogram( const QString& path, const QString& id): Settings* prefObj = objMan->createObject(); m_preferences.reset( prefObj ); - connect( m_renderService.get(), SIGNAL(histogramResult()), - this, SLOT(_histogramRendered())); + connect( m_renderService.get(), + SIGNAL(histogramResult(const Carta::Lib::Hooks::HistogramResult& )), + this, + SLOT(_histogramRendered(const Carta::Lib::Hooks::HistogramResult& ))); m_plotManager->setPlotGenerator( new Plot2DGenerator( Plot2DGenerator::PlotType::HISTOGRAM) ); m_plotManager->setTitleAxisY( "Count(pixels)" ); @@ -193,10 +194,13 @@ void Histogram::_createHistogram( Controller* controller){ double minIntensity = 0; double maxIntensity = 0; std::pair frameBounds = _getFrameBounds(); - bool minValid = controller->getIntensity( frameBounds.first, frameBounds.second, 0, &minIntensity ); - bool maxValid = controller->getIntensity( frameBounds.first, frameBounds.second, 1, &maxIntensity ); + int index = 0; + bool minValid = controller->getIntensity( frameBounds.first, frameBounds.second, + 0, &minIntensity, &index ); + bool maxValid = controller->getIntensity( frameBounds.first, frameBounds.second, + 1, &maxIntensity, &index ); if(minValid && maxValid){ - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); minIntensity = Util::roundToDigits( minIntensity, significantDigits ); maxIntensity = Util::roundToDigits( maxIntensity, significantDigits ); m_stateData.setValue(CLIP_MIN_PERCENT, 0 ); @@ -304,7 +308,9 @@ double Histogram::_getBufferedIntensity( const QString& clipKey, const QString& if ( controller != nullptr ){ double actualIntensity = intensity; std::pair frameBounds = _getFrameBounds(); - bool intensityValid = controller->getIntensity( frameBounds.first, frameBounds.second, percentile, &actualIntensity ); + int index = 0; + bool intensityValid = controller->getIntensity( frameBounds.first, + frameBounds.second, percentile, &actualIntensity, &index ); if ( intensityValid ){ intensity = actualIntensity; } @@ -389,8 +395,7 @@ bool Histogram::getUseClipBuffer(){ return useBuffer; } -void Histogram::_histogramRendered(){ - Carta::Lib::Hooks::HistogramResult result = m_renderService->getResult(); +void Histogram::_histogramRendered(const Carta::Lib::Hooks::HistogramResult& result){ QString resultName = result.getName(); if ( resultName.startsWith( Util::ERROR)){ ErrorManager* hr = Util::findSingletonObject(); @@ -460,7 +465,7 @@ void Histogram::_initializeDefaultState(){ m_state.insertValue(SIZE_ALL_RESTRICT, true ); m_state.insertValue(RESTRICT_SIZE_MAX, 1000000 ); m_state.insertValue(FOOT_PRINT, FOOT_PRINT_IMAGE ); - m_state.insertValue(SIGNIFICANT_DIGITS, 6 ); + m_state.insertValue(Util::SIGNIFICANT_DIGITS, 6 ); //Default Tab m_state.insertValue( Util::TAB_INDEX, 1 ); m_state.flushState(); @@ -905,9 +910,9 @@ void Histogram::_initializeCallbacks(){ addCommandCallback( "setSignificantDigits", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result; - std::set keys = {SIGNIFICANT_DIGITS}; + std::set keys = {Util::SIGNIFICANT_DIGITS}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString digitsStr = dataValues[SIGNIFICANT_DIGITS]; + QString digitsStr = dataValues[Util::SIGNIFICANT_DIGITS]; bool validDigits = false; int digits = digitsStr.toInt( &validDigits ); if ( validDigits ){ @@ -999,7 +1004,6 @@ void Histogram::_loadData( Controller* controller ){ std::shared_ptr pipeline = dataSource->_getPipeline(); m_plotManager->setPipeline( pipeline ); - m_renderService->renderHistogram(image, binCount, minChannel, maxChannel, minFrequency, maxFrequency, rangeUnits, minIntensity, maxIntensity, dataSource->_getFileName()); @@ -1213,7 +1217,8 @@ QString Histogram::setClipMax( double clipMaxClient, bool finish ){ double imageMaxIntensity = 0; Controller* controller = _getControllerSelected(); if ( controller != nullptr ){ - double maxIntensityValid = controller->getIntensity( 1, &imageMaxIntensity ); + int index = 0; + double maxIntensityValid = controller->getIntensity( 1, &imageMaxIntensity, &index ); if ( maxIntensityValid ){ if ( clipMaxClient < imageMaxIntensity ){ adjustedMax = imageMaxIntensity; @@ -1221,7 +1226,7 @@ QString Histogram::setClipMax( double clipMaxClient, bool finish ){ } //Set the max clip that will not exceed the maximum intensity of the image. - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double clipMaxRounded = Util::roundToDigits( adjustedMax, significantDigits ); if ( qAbs(clipMaxRounded - oldMax) > m_errorMargin){ m_stateData.setValue(CLIP_MAX, clipMaxRounded ); @@ -1231,7 +1236,8 @@ QString Histogram::setClipMax( double clipMaxClient, bool finish ){ if ( controller != nullptr ){ std::pair bounds = _getFrameBounds(); double clipUpperBound; - controller->getIntensity( bounds.first, bounds.second, 1, &clipUpperBound ); + int index; + controller->getIntensity( bounds.first, bounds.second, 1, &clipUpperBound, &index ); double clipMaxPercent = controller->getPercentile(bounds.first, bounds.second, clipMaxClient ); if ( clipMaxPercent >= 0 ){ clipMaxPercent = Util::roundToDigits(clipMaxPercent * 100, significantDigits); @@ -1290,7 +1296,8 @@ QString Histogram::setClipMin( double clipMinClient, bool finish ){ if ( controller != nullptr ){ double imageMinIntensity = 0; double minIntensityValid = false; - minIntensityValid = controller->getIntensity( 0, &imageMinIntensity ); + int index = 0; + minIntensityValid = controller->getIntensity( 0, &imageMinIntensity, &index ); if ( minIntensityValid ){ if ( clipMinClient < imageMinIntensity ){ adjustedMin = imageMinIntensity; @@ -1298,7 +1305,7 @@ QString Histogram::setClipMin( double clipMinClient, bool finish ){ } //Decide if we need to change the actual clip min. - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS ); double clipMinRounded = Util::roundToDigits( adjustedMin, significantDigits ); if ( qAbs(clipMinRounded - oldMin) > m_errorMargin){ m_stateData.setValue(CLIP_MIN, clipMinRounded ); @@ -1346,7 +1353,7 @@ QString Histogram::setClipMinPercent( double clipMinPercent, bool complete ){ QString result; double oldMinPercent = m_stateData.getValue(CLIP_MIN_PERCENT); double clipMaxPercent = m_stateData.getValue(CLIP_MAX_PERCENT); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS ); double clipMinPercentRounded = Util::roundToDigits( clipMinPercent, significantDigits ); if( 0 <= clipMinPercentRounded && clipMinPercentRounded <= 100 ){ if ( clipMinPercentRounded < clipMaxPercent ){ @@ -1357,7 +1364,9 @@ QString Histogram::setClipMinPercent( double clipMinPercent, bool complete ){ double clipMin = 0; double cMin = clipMinPercentRounded / 100.0; std::pair bounds = _getFrameBounds(); - bool validIntensity = controller->getIntensity( bounds.first, bounds.second, cMin, &clipMin); + int index = 0; + bool validIntensity = controller->getIntensity( bounds.first, bounds.second, + cMin, &clipMin, &index ); if(validIntensity){ double oldClipMin = m_stateData.getValue(CLIP_MIN); if(qAbs(oldClipMin - clipMin) > m_errorMargin){ @@ -1406,7 +1415,7 @@ QString Histogram::setClipMaxPercent( double clipMaxPercent, bool complete ){ QString result; double oldMaxPercent = m_stateData.getValue(CLIP_MAX_PERCENT); double clipMinPercent = m_stateData.getValue(CLIP_MIN_PERCENT); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double clipMaxPercentRounded = Util::roundToDigits( clipMaxPercent, significantDigits ); if( 0 <= clipMaxPercentRounded && clipMaxPercentRounded <= 100 ){ double lookupPercent = clipMaxPercentRounded; @@ -1418,7 +1427,9 @@ QString Histogram::setClipMaxPercent( double clipMaxPercent, bool complete ){ double clipMax = 0; double decPercent = lookupPercent / 100.0; std::pair bound = _getFrameBounds(); - bool validIntensity = controller->getIntensity(bound.first,bound.second, decPercent, &clipMax); + int index = 0; + bool validIntensity = controller->getIntensity(bound.first, bound.second, + decPercent, &clipMax, &index ); if(validIntensity){ double oldClipMax = m_stateData.getValue(CLIP_MAX); if(qAbs(oldClipMax - clipMax) > m_errorMargin){ @@ -1485,7 +1496,7 @@ QString Histogram::setColorMin( double colorMin, bool finish ){ double oldMin = m_stateData.getValue(COLOR_MIN); double colorMax = m_stateData.getValue(COLOR_MAX); double oldMinPercent = m_stateData.getValue(COLOR_MIN_PERCENT); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double colorMinRounded = Util::roundToDigits( colorMin, significantDigits ); //Bypass the check that the min is less than the max if we are not finished //and planning to set the max. @@ -1523,7 +1534,7 @@ QString Histogram::setColorMax( double colorMax, bool finish ){ double oldMax = m_stateData.getValue(COLOR_MAX); double colorMin = m_stateData.getValue(COLOR_MIN); double oldMaxPercent = m_stateData.getValue(COLOR_MAX_PERCENT); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double colorMaxRounded = Util::roundToDigits( colorMax, significantDigits ); //By pass checking that the new max is larger than the min if we are not //finished and are planning to set a new min. @@ -1534,7 +1545,8 @@ QString Histogram::setColorMax( double colorMax, bool finish ){ if ( controller != nullptr ){ std::pair bounds = _getFrameBounds(); double colorUpperBound; - controller->getIntensity( bounds.first, bounds.second, 1, &colorUpperBound ); + int index = 0; + controller->getIntensity( bounds.first, bounds.second, 1, &colorUpperBound, &index ); double colorMaxPercent = controller->getPercentile(bounds.first, bounds.second, colorMaxRounded ); if ( colorMaxPercent >= 0 ){ colorMaxPercent = colorMaxPercent * 100; @@ -1561,7 +1573,7 @@ QString Histogram::setColorMaxPercent( double colorMaxPercent, bool complete ){ QString result; double oldMaxPercent = m_stateData.getValue(COLOR_MAX_PERCENT); double colorMinPercent = m_stateData.getValue(COLOR_MIN_PERCENT); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double colorMaxPercentRounded = Util::roundToDigits( colorMaxPercent, significantDigits); if( 0 <= colorMaxPercentRounded && colorMaxPercentRounded <= 100 ){ double lookupPercent = colorMaxPercentRounded; @@ -1573,7 +1585,9 @@ QString Histogram::setColorMaxPercent( double colorMaxPercent, bool complete ){ double colorMax = 0; double decPercent = lookupPercent / 100.0; std::pair bound = _getFrameBounds(); - bool validIntensity = controller->getIntensity(bound.first,bound.second, decPercent, &colorMax); + int index = 0; + bool validIntensity = controller->getIntensity(bound.first,bound.second, + decPercent, &colorMax, &index); if(validIntensity){ double oldColorMax = m_stateData.getValue(COLOR_MAX); if(qAbs(oldColorMax - colorMax) > m_errorMargin){ @@ -1605,7 +1619,7 @@ QString Histogram::setColorMinPercent( double colorMinPercent, bool complete ){ QString result; double oldMinPercent = m_stateData.getValue(COLOR_MIN_PERCENT); double colorMaxPercent = m_stateData.getValue(COLOR_MAX_PERCENT); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double colorMinPercentRounded = Util::roundToDigits( colorMinPercent, significantDigits ); if( 0 <= colorMinPercentRounded && colorMinPercentRounded <= 100 ){ if ( colorMinPercentRounded < colorMaxPercent || !complete){ @@ -1616,7 +1630,9 @@ QString Histogram::setColorMinPercent( double colorMinPercent, bool complete ){ double colorMin = 0; double cMin = colorMinPercentRounded / 100.0; std::pair bounds = _getFrameBounds(); - bool validIntensity = controller->getIntensity( bounds.first, bounds.second, cMin, &colorMin); + int index = 0; + bool validIntensity = controller->getIntensity( bounds.first, bounds.second, + cMin, &colorMin, &index ); if(validIntensity){ double oldColorMin = m_stateData.getValue(COLOR_MIN); if(qAbs(oldColorMin - colorMin) > m_errorMargin){ @@ -1647,7 +1663,7 @@ QString Histogram::setColorMinPercent( double colorMinPercent, bool complete ){ QString Histogram::setBinWidth( double binWidth ){ QString result; double oldBinWidth = m_state.getValue(BIN_WIDTH); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); double binWidthRounded = Util::roundToDigits( binWidth, significantDigits ); if ( binWidthRounded > 0 ){ if ( qAbs( oldBinWidth - binWidthRounded) > m_errorMargin ){ @@ -1679,7 +1695,7 @@ QString Histogram::setBinCount( int binCount ){ m_state.setValue(BIN_COUNT, binCount ); double binWidth = _toBinWidth( binCount ); - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); m_state.setValue(BIN_WIDTH, Util::roundToDigits(binWidth,significantDigits) ); m_state.flushState(); _generateHistogram( nullptr ); @@ -1797,7 +1813,7 @@ QString Histogram::setPlaneRange( double planeMin, double planeMax){ double storedMin = m_stateData.getValue(PLANE_MIN); double storedMax = m_stateData.getValue(PLANE_MAX); if ( planeMin <= planeMax ){ - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS); bool changedState = false; if ( qAbs(planeMin - storedMin) > m_errorMargin){ m_stateData.setValue(PLANE_MIN, Util::roundToDigits(planeMin, significantDigits )); @@ -1855,8 +1871,8 @@ QString Histogram::setSignificantDigits( int digits ){ result = "Invalid significant digits; must be positive: "+QString::number( digits ); } else { - if ( m_state.getValue(SIGNIFICANT_DIGITS) != digits ){ - m_state.setValue(SIGNIFICANT_DIGITS, digits ); + if ( m_state.getValue(Util::SIGNIFICANT_DIGITS) != digits ){ + m_state.setValue(Util::SIGNIFICANT_DIGITS, digits ); _setErrorMargin(); } } @@ -1864,7 +1880,7 @@ QString Histogram::setSignificantDigits( int digits ){ } void Histogram::_setErrorMargin(){ - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS ); m_errorMargin = 1.0/qPow(10,significantDigits); } diff --git a/carta/cpp/core/Data/Histogram/Histogram.h b/carta/cpp/core/Data/Histogram/Histogram.h index a2e3e581..94416d9a 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.h +++ b/carta/cpp/core/Data/Histogram/Histogram.h @@ -363,7 +363,7 @@ private slots: void _createHistogram( Controller* ); //Notification that new histogram data has been produced. - void _histogramRendered(); + void _histogramRendered(const Carta::Lib::Hooks::HistogramResult& result); void _updateChannel( Controller* controller, Carta::Lib::AxisInfo::KnownType type ); void _updateColorClips( double colorMinPercent, double colorMaxPercent); @@ -453,7 +453,6 @@ private slots: const static QString FOOT_PRINT_REGION_ALL; const static QString CLIP_MIN_PERCENT; const static QString CLIP_MAX_PERCENT; - const static QString SIGNIFICANT_DIGITS; const static QString SIZE_ALL_RESTRICT; const static QString RESTRICT_SIZE_MAX; @@ -488,7 +487,7 @@ private slots: Carta::State::StateInterface m_stateData; Histogram( const Histogram& other); - Histogram operator=( const Histogram& other ); + Histogram& operator=( const Histogram& other ); }; } } diff --git a/carta/cpp/core/Data/Histogram/HistogramRenderService.cpp b/carta/cpp/core/Data/Histogram/HistogramRenderService.cpp index a4cbc93e..3493586a 100644 --- a/carta/cpp/core/Data/Histogram/HistogramRenderService.cpp +++ b/carta/cpp/core/Data/Histogram/HistogramRenderService.cpp @@ -14,10 +14,6 @@ HistogramRenderService::HistogramRenderService( QObject * parent ) : m_renderQueued = false; } -Carta::Lib::Hooks::HistogramResult HistogramRenderService::getResult() const { - return m_result; -} - bool HistogramRenderService::renderHistogram(std::shared_ptr dataSource, int binCount, int minChannel, int maxChannel, double minFrequency, double maxFrequency, @@ -25,6 +21,7 @@ bool HistogramRenderService::renderHistogram(std::shared_ptrgetResult(); - emit histogramResult( ); + Carta::Lib::Hooks::HistogramResult result = m_renderThread->getResult(); + emit histogramResult( result ); m_renderQueued = false; + } diff --git a/carta/cpp/core/Data/Histogram/HistogramRenderService.h b/carta/cpp/core/Data/Histogram/HistogramRenderService.h index c79e09a4..42d4cafc 100644 --- a/carta/cpp/core/Data/Histogram/HistogramRenderService.h +++ b/carta/cpp/core/Data/Histogram/HistogramRenderService.h @@ -4,10 +4,10 @@ #pragma once -#include -#include #include "CartaLib/CartaLib.h" #include "CartaLib/Hooks/HistogramResult.h" +#include +#include namespace Carta { namespace Lib { @@ -34,13 +34,6 @@ class HistogramRenderService : public QObject { */ explicit HistogramRenderService( QObject * parent = 0 ); - - /** - * Returns the appropriate data for generating a histogram. - * @return - the data needed for plotting a histogram. - */ - Carta::Lib::Hooks::HistogramResult getResult() const; - /** * Initiates the process of rendering thee histogram. * @param dataSource - the image that will bee the source of the histogram. @@ -69,7 +62,7 @@ class HistogramRenderService : public QObject { /** * Notification that new histogram data has been computed. */ - void histogramResult( ); + void histogramResult( const Carta::Lib::Hooks::HistogramResult& result ); private slots: @@ -80,10 +73,15 @@ private slots: int binCount, int minChannel, int maxChannel, double minFrequency, double maxFrequency, const QString& rangeUnits, double minIntensity, double maxIntensity, const QString& fileName); - Carta::Lib::Hooks::HistogramResult m_result; HistogramRenderWorker* m_worker; HistogramRenderThread* m_renderThread; bool m_renderQueued; + + + + + HistogramRenderService( const HistogramRenderService& other); + HistogramRenderService& operator=( const HistogramRenderService& other ); }; } } diff --git a/carta/cpp/core/Data/Histogram/HistogramRenderThread.h b/carta/cpp/core/Data/Histogram/HistogramRenderThread.h index 49178f62..32c34ec9 100644 --- a/carta/cpp/core/Data/Histogram/HistogramRenderThread.h +++ b/carta/cpp/core/Data/Histogram/HistogramRenderThread.h @@ -52,6 +52,9 @@ class HistogramRenderThread : public QThread { private: int m_fileDescriptor; Carta::Lib::Hooks::HistogramResult m_result; + + HistogramRenderThread( const HistogramRenderThread& other); + HistogramRenderThread& operator=( const HistogramRenderThread& other ); }; } } diff --git a/carta/cpp/core/Data/Histogram/HistogramRenderWorker.cpp b/carta/cpp/core/Data/Histogram/HistogramRenderWorker.cpp index 18450dd1..28d78a50 100644 --- a/carta/cpp/core/Data/Histogram/HistogramRenderWorker.cpp +++ b/carta/cpp/core/Data/Histogram/HistogramRenderWorker.cpp @@ -59,7 +59,9 @@ bool HistogramRenderWorker::setParameters(std::shared_ptr& levels, QString ContourControls::deleteContourSet( const QString& contourSetName ){ QString result; bool foundSet = false; - std::set >::iterator it = m_dataContours.begin(); + std::set >::iterator it = m_dataContours.begin(); while ( it != m_dataContours.end() ){ if ( (*it)->getName() == contourSetName ){ foundSet = true; @@ -198,8 +198,9 @@ QString ContourControls::_generatePercentile( const QString& contourSetName ){ int percentCount = percentileLevels.size(); std::vector levels( percentCount ); bool validIntensities = false; + int intensityIndex = 0; for ( int i = 0; i < percentCount; i++ ){ - validIntensities = m_percentIntensityMap->getIntensity( percentileLevels[i]/100, &levels[i] ); + validIntensities = m_percentIntensityMap->getIntensity( percentileLevels[i]/100, &levels[i], &intensityIndex ); if ( !validIntensities ){ break; } @@ -272,7 +273,7 @@ std::vector ContourControls::_getLevelsMinMax( double max , QString& err DataContours* ContourControls::_getContour( const QString& setName ) { DataContours* target = nullptr; - for ( std::set >::iterator it = m_dataContours.begin(); + for ( std::set >::iterator it = m_dataContours.begin(); it != m_dataContours.end(); it++ ){ if ( (*it)->getName() == setName ){ target = ( *it ).get(); @@ -622,7 +623,7 @@ void ContourControls::_initializeCallbacks(){ bool ContourControls::_isDuplicate( const QString& contourSetName ) const { bool duplicateSet = false; - for ( std::set >::iterator it = m_dataContours.begin(); + for ( std::set >::iterator it = m_dataContours.begin(); it != m_dataContours.end(); it++ ){ if ( (*it)->getName() == contourSetName ){ duplicateSet = true; @@ -849,7 +850,7 @@ void ContourControls::_updateContourSetState(){ m_stateData.resizeArray( CONTOUR_SETS, contourSetCount ); std::set drawContours; int i = 0; - for ( std::set >::iterator it = m_dataContours.begin(); + for ( std::set >::iterator it = m_dataContours.begin(); it != m_dataContours.end(); it++ ){ QString lookup = Carta::State::UtilState::getLookup( CONTOUR_SETS, i ); Carta::State::StateInterface dataState = (*it)->_getState(); diff --git a/carta/cpp/core/Data/Image/Contour/ContourControls.h b/carta/cpp/core/Data/Image/Contour/ContourControls.h index 12934c1d..65ea87b8 100644 --- a/carta/cpp/core/Data/Image/Contour/ContourControls.h +++ b/carta/cpp/core/Data/Image/Contour/ContourControls.h @@ -218,7 +218,7 @@ class ContourControls : public QObject, public Carta::State::CartaObject{ class Factory; - std::set > m_dataContours; + std::set > m_dataContours; std::shared_ptr m_drawContours; std::shared_ptr m_generatorState; diff --git a/carta/cpp/core/Data/Image/Contour/DataContours.h b/carta/cpp/core/Data/Image/Contour/DataContours.h index 469b6660..ef3986eb 100644 --- a/carta/cpp/core/Data/Image/Contour/DataContours.h +++ b/carta/cpp/core/Data/Image/Contour/DataContours.h @@ -162,8 +162,6 @@ Q_OBJECT class Factory; - - DataContours( const DataContours& other); DataContours& operator=( const DataContours& other ); }; diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index ef475fbc..75628718 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -1,6 +1,7 @@ #include "State/ObjectManager.h" #include "State/UtilState.h" #include "Data/Image/Controller.h" +#include "Data/Image/DataFactory.h" #include "Data/Image/Stack.h" #include "Data/Image/DataSource.h" #include "Data/Image/Grid/AxisMapper.h" @@ -15,11 +16,9 @@ #include "Data/Region/Region.h" - #include "Data/Util.h" #include "ImageView.h" #include "CartaLib/IImage.h" -#include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" #include "Globals.h" #include @@ -116,22 +115,14 @@ void Controller::addContourSet( std::shared_ptr contourSet){ } QString Controller::addData(const QString& fileName, bool* success) { - //Decide on the type of data we are adding based on the file - //suffix. *success = false; - QString result; - if ( fileName.endsWith( DataLoader::CRTF) ){ - result = _addDataRegion( fileName, success ); - } - else { - result = _addDataImage( fileName, success ); - } + QString result = DataFactory::addData( this, fileName, success ); return result; } QString Controller::_addDataImage(const QString& fileName, bool* success ) { - QString result = m_stack->_addDataImage( fileName, m_stateColor, success ); + QString result = m_stack->_addDataImage( fileName, success ); if ( *success ){ if ( isStackSelectAuto() ){ QStringList selectedLayers; @@ -145,16 +136,12 @@ QString Controller::_addDataImage(const QString& fileName, bool* success ) { return result; } -QString Controller::_addDataRegion(const QString& fileName, bool* success) { - QString result = m_stack->_addDataRegion( fileName, success ); - if ( success ){ + +void Controller::_addDataRegions( std::vector > regions ){ + if ( regions.size() > 0 ){ + m_stack->_addDataRegions( regions ); emit dataChangedRegion( this ); } - else { - ErrorManager* hr = Util::findSingletonObject(); - hr->registerError( result ); - } - return result; } QString Controller::applyClips( double minIntensityPercentile, double maxIntensityPercentile ){ @@ -182,7 +169,6 @@ QString Controller::applyClips( double minIntensityPercentile, double maxIntensi else { result = "Maximum intensity percentile invalid [0,1]: "+ QString::number( maxIntensityPercentile); } - if( clipsChangedValue ){ m_state.flushState(); _loadViewQueued(); @@ -349,14 +335,17 @@ std::vector Controller::getImageSlice() const { } -bool Controller::getIntensity( double percentile, double* intensity ) const{ +bool Controller::getIntensity( double percentile, double* intensity, + int* intensityIndex ) const{ int currentFrame = getFrame( AxisInfo::KnownType::SPECTRAL); - bool validIntensity = getIntensity( currentFrame, currentFrame, percentile, intensity ); + bool validIntensity = getIntensity( currentFrame, currentFrame, percentile, intensity, intensityIndex ); return validIntensity; } -bool Controller::getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const{ - bool validIntensity = m_stack->_getIntensity( frameLow, frameHigh, percentile, intensity ); +bool Controller::getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const{ + bool validIntensity = m_stack->_getIntensity( frameLow, frameHigh, percentile, + intensity, intensityIndex ); return validIntensity; } @@ -410,8 +399,8 @@ int Controller::getSelectImageIndex() const { } -std::vector< std::shared_ptr > Controller::getSelectedColorStates(){ - std::vector< std::shared_ptr > colorStates = m_stack->_getSelectedColorStates(); +std::vector > Controller::getSelectedColorStates( bool global ){ + std::vector > colorStates = m_stack->_getSelectedColorStates( global ); return colorStates; } @@ -930,6 +919,7 @@ void Controller::resetStateData( const QString& state ){ //Notify others there has been a change to the data. emit dataChanged( this ); emit dataChangedRegion( this ); + emit colorChanged( this ); //Reset the state of the grid controls based on the selected image. StateInterface gridState = m_stack->_getGridState(); @@ -997,7 +987,7 @@ void Controller::setAutoClip( bool autoClip ){ QString Controller::setClipValue( double clipVal ) { QString result; - if ( 0 <= clipVal && clipVal < 1 ){ + if ( 0 <= clipVal && clipVal <= 1 ){ double oldClipValMin = m_state.getValue( CLIP_VALUE_MIN ); double oldClipValMax = m_state.getValue( CLIP_VALUE_MAX ); double oldClipVal = oldClipValMax - oldClipValMin; @@ -1010,7 +1000,7 @@ QString Controller::setClipValue( double clipVal ) { } } else { - result = "Clip value must be in [0,1)."; + result = "Clip value must be in [0,1]."; } return result; } @@ -1043,22 +1033,6 @@ void Controller::setFrameImage( int val) { } -void Controller::_setColorMapGlobal( std::shared_ptr colorState ){ - m_stateColor = colorState; -} - -void Controller::_setColorMapUseGlobal( bool global ) { - //Reset the color maps. - if ( global ){ - m_stack->_setColorMapGlobal( m_stateColor ); - } - else { - m_stack->_setColorMapGlobal( nullptr ); - } - emit colorChanged( this ); -} - - QString Controller::setImageVisibility( /*int dataIndex*/const QString& idStr, bool visible ){ QString result; if ( idStr.length() > 0 ){ diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index a0fca511..c904674c 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -21,9 +21,6 @@ class CoordinateFormatterInterface; namespace Carta { namespace Lib { - namespace PixelPipeline { - class CustomizablePixelPipeline; - } namespace Image { class ImageInterface; } @@ -52,6 +49,7 @@ class Controller: public QObject, public Carta::State::CartaObject, friend class Animator; friend class Colormap; + friend class DataFactory; friend class Profiler; Q_OBJECT @@ -215,21 +213,24 @@ class Controller: public QObject, public Carta::State::CartaObject, /** * Returns the intensity corresponding to a given percentile in the current frame. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - frame index where maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - bool getIntensity( double percentile, double* intensity ) const; + bool getIntensity( double percentile, double* intensity, int* intensityIndex ) const; /** * Returns the intensity corresponding to a given percentile. - * @param frameLow a lower bound for the image channels or -1 if there is no lower bound. - * @param frameHigh an upper bound for the image channels or -1 if there is no upper bound. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param frameLow - a lower bound for the image channels or -1 if there is no lower bound. + * @param frameHigh - an upper bound for the image channels or -1 if there is no upper bound. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - frame index where maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - bool getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; + bool getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const; /** * Get the dimensions of the image viewer (window size). @@ -290,10 +291,12 @@ class Controller: public QObject, public Carta::State::CartaObject, /** * Get the color map information for the data sources that have been * selected. + * @param global - whether color changes apply globally or only to data sources + * that are selected. * @return - a list containing color map information for the data sources * that have been selected. */ - std::vector< std::shared_ptr > getSelectedColorStates(); + std::vector > getSelectedColorStates( bool global ); /** * Return a count of the number of image layers in the stack. @@ -586,7 +589,7 @@ private slots: class Factory; /// Add a region to the stack from a file. - QString _addDataRegion(const QString& fileName, bool* success ); + void _addDataRegions( std::vector > regions ); /// Add an image to the stack from a file. QString _addDataImage( const QString& fileName, bool* success ); @@ -598,7 +601,7 @@ private slots: void _clearStatistics(); - set _getAxesHidden() const; + std::set _getAxesHidden() const; std::vector _getAxisZTypes() const; @@ -608,19 +611,6 @@ private slots: void _initializeState(); void _initializeCallbacks(); - /** - * Set whether or not the selected layers should be using the global - * colormap. - * @param global - true if selected layers should use the global color map; - * false, otherwise. - */ - void _setColorMapUseGlobal( bool global ); - - /** - * Set the global color map.. - * @param colorState - the global color map information. - */ - void _setColorMapGlobal( std::shared_ptr colorState ); /** * Make a frame selection. @@ -658,8 +648,6 @@ private slots: //Data available to and managed by this controller. std::unique_ptr m_stack; - std::shared_ptr m_stateColor; - //Separate state for mouse events since they get updated rapidly and not //everyone wants to listen to them. Carta::State::StateInterface m_stateMouse; diff --git a/carta/cpp/core/Data/Image/DataFactory.cpp b/carta/cpp/core/Data/Image/DataFactory.cpp new file mode 100644 index 00000000..38a51989 --- /dev/null +++ b/carta/cpp/core/Data/Image/DataFactory.cpp @@ -0,0 +1,130 @@ +#include "DataFactory.h" +#include "Data/DataLoader.h" +#include "Data/Image/Controller.h" +#include "Data/Image/DataSource.h" +#include "Data/Region/Region.h" +#include "Data/Region/RegionFactory.h" +#include "Data/Util.h" +#include "CartaLib/Hooks/LoadRegion.h" +#include "Globals.h" + +#include + +namespace Carta { + +namespace Data { + + +DataFactory::DataFactory(){ +} + + +QString DataFactory::addData( Controller* controller, const QString& fileName, bool* success ){ + QString result; + *success = false; + if ( controller ){ + QFileInfo fileInfo( fileName ); + bool dataFile = fileInfo.isFile(); + if ( dataFile ){ + bool regionFile = _isRegion( fileName ); + //If we think it is a region, see if any of the region parsing + //plugins can handle it. + if ( regionFile ){ + std::vector > regions = + _loadRegions( controller, fileName, success, result ); + if ( regions.size() > 0 ){ + controller->_addDataRegions( regions ); + } + } + } + //Try loading it as an image. + if ( !(*success) ){ + result = controller->_addDataImage( fileName, success ); + } + } + else { + result = "The data in "+fileName +" could not be added because no controller was specified."; + } + return result; +} + + +bool DataFactory::_isRegion( const QString& fileName ){ + bool regionFile = false; + if ( fileName.endsWith( DataLoader::CRTF) ){ + regionFile = true; + } + else if ( fileName.endsWith( ".reg")){ + regionFile = true; + } + else { + QFile file( fileName ); + if ( file.open( QIODevice::ReadOnly | QIODevice::Text)){ + char buf[1024]; + qint64 lineLength = file.readLine( buf, sizeof(buf)); + if ( lineLength > 0 ){ + QString line( buf ); + if ( line.startsWith( "#CRTF") ){ + regionFile = true; + } + else if ( line.startsWith( "# Region file format: DS9") ){ + regionFile = true; + } + //Region files for unspecified plug-ins? + else if ( line.contains( "region", Qt::CaseInsensitive) ){ + regionFile = true; + } + } + } + } + return regionFile; +} + + +std::vector > DataFactory::_loadRegions( Controller* controller, + const QString& fileName, bool* success, QString& errorMsg ){ + std::vector< std::shared_ptr > regions; + std::shared_ptr dataSource = controller->getDataSource(); + if ( dataSource ){ + + std::shared_ptr image = dataSource->_getImage(); + auto result = Globals::instance()-> pluginManager() + -> prepare (fileName, image ); + auto lam = /*[=]*/[®ions,fileName] ( const Carta::Lib::Hooks::LoadRegion::ResultType &data ) { + int regionCount = data.size(); + //Return whether to continue the loop or not. We continue until we + //find the first plugin that can handle the region format and generate + //one or more regions. + bool continueLoop = true; + if ( regionCount > 0 ){ + continueLoop = false; + } + for ( int i = 0; i < regionCount; i++ ){ + if ( data[i] ){ + std::shared_ptr regionPtr = RegionFactory::makeRegion( data[i] ); + regionPtr -> _setUserId( fileName, i ); + regions.push_back( regionPtr ); + } + } + return continueLoop; + }; + + try { + //Find the first plugin that can load the region. + result.forEachCond( lam ); + *success = true; + } + catch( char*& error ){ + errorMsg = QString( error ); + *success = false; + } + } + return regions; +} + + +DataFactory::~DataFactory(){ + +} +} +} diff --git a/carta/cpp/core/Data/Image/DataFactory.h b/carta/cpp/core/Data/Image/DataFactory.h new file mode 100644 index 00000000..23c9be09 --- /dev/null +++ b/carta/cpp/core/Data/Image/DataFactory.h @@ -0,0 +1,59 @@ +/*** + * Factory for adding data (images or regions) to a controller. + */ + +#pragma once + +#include "CartaLib/RegionInfo.h" +#include + +namespace Carta { + +namespace Data { + +class Controller; +class Region; + +class DataFactory { + + +public: + /** + * Add data from a file to the given controller; this could be region or + * image data. + * @param controller - the controller that will manage the data. + * @param fileName - the absolute path to a file or directory containing the + * data. + * @param success - set to true for a successful image load; false otherwise. + * @return - an error message if the data could not be added; an identifier for + * the carta object managing the image if the data was added. + */ + static QString + addData( Controller* controller, const QString& fileName, bool* success ); + + + virtual ~DataFactory(); + +private: + + /** + * Returns true if the file is a recognized region file; false otherwise. + * @param fileName - an identifier for a file. + * @return - true if the file is a recognized format for region files; false otherwise. + */ + static bool _isRegion( const QString& fileName ); + + static std::vector > _loadRegions( Controller* controller, + const QString& fileName, bool* success, QString& errorMsg ); + + /** + * Constructor. + */ + DataFactory(); + + DataFactory( const DataFactory& other); + DataFactory& operator=( const DataFactory& other ); + +}; +} +} diff --git a/carta/cpp/core/Data/Image/DataSource.cpp b/carta/cpp/core/Data/Image/DataSource.cpp index 57268aee..da7b04f6 100755 --- a/carta/cpp/core/Data/Image/DataSource.cpp +++ b/carta/cpp/core/Data/Image/DataSource.cpp @@ -53,7 +53,7 @@ DataSource::DataSource() : } -int DataSource::_getFrameIndex( int sourceFrameIndex, const vector& sourceFrames ) const { +int DataSource::_getFrameIndex( int sourceFrameIndex, const std::vector& sourceFrames ) const { int frameIndex = 0; if (m_image ){ AxisInfo::KnownType axisType = static_cast( sourceFrameIndex ); @@ -67,7 +67,7 @@ int DataSource::_getFrameIndex( int sourceFrameIndex, const vector& sourceF return frameIndex; } -std::vector DataSource::_fitFramesToImage( const vector& sourceFrames ) const { +std::vector DataSource::_fitFramesToImage( const std::vector& sourceFrames ) const { int sourceFrameCount = sourceFrames.size(); std::vector outputFrames( sourceFrameCount ); for ( int i = 0; i < sourceFrameCount; i++ ){ @@ -330,7 +330,8 @@ std::shared_ptr DataSource::_getRender return m_renderService; } -bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const { +bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const { bool intensityFound = false; int spectralIndex = Util::getAxisIndex( m_image, AxisInfo::KnownType::SPECTRAL ); Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh, spectralIndex ); @@ -338,11 +339,15 @@ bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, Carta::Lib::NdArray::TypedView view( rawData, false ); // read in all values from the view into an array // we need our own copy because we'll do quickselect on it... + int index = 0; + std::vector < int > allIndices; std::vector < double > allValues; - view.forEach( [& allValues] ( const double val ) { + view.forEach( [& allValues, &allIndices, &index] ( const double val ) { if ( std::isfinite( val ) ) { allValues.push_back( val ); + allIndices.push_back( index ); } + index++; } ); @@ -354,6 +359,13 @@ bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, } std::nth_element( allValues.begin(), allValues.begin()+locationIndex, allValues.end() ); *intensity = allValues[locationIndex]; + int divisor = 1; + std::vector dims = m_image->dims(); + for ( int i = 0; i < spectralIndex; i++ ){ + divisor = divisor * dims[i]; + } + int specIndex = allIndices[locationIndex ]/divisor; + *intensityIndex = specIndex; intensityFound = true; } } diff --git a/carta/cpp/core/Data/Image/DataSource.h b/carta/cpp/core/Data/Image/DataSource.h index b68c541f..de67cea5 100755 --- a/carta/cpp/core/Data/Image/DataSource.h +++ b/carta/cpp/core/Data/Image/DataSource.h @@ -42,8 +42,10 @@ class CoordinateSystems; class DataSource : public QObject { friend class LayerData; +friend class DataFactory; friend class Histogram; friend class Profiler; +friend class Colormap; Q_OBJECT @@ -195,13 +197,15 @@ Q_OBJECT /** * Returns the intensity corresponding to a given percentile. - * @param frameLow a lower bound for the image channels or -1 if there is no lower bound. - * @param frameHigh an upper bound for the image channels or -1 if there is no upper bound. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param frameLow - a lower bound for the image channels or -1 if there is no lower bound. + * @param frameHigh - an upper bound for the image channels or -1 if there is no upper bound. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - location where the maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; + bool _getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* locationIndex ) const; /** * Returns the color used to draw nan pixels. diff --git a/carta/cpp/core/Data/Image/Draw/DrawSynchronizer.h b/carta/cpp/core/Data/Image/Draw/DrawSynchronizer.h index 4c987d80..330800b5 100644 --- a/carta/cpp/core/Data/Image/Draw/DrawSynchronizer.h +++ b/carta/cpp/core/Data/Image/Draw/DrawSynchronizer.h @@ -99,6 +99,9 @@ private slots: std::shared_ptr m_cec; std::vector m_pens; + DrawSynchronizer( const DrawSynchronizer& other); + DrawSynchronizer& operator=( const DrawSynchronizer& other ); + }; diff --git a/carta/cpp/core/Data/Image/Grid/Fonts.h b/carta/cpp/core/Data/Image/Grid/Fonts.h index 6c11901a..1d2dbb97 100644 --- a/carta/cpp/core/Data/Image/Grid/Fonts.h +++ b/carta/cpp/core/Data/Image/Grid/Fonts.h @@ -79,7 +79,6 @@ class Fonts : public Carta::State::CartaObject { class Factory; - Fonts( const Fonts& other); Fonts& operator=( const Fonts& other ); }; diff --git a/carta/cpp/core/Data/Image/Grid/Themes.h b/carta/cpp/core/Data/Image/Grid/Themes.h index da1b447b..c99519bf 100644 --- a/carta/cpp/core/Data/Image/Grid/Themes.h +++ b/carta/cpp/core/Data/Image/Grid/Themes.h @@ -64,7 +64,6 @@ class Themes : public Carta::State::CartaObject { class Factory; - Themes( const Themes& other); Themes& operator=( const Themes& other ); }; diff --git a/carta/cpp/core/Data/Image/IPercentIntensityMap.h b/carta/cpp/core/Data/Image/IPercentIntensityMap.h index 74712efe..1e741b7a 100755 --- a/carta/cpp/core/Data/Image/IPercentIntensityMap.h +++ b/carta/cpp/core/Data/Image/IPercentIntensityMap.h @@ -21,11 +21,12 @@ class IPercentIntensityMap { /** * Returns the intensity corresponding to a given percentile in the current frame. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - the frame where maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - virtual bool getIntensity( double percentile, double* intensity ) const = 0; + virtual bool getIntensity( double percentile, double* intensity, int* intensityIndex ) const = 0; /** * Return the percentile corresponding to the given intensity in the current frame. diff --git a/carta/cpp/core/Data/Image/Layer.h b/carta/cpp/core/Data/Image/Layer.h index 93141e1c..b861c222 100755 --- a/carta/cpp/core/Data/Image/Layer.h +++ b/carta/cpp/core/Data/Image/Layer.h @@ -230,13 +230,15 @@ class Layer : public QObject, public Carta::State::CartaObject { /** * Returns the intensity corresponding to a given percentile. - * @param frameLow a lower bound for the image frames or -1 if there is no lower bound. - * @param frameHigh an upper bound for the image frames or -1 if there is no upper bound. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param frameLow - a lower bound for the image frames or -1 if there is no lower bound. + * @param frameHigh - an upper bound for the image frames or -1 if there is no upper bound. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - the frame where maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - virtual bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const = 0; + virtual bool _getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const = 0; /** * Return the current layer. @@ -335,7 +337,13 @@ class Layer : public QObject, public Carta::State::CartaObject { */ virtual QPointF _getScreenPt( QPointF imagePt, bool* valid ) const = 0; - virtual std::vector< std::shared_ptr > _getSelectedColorStates() = 0; + /** + * Return the color states that are eligible for state changes. + * @param global - whether color state changes apply to all color maps or only to those that + * correspond to selected images. + * @return - a list of color states whose states may be changed. + */ + virtual std::vector< std::shared_ptr > _getSelectedColorStates( bool global ) = 0; /** @@ -395,7 +403,7 @@ class Layer : public QObject, public Carta::State::CartaObject { * @param clipMinPercentile the minimum clip value. * @param clipMaxPercentile the maximum clip value. */ - virtual void _load( vector frames, bool autoClip, double clipMinPercentile, + virtual void _load( std::vector frames, bool autoClip, double clipMinPercentile, double clipMaxPercentile ) = 0; /** @@ -442,12 +450,6 @@ class Layer : public QObject, public Carta::State::CartaObject { */ virtual void _resetZoom( ) = 0; - /** - * Reset the color map information for this data. - * @param colorState - stored information about the color map. - */ - virtual void _setColorMapGlobal( std::shared_ptr colorState ) = 0; - /** * Set the mode used to compose this layer. * @param id - the identifier for the layer group where the composition mode will change. diff --git a/carta/cpp/core/Data/Image/LayerData.cpp b/carta/cpp/core/Data/Image/LayerData.cpp index 85b17e75..754908e7 100755 --- a/carta/cpp/core/Data/Image/LayerData.cpp +++ b/carta/cpp/core/Data/Image/LayerData.cpp @@ -54,10 +54,17 @@ LayerData::LayerData(const QString& path, const QString& id) : m_renderQueued = false; _initializeState(); + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + ColorState* colorObj = objMan->createObject(); + m_stateColor.reset( colorObj ); + connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); + + DataGrid* gridObj = objMan->createObject(); m_dataGrid.reset( gridObj ); m_dataGrid->_initializeGridRenderer(); + _colorChanged(); std::shared_ptr gridService = m_dataGrid->_getRenderer(); std::shared_ptr imageService = m_dataSource->_getRenderer(); @@ -159,56 +166,14 @@ std::shared_ptr LayerData::_getColorState(){ return m_stateColor; } -std::vector< std::shared_ptr > LayerData::_getSelectedColorStates(){ +std::vector< std::shared_ptr > LayerData::_getSelectedColorStates( bool global){ std::vector< std::shared_ptr > colorStates; - if ( _isSelected() ){ + if ( _isSelected() || global ){ colorStates.push_back( m_stateColor ); } return colorStates; } -void LayerData::_setColorMapGlobal( std::shared_ptr colorState ){ - - if ( m_stateColor ){ - //Decide if we are going to use our own separate map that is a copy of our current - //one or reset to a shared global color map based on whether the passed in map - //is null - if ( _isSelected() ){ - bool colorReset = false; - if ( colorState ){ - _clearColorMap(); - colorReset = true; - m_stateColor = colorState; - } - else { - //We are going to use our own color map - if ( m_stateColor->_isGlobal() ){ - if ( m_stateColor ){ - disconnect( m_stateColor.get() ); - } - Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); - ColorState* cObject = objMan->createObject(); - if ( m_stateColor.get() != nullptr ){ - m_stateColor->_replicateTo( cObject ); - } - cObject->_setGlobal( false ); - m_stateColor.reset (cObject); - colorReset = true; - } - } - if ( colorReset ){ - _colorChanged( ); - connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); - } - } - } - //If ours is null, just use the one provided, no questions asked. - else if (colorState){ - m_stateColor = colorState; - _colorChanged(); - connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); - } -} std::shared_ptr LayerData::_getContour( const QString& name ){ std::shared_ptr contourSet; @@ -320,10 +285,12 @@ QPointF LayerData::_getImagePt( QPointF screenPt, bool* valid ) const { } -bool LayerData::_getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const { +bool LayerData::_getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const { bool intensityFound = false; if ( m_dataSource ){ - intensityFound = m_dataSource->_getIntensity( frameLow, frameHigh, percentile, intensity ); + intensityFound = m_dataSource->_getIntensity( frameLow, frameHigh, percentile, + intensity, intensityIndex ); } return intensityFound; } @@ -518,7 +485,7 @@ bool LayerData::_isContourDraw() const { return contourDraw; } -void LayerData::_load(vector frames, bool recomputeClipsOnNewFrame, +void LayerData::_load(std::vector frames, bool recomputeClipsOnNewFrame, double minClipPercentile, double maxClipPercentile ){ if ( m_dataSource ){ m_dataSource->_load( frames, recomputeClipsOnNewFrame, @@ -739,7 +706,9 @@ void LayerData::_resetState( const Carta::State::StateInterface& restoreState ){ if ( ! m_stateColor ){ Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); ColorState* cObject = objMan->createObject(); + m_stateColor.reset( cObject); + connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); } m_stateColor->_resetState( colorState ); } @@ -887,7 +856,8 @@ void LayerData::_updateColor(){ m_dataSource->_setColorMap( mapName ); m_dataSource->_setTransformData( m_stateColor->_getDataTransform() ); m_dataSource->_setGamma( m_stateColor->_getGamma() ); - m_dataSource->_setColorReversed( m_stateColor->_isReversed() ); + bool reversed = m_stateColor->_isReversed(); + m_dataSource->_setColorReversed( reversed ); m_dataSource->_setColorInverted( m_stateColor->_isInverted() ); double redAmount = m_stateColor->_getMixRed(); double greenAmount = m_stateColor->_getMixGreen(); diff --git a/carta/cpp/core/Data/Image/LayerData.h b/carta/cpp/core/Data/Image/LayerData.h index 8479dede..a828d8f1 100755 --- a/carta/cpp/core/Data/Image/LayerData.h +++ b/carta/cpp/core/Data/Image/LayerData.h @@ -240,7 +240,13 @@ Q_OBJECT */ virtual QPointF _getScreenPt( QPointF imagePt, bool* valid ) const Q_DECL_OVERRIDE; - virtual std::vector< std::shared_ptr > _getSelectedColorStates() Q_DECL_OVERRIDE; + /** + * Return the color states that are eligible for state changes. + * @param global - whether color state changes apply to all color maps or only to those that + * correspond to selected images. + * @return - a list of color states whose states may be changed. + */ + virtual std::vector< std::shared_ptr > _getSelectedColorStates( bool global ) Q_DECL_OVERRIDE; /** * Return the state of this layer. @@ -265,7 +271,7 @@ Q_OBJECT * @param clipMinPercentile the minimum clip value. * @param clipMaxPercentile the maximum clip value. */ - virtual void _load( vector frames, bool autoClip, double clipMinPercentile, + virtual void _load( std::vector frames, bool autoClip, double clipMinPercentile, double clipMaxPercentile ) Q_DECL_OVERRIDE; @@ -301,13 +307,15 @@ Q_OBJECT /** * Returns the intensity corresponding to a given percentile. - * @param frameLow a lower bound for the image frames or -1 if there is no lower bound. - * @param frameHigh an upper bound for the image frames or -1 if there is no upper bound. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param frameLow - a lower bound for the image frames or -1 if there is no lower bound. + * @param frameHigh - an upper bound for the image frames or -1 if there is no upper bound. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - the frame where maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - virtual bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const Q_DECL_OVERRIDE; + virtual bool _getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const Q_DECL_OVERRIDE; /** @@ -352,12 +360,6 @@ Q_OBJECT */ virtual void _resetStateContours(const Carta::State::StateInterface& restoreState ); - /** - * Reset the color map information for this data. - * @param colorState - stored information about the color map. - */ - virtual void _setColorMapGlobal( std::shared_ptr colorState ) Q_DECL_OVERRIDE; - virtual bool _setLayersGrouped( bool grouped ) Q_DECL_OVERRIDE; /** diff --git a/carta/cpp/core/Data/Image/LayerGroup.cpp b/carta/cpp/core/Data/Image/LayerGroup.cpp index 3e364ab6..bab8fc56 100755 --- a/carta/cpp/core/Data/Image/LayerGroup.cpp +++ b/carta/cpp/core/Data/Image/LayerGroup.cpp @@ -67,7 +67,6 @@ void LayerGroup::_addContourSet( std::shared_ptr contourSet){ } QString LayerGroup::_addData(const QString& fileName, bool* success, int* stackIndex, - std::shared_ptr colorState, QSize viewSize ) { QString result; Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); @@ -81,20 +80,16 @@ QString LayerGroup::_addData(const QString& fileName, bool* success, int* stackI //If we are making a new layer, see if there is a selected group. If so, //add to the group. If not, add to this group. if ( *success ){ - if ( colorState ){ - targetSource->_setColorMapGlobal( colorState ); - } targetSource->_viewResize( viewSize ); _setColorSupport( targetSource ); std::shared_ptr selectedGroup = _getSelectedGroup(); - if (selectedGroup && colorState ){ + if (selectedGroup ){ selectedGroup->_addLayer( std::shared_ptr(targetSource) ); } else { m_children.append( std::shared_ptr(targetSource) ); *stackIndex = m_children.size() - 1; } - } else { delete targetSource; @@ -454,11 +449,13 @@ int LayerGroup::_getIndexCurrent( ) const { return dataIndex; } -bool LayerGroup::_getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const { +bool LayerGroup::_getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const { bool intensityFound = false; int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ - intensityFound = m_children[dataIndex]->_getIntensity( frameLow, frameHigh, percentile, intensity ); + intensityFound = m_children[dataIndex]->_getIntensity( frameLow, frameHigh, + percentile, intensity, intensityIndex ); } return intensityFound; } @@ -685,7 +682,7 @@ bool LayerGroup::_isEmpty() const { } -void LayerGroup::_load(vector frames, bool recomputeClipsOnNewFrame, +void LayerGroup::_load(std::vector frames, bool recomputeClipsOnNewFrame, double minClipPercentile, double maxClipPercentile ){ int childCount = m_children.size(); for ( int i = 0; i < childCount; i++ ){ @@ -813,12 +810,6 @@ void LayerGroup::_resetPan( ){ } -void LayerGroup::_setColorMapGlobal( std::shared_ptr colorState ){ - for ( std::shared_ptr layer : m_children ){ - layer->_setColorMapGlobal( colorState ); - } -} - void LayerGroup::_setColorSupport( Layer* layer ){ QString compMode = _getCompositionMode(); bool colorSupport = m_compositionModes->isColorSupport( compMode ); @@ -977,10 +968,13 @@ bool LayerGroup::_setSelected( QStringList& names){ return stateChanged; } -std::vector< std::shared_ptr > LayerGroup::_getSelectedColorStates(){ +std::vector< std::shared_ptr > LayerGroup::_getSelectedColorStates( bool global ){ std::vector< std::shared_ptr > colorStates; - for ( std::shared_ptr layer : m_children ){ - std::vector > layerColorStates = layer->_getSelectedColorStates(); + int childCount = m_children.size(); + int currentIndex = _getIndexCurrent(); + for ( int j = 0; j < childCount; j++ ){ + int index = (currentIndex + j) % childCount; + std::vector > layerColorStates = m_children[index]->_getSelectedColorStates( global ); int layerStateCount = layerColorStates.size(); for ( int i = 0; i < layerStateCount; i++ ){ colorStates.push_back( layerColorStates[i] ); diff --git a/carta/cpp/core/Data/Image/LayerGroup.h b/carta/cpp/core/Data/Image/LayerGroup.h index 09716e42..e4168d3b 100755 --- a/carta/cpp/core/Data/Image/LayerGroup.h +++ b/carta/cpp/core/Data/Image/LayerGroup.h @@ -48,11 +48,9 @@ Q_OBJECT * @param success - set to true if the image file is successfully loaded. * @param stackIndex - set to the index of the image in this group if it is loaded * in this group. - * @param colorState - information about the color display for the image. * @param viewSize - the current client view size. */ QString _addData(const QString& fileName, bool* success, int* stackIndex, - std::shared_ptr colorState=std::shared_ptr(nullptr), QSize viewSize=QSize() ); @@ -194,13 +192,15 @@ Q_OBJECT /** * Returns the intensity corresponding to a given percentile. - * @param frameLow a lower bound for the image frames or -1 if there is no lower bound. - * @param frameHigh an upper bound for the image frames or -1 if there is no upper bound. - * @param percentile a number [0,1] for which an intensity is desired. - * @param intensity the computed intensity corresponding to the percentile. + * @param frameLow - a lower bound for the image frames or -1 if there is no lower bound. + * @param frameHigh - an upper bound for the image frames or -1 if there is no upper bound. + * @param percentile - a number [0,1] for which an intensity is desired. + * @param intensity - the computed intensity corresponding to the percentile. + * @param intensityIndex - the frame where maximum intensity was found. * @return true if the computed intensity is valid; otherwise false. */ - virtual bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const Q_DECL_OVERRIDE; + virtual bool _getIntensity( int frameLow, int frameHigh, double percentile, + double* intensity, int* intensityIndex ) const Q_DECL_OVERRIDE; /** * Return the current layer. @@ -275,7 +275,13 @@ Q_OBJECT */ virtual QSize _getSaveSize( const QSize& outputSize, Qt::AspectRatioMode aspectMode) const Q_DECL_OVERRIDE; - virtual std::vector< std::shared_ptr > _getSelectedColorStates() Q_DECL_OVERRIDE; + /** + * Return the color states that are eligible for state changes. + * @param global - whether color state changes apply to all color maps or only to those that + * correspond to selected images. + * @return - a list of color states whose states may be changed. + */ + virtual std::vector< std::shared_ptr > _getSelectedColorStates( bool global ) Q_DECL_OVERRIDE; /** * Returns the location on the screen corresponding to a location in image coordinates. @@ -334,7 +340,7 @@ Q_OBJECT * @param clipMinPercentile the minimum clip value. * @param clipMaxPercentile the maximum clip value. */ - virtual void _load( vector frames, bool autoClip, double clipMinPercentile, + virtual void _load( std::vector frames, bool autoClip, double clipMinPercentile, double clipMaxPercentile ) Q_DECL_OVERRIDE; /** @@ -364,11 +370,6 @@ Q_OBJECT */ virtual void _resetZoom() Q_DECL_OVERRIDE; - /** - * Reset the color map information for this data. - * @param colorState - stored information about the color map. - */ - virtual void _setColorMapGlobal( std::shared_ptr colorState ) Q_DECL_OVERRIDE; /** * Set the mode used to compose this layer. diff --git a/carta/cpp/core/Data/Image/Save/SaveService.h b/carta/cpp/core/Data/Image/Save/SaveService.h index 86bc1962..077969a7 100644 --- a/carta/cpp/core/Data/Image/Save/SaveService.h +++ b/carta/cpp/core/Data/Image/Save/SaveService.h @@ -117,6 +117,9 @@ private slots: int m_renderCount; //Count of images that have been redrawn to date. int m_redrawCount; + + SaveService( const SaveService& other); + SaveService& operator=( const SaveService& other ); }; } } diff --git a/carta/cpp/core/Data/Image/Save/SaveView.h b/carta/cpp/core/Data/Image/Save/SaveView.h index ec6972c3..7ee03d18 100644 --- a/carta/cpp/core/Data/Image/Save/SaveView.h +++ b/carta/cpp/core/Data/Image/Save/SaveView.h @@ -56,6 +56,9 @@ class SaveView { Carta::Lib::VectorGraphics::VGList m_vgList; qint64 m_lastRepaintId = - 1; + + SaveView( const SaveView& other); + SaveView& operator=( const SaveView& other ); }; } } diff --git a/carta/cpp/core/Data/Image/Save/SaveViewLayered.h b/carta/cpp/core/Data/Image/Save/SaveViewLayered.h index 7c578169..e27c623b 100644 --- a/carta/cpp/core/Data/Image/Save/SaveViewLayered.h +++ b/carta/cpp/core/Data/Image/Save/SaveViewLayered.h @@ -73,6 +73,9 @@ class SaveViewLayered { std::vector < RasterLayerInfo > m_rasterLayers; std::vector < VGLayerInfo > m_vgLayers; + SaveViewLayered( const SaveViewLayered& other); + SaveViewLayered& operator=( const SaveViewLayered& other ); + }; } } diff --git a/carta/cpp/core/Data/Image/Stack.cpp b/carta/cpp/core/Data/Image/Stack.cpp index ac17c29f..3d6443d9 100755 --- a/carta/cpp/core/Data/Image/Stack.cpp +++ b/carta/cpp/core/Data/Image/Stack.cpp @@ -53,10 +53,10 @@ Stack::Stack(const QString& path, const QString& id) : _initializeSelections(); } -QString Stack::_addDataImage(const QString& fileName, std::shared_ptr colorState, bool* success ) { +QString Stack::_addDataImage(const QString& fileName, bool* success ) { int stackIndex = -1; QSize viewSize = m_stackDraw->getClientSize(); - QString result = _addData( fileName, success, &stackIndex, colorState, viewSize); + QString result = _addData( fileName, success, &stackIndex, viewSize); if ( *success && stackIndex >= 0 ){ _resetFrames( stackIndex ); _saveState(); @@ -64,35 +64,12 @@ QString Stack::_addDataImage(const QString& fileName, std::shared_ptr= 0 ){ - - std::shared_ptr image = m_children[selectIndex]->_getImage(); - auto result = Globals::instance()-> pluginManager() - -> prepare (fileName, image ); - auto lam = [=] ( const Carta::Lib::Hooks::LoadRegion::ResultType &data ) { - int regionCount = data.size(); - for ( int i = 0; i < regionCount; i++ ){ - if ( data[i] ){ - std::shared_ptr regionPtr = RegionFactory::makeRegion( data[i] ); - regionPtr -> _setUserId( fileName, i ); - m_regions.push_back( regionPtr ); - } - } - }; - try { - result.forEach( lam ); - _saveStateRegions(); - *success = true; - } - catch( char*& error ){ - msg = QString( error ); - *success = false; - } +void Stack::_addDataRegions( std::vector> regions ){ + int count = regions.size(); + for ( int i = 0; i < count; i++ ){ + m_regions.push_back( regions[i]); } - return msg; + _saveStateRegions(); } @@ -210,6 +187,16 @@ QStringList Stack::getCoordinates( double x, double y, return _getCoordinates( x, y, system, indices ); } + +QString Stack::_getCurrentId() const { + QString id = ""; + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + id = m_children[dataIndex]->_getLayerId(); + } + return id; +} + QString Stack::_getCursorText( int mouseX, int mouseY ){ int dataIndex = _getIndexCurrent(); QString cursorText; @@ -254,6 +241,28 @@ int Stack::_getFrameUpperBound( AxisInfo::KnownType axisType ) const { return upperBound; } + +std::vector Stack::_getImageSlice() const { + std::vector result; + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + int dimensions = m_children[dataIndex] -> _getDimension(); + result.resize( dimensions ); + Carta::Lib::AxisInfo::KnownType axisXType = _getAxisXType(); + Carta::Lib::AxisInfo::KnownType axisYType = _getAxisYType(); + for ( int i = 0; i < dimensions; i++ ){ + Carta::Lib::AxisInfo::KnownType type = _getAxisType( i ); + if ( type == axisXType || type == axisYType ){ + result[i] = -1; + } + else { + result[i] = _getFrame( type ); + } + } + } + return result; +} + int Stack::_getIndex( const QString& layerId) const { int index = -1; int dataCount = m_children.size(); @@ -266,6 +275,7 @@ int Stack::_getIndex( const QString& layerId) const { return index; } + int Stack::_getIndexCurrent( ) const { int dataIndex = -1; if ( m_selectImage ){ @@ -307,55 +317,6 @@ int Stack::_getSelectImageIndex() const { return selectImageIndex; } -void Stack::_initializeSelections(){ - Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); - m_selectImage = objMan->createObject(); - connect( m_selectImage, SIGNAL(indexChanged()), this, SIGNAL(viewLoad())); - int axisCount = static_cast(AxisInfo::KnownType::OTHER); - m_selects.resize( axisCount ); - for ( int i = 0; i < axisCount; i++ ){ - m_selects[i] = objMan->createObject(); - connect( m_selects[i], SIGNAL(indexChanged()), this, SIGNAL(viewLoad())); - } -} - -void Stack::_initializeState(){ - int regionCount = m_regions.size(); - m_state.insertArray(REGIONS, regionCount ); - m_state.setValue( LayerGroup::COMPOSITION_MODE, LayerCompositionModes::NONE ); - m_state.flushState(); -} - -QString Stack::_getCurrentId() const { - QString id = ""; - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - id = m_children[dataIndex]->_getLayerId(); - } - return id; -} - -std::vector Stack::_getImageSlice() const { - std::vector result; - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - int dimensions = m_children[dataIndex] -> _getDimension(); - result.resize( dimensions ); - Carta::Lib::AxisInfo::KnownType axisXType = _getAxisXType(); - Carta::Lib::AxisInfo::KnownType axisYType = _getAxisYType(); - for ( int i = 0; i < dimensions; i++ ){ - Carta::Lib::AxisInfo::KnownType type = _getAxisType( i ); - if ( type == axisXType || type == axisYType ){ - result[i] = -1; - } - else { - result[i] = _getFrame( type ); - } - } - } - return result; -} - std::vector Stack::_getRegions() const { int regionCount = m_regions.size(); std::vector regionInfos( regionCount ); @@ -404,6 +365,26 @@ void Stack::_gridChanged( const Carta::State::StateInterface& state, bool applyA } } +void Stack::_initializeSelections(){ + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + m_selectImage = objMan->createObject(); + connect( m_selectImage, SIGNAL(indexChanged()), this, SIGNAL(viewLoad())); + int axisCount = static_cast(AxisInfo::KnownType::OTHER); + m_selects.resize( axisCount ); + for ( int i = 0; i < axisCount; i++ ){ + m_selects[i] = objMan->createObject(); + connect( m_selects[i], SIGNAL(indexChanged()), this, SIGNAL(viewLoad())); + } +} + + +void Stack::_initializeState(){ + int regionCount = m_regions.size(); + m_state.insertArray(REGIONS, regionCount ); + m_state.setValue( LayerGroup::COMPOSITION_MODE, LayerCompositionModes::NONE ); + m_state.flushState(); +} + void Stack::_load( bool recomputeClipsOnNewFrame, double minClipPercentile, double maxClipPercentile ){ std::vector frames = _getFrameIndices(); diff --git a/carta/cpp/core/Data/Image/Stack.h b/carta/cpp/core/Data/Image/Stack.h index 056e1f09..c2e590d8 100755 --- a/carta/cpp/core/Data/Image/Stack.h +++ b/carta/cpp/core/Data/Image/Stack.h @@ -76,8 +76,8 @@ private slots: private: - QString _addDataImage(const QString& fileName, std::shared_ptr colorState, bool* success ); - QString _addDataRegion(const QString& fileName, bool* success ); + QString _addDataImage(const QString& fileName, bool* success ); + void _addDataRegions( std::vector> regions ); QString _closeRegion( const QString& regionId ); diff --git a/carta/cpp/core/Data/Plotter/Plot2DManager.cpp b/carta/cpp/core/Data/Plotter/Plot2DManager.cpp index 648c1e51..8c90f6c1 100755 --- a/carta/cpp/core/Data/Plotter/Plot2DManager.cpp +++ b/carta/cpp/core/Data/Plotter/Plot2DManager.cpp @@ -18,7 +18,7 @@ namespace Data { const QString Plot2DManager::CLASS_NAME = "Plot2DManager"; const QString Plot2DManager::DATA_PATH = "dataPath"; const QString Plot2DManager::CURSOR_TEXT = "cursorText"; -const QString Plot2DManager::X_COORDINATE = "x"; + using Carta::State::UtilState; using Carta::State::StateInterface; @@ -45,7 +45,7 @@ Plot2DManager::Plot2DManager( const QString& path, const QString& id ): void Plot2DManager::addData( const Carta::Lib::Hooks::Plot2DResult* data){ if ( m_plotGenerator ){ - std::vector< pair > plotData = data->getData(); + std::vector< std::pair > plotData = data->getData(); const QString& name = data->getName(); m_plotGenerator->addData( plotData, name ); } @@ -78,9 +78,9 @@ void Plot2DManager::clearSelectionColor(){ void Plot2DManager::endSelection(const QString& params ){ - std::set keys = {X_COORDINATE}; + std::set keys = {Util::XCOORD}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString xstr = dataValues[X_COORDINATE]; + QString xstr = dataValues[Util::XCOORD]; m_selectionEnd = xstr.toDouble(); if ( m_plotGenerator ){ m_plotGenerator->setSelectionMode( false ); @@ -93,9 +93,9 @@ void Plot2DManager::endSelection(const QString& params ){ void Plot2DManager::endSelectionColor(const QString& params ){ - std::set keys = {X_COORDINATE}; + std::set keys = {Util::XCOORD}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString xstr = dataValues[X_COORDINATE]; + QString xstr = dataValues[Util::XCOORD]; m_selectionEnd = xstr.toDouble(); if ( m_plotGenerator ){ m_plotGenerator->setSelectionModeColor( false ); @@ -439,9 +439,9 @@ void Plot2DManager::setVLinePosition( double xPos ){ void Plot2DManager::startSelection(const QString& params ){ - std::set keys = {X_COORDINATE}; + std::set keys = {Util::XCOORD}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString xstr = dataValues[X_COORDINATE]; + QString xstr = dataValues[Util::XCOORD]; double proposedStart = xstr.toDouble(); bool onTarget = m_plotGenerator->isSelectionOnCanvas( proposedStart ); if ( onTarget ){ @@ -455,9 +455,9 @@ void Plot2DManager::startSelection(const QString& params ){ void Plot2DManager::startSelectionColor(const QString& params ){ - std::set keys = {X_COORDINATE}; + std::set keys = {Util::XCOORD}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString xstr = dataValues[X_COORDINATE]; + QString xstr = dataValues[Util::XCOORD]; m_selectionEnabledColor = true; m_selectionStart = xstr.toDouble(); if ( m_plotGenerator ){ diff --git a/carta/cpp/core/Data/Plotter/Plot2DManager.h b/carta/cpp/core/Data/Plotter/Plot2DManager.h index fe5916c6..91e2f21b 100755 --- a/carta/cpp/core/Data/Plotter/Plot2DManager.h +++ b/carta/cpp/core/Data/Plotter/Plot2DManager.h @@ -343,7 +343,6 @@ private slots: private: const static QString DATA_PATH; - const static QString X_COORDINATE; void _initializeDefaultState(); void _initializeCallbacks(); @@ -368,7 +367,7 @@ private slots: //everyone wants to listen to them. Carta::State::StateInterface m_stateMouse; Plot2DManager( const Plot2DManager& other); - Plot2DManager operator=( const Plot2DManager& other ); + Plot2DManager& operator=( const Plot2DManager& other ); }; } } diff --git a/carta/cpp/core/Data/Profile/CurveData.cpp b/carta/cpp/core/Data/Profile/CurveData.cpp index a404df06..68ca2490 100755 --- a/carta/cpp/core/Data/Profile/CurveData.cpp +++ b/carta/cpp/core/Data/Profile/CurveData.cpp @@ -1,9 +1,15 @@ #include "CurveData.h" -#include "Data/Util.h" +#include "CartaLib/Hooks/ConversionSpectralHook.h" +#include "Data/Error/ErrorManager.h" #include "Data/Plotter/LineStyles.h" #include "Data/Profile/ProfileStatistics.h" #include "Data/Profile/ProfilePlotStyles.h" +#include "Data/Units/UnitsFrequency.h" +#include "Data/Units/UnitsWavelength.h" +#include "Data/Util.h" #include "State/UtilState.h" + +#include "Globals.h" #include #include #include @@ -20,6 +26,9 @@ const QString CurveData::STATISTIC = "stat"; const QString CurveData::REGION_NAME = "region"; const QString CurveData::IMAGE_NAME = "image"; const QString CurveData::REST_FREQUENCY = "restFrequency"; +const QString CurveData::REST_FREQUENCY_UNITS = "restFrequencyUnits"; +const QString CurveData::REST_UNIT_FREQ = "restUnitFreq"; +const QString CurveData::REST_UNIT_WAVE = "restUnitWave"; class CurveData::Factory : public Carta::State::CartaObjectFactory { @@ -31,9 +40,12 @@ class CurveData::Factory : public Carta::State::CartaObjectFactory { bool CurveData::m_registered = Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new CurveData::Factory()); + LineStyles* CurveData::m_lineStyles = nullptr; ProfileStatistics* CurveData::m_stats = nullptr; ProfilePlotStyles* CurveData::m_plotStyles = nullptr; +UnitsFrequency* CurveData::m_frequencyUnits = nullptr; +UnitsWavelength* CurveData::m_wavelengthUnits = nullptr; using Carta::State::UtilState; using Carta::State::StateInterface; @@ -77,11 +89,41 @@ void CurveData::_calculateRelativeErrors( double& errorX, double& errorY ) const double dataMaxX = -1 * dataMinX; double dataMinY = std::numeric_limits::max();; double dataMaxY = -1 * dataMinY; - _getMinMax( &dataMinX, &dataMaxX, &dataMinY, &dataMaxY ); + getMinMax( &dataMinX, &dataMaxX, &dataMinY, &dataMaxY ); errorX = _calculateRelativeError( dataMinX, dataMaxX ); errorY = _calculateRelativeError( dataMinY, dataMaxY ); } +void CurveData::_convertRestFrequency( const QString& oldUnits, const QString& newUnits, + int significantDigits, double errorMargin ){ + //Do conversion + if ( m_imageSource ){ + std::vector converted(1); + converted[0] = m_state.getValue( REST_FREQUENCY ); + auto result = Globals::instance()-> pluginManager() + -> prepare (m_imageSource, oldUnits, newUnits, converted ); + auto lam = [&converted] ( const Carta::Lib::Hooks::ConversionSpectralHook::ResultType &data ) { + converted = data; + }; + try { + result.forEach( lam ); + if ( converted.size() > 0 ){ + double roundedFreq = Util::roundToDigits( converted[0], significantDigits ); + double oldFreq = m_state.getValue( REST_FREQUENCY ); + if ( qAbs( oldFreq - roundedFreq ) > errorMargin ){ + m_state.setValue(REST_FREQUENCY, roundedFreq ); + } + } + } + catch( char*& error ){ + QString errorStr( error ); + ErrorManager* hr = Util::findSingletonObject(); + hr->registerError( errorStr ); + } + } +} + + void CurveData::copy( const std::shared_ptr & other ){ if ( other ){ @@ -103,6 +145,8 @@ void CurveData::copy( const std::shared_ptr & other ){ } } + + QColor CurveData::getColor() const { int red = m_state.getValue( Util::RED ); int green = m_state.getValue( Util::GREEN ); @@ -153,12 +197,12 @@ QString CurveData::getCursorText( double x, double y, double* error ) const { } -double CurveData::getDataMax() const { - return m_plotDataX.size(); +std::shared_ptr CurveData::getImage() const { + return m_imageSource; } -void CurveData::_getMinMax(double* xmin, double* xmax, double* ymin, +void CurveData::getMinMax(double* xmin, double* xmax, double* ymin, double* ymax) const { int maxPoints = m_plotDataX.size(); for (int i = 0; i < maxPoints; ++i) { @@ -192,10 +236,43 @@ QString CurveData::getNameRegion() const { } +std::vector< std::pair > CurveData::getPlotData() const { + int dataCount = m_plotDataX.size(); + std::vector > data( dataCount ); + for ( int i = 0; i < dataCount; i++ ){ + data[i] = std::pair( m_plotDataX[i], m_plotDataY[i] ); + } + return data; +} + + +Carta::Lib::ProfileInfo CurveData::getProfileInfo() const { + Carta::Lib::ProfileInfo profInfo; + profInfo.setRestFrequency( getRestFrequency() ); + profInfo.setRestUnit( getRestUnits() ); + QString stat = getStatistic(); + Carta::Lib::ProfileInfo::AggregateType agType = m_stats ->getTypeFor( stat ); + profInfo.setAggregateType( agType ); + return profInfo; +} + + double CurveData::getRestFrequency() const { return m_state.getValue( REST_FREQUENCY ); } +QString CurveData::getRestUnits() const { + bool freqUnits = m_state.getValue( REST_FREQUENCY_UNITS ); + QString units; + if ( freqUnits ){ + units = m_state.getValue( REST_UNIT_FREQ ); + } + else { + units = m_state.getValue( REST_UNIT_WAVE ); + } + return units; +} + std::shared_ptr CurveData::getSource() const { return m_imageSource; @@ -231,6 +308,10 @@ void CurveData::_initializeDefaultState(){ m_state.insertValue( STATISTIC, m_stats->getDefault()); m_state.insertValue(REST_FREQUENCY, 0 ); + m_state.insertValue(REST_FREQUENCY_UNITS, true ); + m_state.insertValue(REST_UNIT_FREQ, m_frequencyUnits->getDefault()); + m_state.insertValue(REST_UNIT_WAVE, m_wavelengthUnits->getDefault()); + m_state.insertValue(IMAGE_NAME, ""); m_state.insertValue(REGION_NAME, ""); } @@ -246,6 +327,12 @@ void CurveData::_initializeStatics(){ if ( m_plotStyles == nullptr ){ m_plotStyles = Util::findSingletonObject(); } + if ( m_frequencyUnits == nullptr ){ + m_frequencyUnits = Util::findSingletonObject(); + } + if ( m_wavelengthUnits == nullptr ){ + m_wavelengthUnits = Util::findSingletonObject(); + } } bool CurveData::isMatch( const QString& name ) const { @@ -257,6 +344,24 @@ bool CurveData::isMatch( const QString& name ) const { } +void CurveData::resetRestFrequency(){ + bool restFreqSet = false; + setRestFrequency( m_restFrequency, 0, &restFreqSet ); + QString freqUnits = m_frequencyUnits->getActualUnits( m_restUnits ); + if ( !freqUnits.isEmpty() ){ + m_state.setValue(REST_FREQUENCY_UNITS, true ); + m_state.setValue( REST_UNIT_FREQ, freqUnits); + } + else { + QString waveUnits = m_wavelengthUnits->getActualUnits( m_restUnits ); + if ( !waveUnits.isEmpty()){ + m_state.setValue(REST_FREQUENCY_UNITS, false ); + m_state.setValue( REST_UNIT_WAVE, waveUnits ); + } + } +} + + void CurveData::setColor( QColor color ){ m_state.setValue( Util::RED, color.red() ); m_state.setValue( Util::GREEN, color.green() ); @@ -270,6 +375,73 @@ void CurveData::setData( const std::vector& valsX, const std::vector& valsX ){ + CARTA_ASSERT( m_plotDataY.size() == valsX.size()); + m_plotDataX = valsX; +} + +void CurveData::setDataY( const std::vector& valsY ){ + CARTA_ASSERT( m_plotDataX.size() == valsY.size()); + m_plotDataY = valsY; +} + + + +QString CurveData::setRestFrequency( double freq, double errorMargin, bool* valueChanged ){ + QString result; + *valueChanged = false; + if ( freq >= 0 ){ + double oldRestFrequency = m_state.getValue( REST_FREQUENCY ); + if ( qAbs( freq - oldRestFrequency ) > errorMargin ){ + *valueChanged = true; + m_state.setValue( REST_FREQUENCY, freq ); + } + } + else { + result = "Rest frequency must be nonnegative."; + } + return result; +} + +QString CurveData::setStatistic( const QString& stat ){ + QString result; + QString actualStat = m_stats->getActualStatistic( stat ); + if ( !actualStat.isEmpty() ){ + QString oldStat = m_state.getValue( STATISTIC ); + if ( oldStat != actualStat ){ + m_state.setValue( STATISTIC, actualStat ); + } + } + else { + result = "Unrecognized profile statistic: "+stat; + } + return result; +} + +void CurveData::setRestQuantity( double restFrequency, const QString& restUnit ){ + if ( restFrequency >= 0 ){ + m_restFrequency = restFrequency; + m_restUnits = restUnit; + QString actualUnit = m_frequencyUnits->getActualUnits( restUnit ); + bool restUnitType = true; + if ( actualUnit.isEmpty()){ + //Not a valid frequency unit, try a wavelength unit. + actualUnit = m_wavelengthUnits->getActualUnits( restUnit ); + restUnitType = false; + } + if ( !actualUnit.isEmpty() ){ + m_state.setValue(REST_FREQUENCY, restFrequency ); + m_state.setValue(REST_FREQUENCY_UNITS, restUnitType ); + if ( restUnitType ){ + m_state.setValue(REST_UNIT_FREQ, actualUnit); + } + else { + m_state.setValue(REST_UNIT_WAVE, actualUnit ); + } + } + } +} + QString CurveData::setLineStyle( const QString& lineStyle ){ QString result; @@ -330,6 +502,51 @@ QString CurveData::setPlotStyle( const QString& plotStyle ){ return result; } +QString CurveData::setRestUnits( const QString& restUnits, int significantDigits, double errorMargin){ + bool freqUnits = m_state.getValue( REST_FREQUENCY_UNITS ); + QString result; + QString actualUnits; + if ( freqUnits ){ + actualUnits = m_frequencyUnits->getActualUnits( restUnits ); + } + else { + actualUnits = m_wavelengthUnits ->getActualUnits( restUnits ); + } + if ( !actualUnits.isEmpty() ){ + QString oldUnits = getRestUnits(); + if ( actualUnits != oldUnits ){ + _convertRestFrequency( oldUnits, actualUnits, significantDigits, errorMargin ); + if ( freqUnits ){ + m_state.setValue( REST_UNIT_FREQ, actualUnits ); + } + else { + m_state.setValue( REST_UNIT_WAVE, actualUnits ); + } + } + } + else { + result = "Unrecognized rest units: " + restUnits; + } + return result; +} + +void CurveData::setRestUnitType( bool restUnitsFreq, int significantDigits, double errorMargin ){ + bool oldType = m_state.getValue( REST_FREQUENCY_UNITS ); + if ( oldType != restUnitsFreq ){ + m_state.setValue(REST_FREQUENCY_UNITS, restUnitsFreq ); + //Need to translate the old frequency, unit pair to a new one. + QString oldUnits = m_state.getValue( REST_UNIT_FREQ ); + QString newUnits = m_state.getValue( REST_UNIT_WAVE ); + if ( restUnitsFreq ){ + QString tmpUnits = oldUnits; + oldUnits = newUnits; + newUnits = tmpUnits; + } + _convertRestFrequency( oldUnits, newUnits, significantDigits, errorMargin ); + } +} + + void CurveData::setSource( std::shared_ptr imageSource ){ m_imageSource = imageSource; diff --git a/carta/cpp/core/Data/Profile/CurveData.h b/carta/cpp/core/Data/Profile/CurveData.h index 139c2534..c54aeb24 100755 --- a/carta/cpp/core/Data/Profile/CurveData.h +++ b/carta/cpp/core/Data/Profile/CurveData.h @@ -7,6 +7,7 @@ #include "State/ObjectManager.h" #include "State/StateInterface.h" #include "CartaLib/IImage.h" +#include "CartaLib/ProfileInfo.h" #include #include @@ -26,6 +27,8 @@ class LineStyles; class ProfileStatistics; class ProfilePlotStyles; class Region; +class UnitsFrequency; +class UnitsWavelength; class CurveData : public Carta::State::CartaObject { friend class Profiler; @@ -54,11 +57,13 @@ friend class Profiler; */ QString getCursorText( double x, double y, double* error) const; + + /** - * Return the maximum x- data value. - * @return - the maximum x- data value. + * Returns the image that was used in the profile calculation. + * @return - the image used in the profile calculation. */ - double getDataMax() const; + std::shared_ptr getImage() const; /** * Return an identifier for the style to use in drawing lines. @@ -66,6 +71,29 @@ friend class Profiler; */ QString getLineStyle() const; + /** + * Return the minimum and maximum x- and y-values of points on + * the curve. + * @param xmin - pointer to the curve minimum x-value. + * @param xmax - pointer to the curve maximum x-value. + * @param ymin - pointer to the curve minimum y-value. + * @param ymax - pointer to the curve maximum y-value. + */ + void getMinMax(double* xmin, double* xmax, double* ymin, + double* ymax ) const; + + /** + * Return the curve data. + * @return - the (x,y) pairs that make up the plot curve. + */ + std::vector< std::pair > getPlotData() const; + + /** + * Return information for calculating a profile. + * @return - information for calculating a profile. + */ + Carta::Lib::ProfileInfo getProfileInfo() const; + /** * Return an identifier for the curve. @@ -91,6 +119,12 @@ friend class Profiler; */ double getRestFrequency() const; + /** + * Return the units of rest frequency. + * @return - the rest frequency units. + */ + QString getRestUnits() const; + /** * Return the internal state of the curve as a string. * @return - the curve state. @@ -129,6 +163,11 @@ friend class Profiler; */ bool isMatch( const QString& name ) const; + /** + * Set the rest frequency back to its original value. + */ + void resetRestFrequency(); + /** * Set the color to use in plotting the points of the curve. * @param color - the color to use in plotting curve points. @@ -142,6 +181,18 @@ friend class Profiler; */ void setData( const std::vector& valsX, const std::vector& valsY ); + /** + * Set the x-values that comprise the curve. + * @param valsX - the x-coordinate values of the curve. + */ + void setDataX( const std::vector& valsX ); + + /** + * Set the y- data values that comprise the curve. + * @param valsY - the y-coordinate values of the curve. + */ + void setDataY( const std::vector& valsY ); + /** * Set the name of the layer that is the source of profile. * @param imageName - an identifier for the layer that is the source of @@ -162,8 +213,59 @@ friend class Profiler; */ QString setName( const QString& curveName ); + /** + * Set the line style to use in plotting the profile curve. + * @param plotStyle - the line style to be used in plotting the profile + * curve. + * @return - an error message if the plotting style could not be set; otherwise, + * an empty string. + */ QString setPlotStyle( const QString& plotStyle ); + /** + * Set the rest frequency to be used in calculating the profile. + * @param freq - the rest frequency to use in calculating the profile. + * @param errorMargin - how far the new frequncy needs to be from the old one in + * order to be recognized as different. + * @param valueChanged - set to true if the stored frequency value was changed. + * @return - an error message if the rest frequency could not be set; otherwise, + * an empty string. + */ + QString setRestFrequency( double freq, double errorMargin, bool* valueChanged ); + + /** + * Set the rest frequency and units that were used in calculating the profile. + * @param restFrequency - the rest frequency used in the calculation. + * @param restUnit - the units of rest frequency. + */ + void setRestQuantity( double restFrequency, const QString& restUnit ); + + /** + * Set the units of rest frequency. + * @param restUnits - the rest frequency units. + * @param significantDigits - the number of significant digits to store. + * @param errorMargin - required difference before deciding a value is significantly different. + * @return - an error message if the rest frequency units could not be set; + * otherwise, and empty string. + */ + QString setRestUnits( const QString& restUnits, int significantDigits, double errorMargin); + + /** + * Set whether the rest units are frequency or wavelength. + * @param restUnitsFreq - true if rest frequency is specified as frequency; false, + * if it is specified as wavelength. + * @param significantDigits - the number of significant digits to store. + * @param errorMargin - required difference before deciding a value is significantly different. + */ + void setRestUnitType( bool restUnitsFreq, int significantDigits, double errorMargin ); + + /** + * Set the method used to summarize profile points. + * @param stat - the method used to summarize profile points. + * @return - an error message if the method was unrecognized; an empty string otherwise. + */ + QString setStatistic( const QString& stat ); + /** * Set the image that was used to generate the curve. * @param imageSource - the image that was used to generate the curve. @@ -182,21 +284,27 @@ friend class Profiler; const static QString REGION_NAME; const static QString IMAGE_NAME; const static QString REST_FREQUENCY; + const static QString REST_FREQUENCY_UNITS; + const static QString REST_UNIT_FREQ; + const static QString REST_UNIT_WAVE; double _calculateRelativeError( double minValue, double maxValue ) const; void _calculateRelativeErrors( double& errorX, double& errorY ) const; - void _getMinMax(double* xmin, double* xmax, double* ymin, - double* ymax ) const; + void _convertRestFrequency( const QString& oldUnits, const QString& newUnits, + int significantDigits, double errorMargin ); + void _initializeDefaultState(); void _initializeStatics(); - void _saveCurve(); + static bool m_registered; static LineStyles* m_lineStyles; static ProfileStatistics* m_stats; static ProfilePlotStyles* m_plotStyles; + static UnitsFrequency* m_frequencyUnits; + static UnitsWavelength* m_wavelengthUnits; CurveData( const QString& path, const QString& id ); class Factory; @@ -204,10 +312,14 @@ friend class Profiler; std::vector m_plotDataX; std::vector m_plotDataY; std::shared_ptr m_region; + + double m_restFrequency; + QString m_restUnits; + std::shared_ptr m_imageSource; CurveData( const CurveData& other); - CurveData operator=( const CurveData& other ); + CurveData& operator=( const CurveData& other ); }; } } diff --git a/carta/cpp/core/Data/Profile/GenerateModes.cpp b/carta/cpp/core/Data/Profile/GenerateModes.cpp index e358edf5..895fa7c2 100644 --- a/carta/cpp/core/Data/Profile/GenerateModes.cpp +++ b/carta/cpp/core/Data/Profile/GenerateModes.cpp @@ -10,7 +10,7 @@ const QString GenerateModes::GEN_LIST = "genModes"; const QString GenerateModes::CLASS_NAME = "GenerateModes"; const QString GenerateModes::GEN_ALL = "All"; const QString GenerateModes::GEN_CURRENT = "Current"; -const QString GenerateModes::GEN_CUSTOM = "Custom"; +//const QString GenerateModes::GEN_CUSTOM = "Custom"; const QString GenerateModes::GEN_NO_SINGLE_PLANES = "Exclude Single Plane"; @@ -59,12 +59,12 @@ QString GenerateModes::getDefault() const { void GenerateModes::_initializeDefaultState(){ - m_state.insertArray( GEN_LIST, 4 ); + m_state.insertArray( GEN_LIST, 3 ); int i = 0; _initMode( &i, GEN_CURRENT ); _initMode( &i, GEN_ALL ); - _initMode( &i, GEN_CUSTOM ); + // _initMode( &i, GEN_CUSTOM ); _initMode( &i, GEN_NO_SINGLE_PLANES ); m_state.flushState(); diff --git a/carta/cpp/core/Data/Profile/GenerateModes.h b/carta/cpp/core/Data/Profile/GenerateModes.h index 473778e5..71bf32a9 100644 --- a/carta/cpp/core/Data/Profile/GenerateModes.h +++ b/carta/cpp/core/Data/Profile/GenerateModes.h @@ -69,7 +69,7 @@ class GenerateModes : public Carta::State::CartaObject { const static QString GEN_CURRENT; const static QString GEN_ALL; - const static QString GEN_CUSTOM; + //const static QString GEN_CUSTOM; const static QString GEN_NO_SINGLE_PLANES; void _initializeDefaultState(); diff --git a/carta/cpp/core/Data/Profile/IntensityUnits.cpp b/carta/cpp/core/Data/Profile/IntensityUnits.cpp deleted file mode 100644 index 7117dd8c..00000000 --- a/carta/cpp/core/Data/Profile/IntensityUnits.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "IntensityUnits.h" -#include "CartaLib/CartaLib.h" -#include "State/UtilState.h" -#include -#include - -namespace Carta { - -namespace Data { - -const QString IntensityUnits::UNIT_LIST = "intensityUnits"; -const QString IntensityUnits::CLASS_NAME = "IntensityUnits"; -const QString IntensityUnits::NAME_PEAK = "Fraction of Peak"; -const QString IntensityUnits::NAME_JYBEAM = "Jy/beam"; -const QString IntensityUnits::NAME_JYSR = "MJy/sr"; -const QString IntensityUnits::NAME_JYARCSEC = "Jy/arcsec^2"; -const QString IntensityUnits::NAME_JY = "Jy"; -const QString IntensityUnits::NAME_KELVIN = "Kelvin"; - - -class IntensityUnits::Factory : public Carta::State::CartaObjectFactory { -public: - - Factory(): - CartaObjectFactory(CLASS_NAME){ - }; - - Carta::State::CartaObject * create (const QString & path, const QString & id) - { - return new IntensityUnits (path, id); - } -}; - - - -bool IntensityUnits::m_registered = - Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new IntensityUnits::Factory()); - -IntensityUnits::IntensityUnits( const QString& path, const QString& id): - CartaObject( CLASS_NAME, path, id ){ - _initializeDefaultState(); -} - -QString IntensityUnits::getActualUnits( const QString& unitStr ) const { - QString actualUnits; - int dataCount = m_state.getArraySize( UNIT_LIST ); - for ( int i = 0; i < dataCount; i++ ){ - QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); - QString unitName = m_state.getValue( key ); - int result = QString::compare( unitName, unitStr, Qt::CaseInsensitive ); - if ( result == 0 ){ - actualUnits = unitName; - break; - } - } - return actualUnits; -} - - -QString IntensityUnits::getDefault() const { - return NAME_JYBEAM; -} - -void IntensityUnits::_initUnit( int * index, const QString& name ){ - QString key = Carta::State::UtilState::getLookup( UNIT_LIST, *index ); - m_state.setValue( key, name ); - *index = *index + 1; -} - -void IntensityUnits::_initializeDefaultState(){ - m_state.insertArray( UNIT_LIST, 5 ); - int i = 0; - - _initUnit( &i, NAME_JYBEAM ); - _initUnit( &i, NAME_JYSR ); - _initUnit( &i, NAME_JYARCSEC ); - _initUnit( &i, NAME_JY ); - _initUnit( &i, NAME_KELVIN ); - - m_state.flushState(); -} - - -IntensityUnits::~IntensityUnits(){ - -} -} -} diff --git a/carta/cpp/core/Data/Profile/ProfileRenderService.cpp b/carta/cpp/core/Data/Profile/ProfileRenderService.cpp new file mode 100644 index 00000000..912a673b --- /dev/null +++ b/carta/cpp/core/Data/Profile/ProfileRenderService.cpp @@ -0,0 +1,96 @@ +#include "ProfileRenderService.h" +#include "ProfileRenderWorker.h" +#include "ProfileRenderThread.h" +#include "CartaLib/Hooks/ProfileHook.h" + +namespace Carta { +namespace Data { + +ProfileRenderService::ProfileRenderService( QObject * parent ) : + QObject( parent ), + m_worker( nullptr), + m_renderThread( nullptr ){ + m_renderQueued = false; +} + + +bool ProfileRenderService::renderProfile(std::shared_ptr dataSource, + Carta::Lib::RegionInfo& regionInfo, Carta::Lib::ProfileInfo& profInfo, + int curveIndex, const QString& layerName, bool createNew ){ + bool profileRender = true; + if ( dataSource ){ + RenderRequest request; + request.m_image = dataSource; + request.m_regionInfo = regionInfo; + request.m_profileInfo = profInfo; + request.m_curveIndex = curveIndex; + request.m_layerName = layerName; + request.m_createNew = createNew; + m_requests.enqueue( request ); + _scheduleRender( dataSource, regionInfo, profInfo ); + } + else { + profileRender = false; + } + return profileRender; +} + + +void ProfileRenderService::_scheduleRender( std::shared_ptr dataSource, + Carta::Lib::RegionInfo& regionInfo, Carta::Lib::ProfileInfo& profInfo){ + + if ( m_renderQueued ) { + return; + } + m_renderQueued = true; + + + //Create a worker if we don't have one. + if ( !m_worker ){ + m_worker = new ProfileRenderWorker(); + } + bool paramsChanged = m_worker->setParameters( dataSource, regionInfo, profInfo ); + if ( paramsChanged ){ + + int pid = m_worker->computeProfile(); + if ( pid != -1 ){ + if ( !m_renderThread ){ + m_renderThread = new ProfileRenderThread( pid ); + connect( m_renderThread, SIGNAL(finished()), this, SLOT( _postResult())); + } + else { + m_renderThread->setFileDescriptor( pid ); + } + m_renderThread->start(); + } + else { + qDebug() << "Bad file descriptor: "<getResult(); + RenderRequest request = m_requests.dequeue(); + emit profileResult( result, request.m_curveIndex, request.m_layerName, + request.m_createNew, request.m_image ); + m_renderQueued = false; + if ( m_requests.size() > 0 ){ + RenderRequest& head = m_requests.head(); + _scheduleRender( head.m_image, + head.m_regionInfo, head.m_profileInfo ); + } +} + + +ProfileRenderService::~ProfileRenderService(){ + delete m_worker; + delete m_renderThread; +} +} +} + diff --git a/carta/cpp/core/Data/Profile/ProfileRenderService.h b/carta/cpp/core/Data/Profile/ProfileRenderService.h new file mode 100644 index 00000000..5eceb7e0 --- /dev/null +++ b/carta/cpp/core/Data/Profile/ProfileRenderService.h @@ -0,0 +1,98 @@ +/** + * Manages the production of profile data from an image cube. + **/ + +#pragma once + + +#include "CartaLib/CartaLib.h" +#include "CartaLib/RegionInfo.h" +#include "CartaLib/Hooks/ProfileResult.h" + +#include +#include +#include + +namespace Carta { +namespace Lib { + +namespace Image { +class ImageInterface; +} +} +} + +namespace Carta{ +namespace Data{ + +class ProfileRenderWorker; +class ProfileRenderThread; + +class ProfileRenderService : public QObject { + Q_OBJECT + +public: + + /** + * Constructor. + */ + explicit ProfileRenderService( QObject * parent = 0 ); + + /** + * Initiates the process of rendering the Profile. + * @param dataSource - the image that will be the source of the profile. + * @param regionInfo - information about the region within the image that will be profiled. + * @param profInfo - information about the profile to be rendered such as rest frequency. + * @param curveIndex - the profile curve to be rendered. + * @param layerName - the name of the layer responsible for the profile. + * @param createNew - whether this is a new profile or a replacement for an existing one. + * @return - whether or not the profile is being rendered. + */ + bool renderProfile(std::shared_ptr dataSource, + Carta::Lib::RegionInfo& regionInfo, Carta::Lib::ProfileInfo& profInfo, + int curveIndex, const QString& layerName, bool createNew ); + + /** + * Destructor. + */ + ~ProfileRenderService(); + +signals: + + /** + * Notification that new Profile data has been computed. + */ + void profileResult( const Carta::Lib::Hooks::ProfileResult&, + int curveIndex, const QString& layerName, bool createNew, + std::shared_ptr image ); + +private slots: + + void _postResult( ); + +private: + void _scheduleRender( std::shared_ptr dataSource, + Carta::Lib::RegionInfo& regionInfo, Carta::Lib::ProfileInfo& profInfo ); + ProfileRenderWorker* m_worker; + ProfileRenderThread* m_renderThread; + bool m_renderQueued; + + + struct RenderRequest { + bool m_createNew; + int m_curveIndex; + QString m_layerName; + Carta::Lib::ProfileInfo m_profileInfo; + Carta::Lib ::RegionInfo m_regionInfo; + std::shared_ptr m_image; + }; + QQueue m_requests; + + ProfileRenderService( const ProfileRenderService& other); + ProfileRenderService& operator=( const ProfileRenderService& other ); +}; +} +} + + + diff --git a/carta/cpp/core/Data/Profile/ProfileRenderThread.cpp b/carta/cpp/core/Data/Profile/ProfileRenderThread.cpp new file mode 100644 index 00000000..c2cde937 --- /dev/null +++ b/carta/cpp/core/Data/Profile/ProfileRenderThread.cpp @@ -0,0 +1,44 @@ +#include "ProfileRenderThread.h" +#include +#include +#include + +namespace Carta +{ +namespace Data +{ + +ProfileRenderThread::ProfileRenderThread( int pipeFd, QObject* parent ): + QThread( parent ){ + m_fileDescriptor = pipeFd; +} + +Carta::Lib::Hooks::ProfileResult ProfileRenderThread::getResult() const { + return m_result; +} + + +void ProfileRenderThread::run(){ + QFile file; + if ( !file.open( m_fileDescriptor, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle ) ){ + QString errorStr("Could not read Profile results"); + qDebug() << errorStr; + m_result.setError( errorStr ); + } + else { + QDataStream dataStream( & file ); + dataStream >> m_result; + file.close(); + } +} + +void ProfileRenderThread::setFileDescriptor( int fileDescriptor ){ + m_fileDescriptor = fileDescriptor; +} + + +ProfileRenderThread::~ProfileRenderThread(){ +} +} +} + diff --git a/carta/cpp/core/Data/Profile/ProfileRenderThread.h b/carta/cpp/core/Data/Profile/ProfileRenderThread.h new file mode 100644 index 00000000..92481c3f --- /dev/null +++ b/carta/cpp/core/Data/Profile/ProfileRenderThread.h @@ -0,0 +1,63 @@ +/** + * A thread that blocks until the Profile data has been computed and is + * available for reading. + **/ + +#pragma once + +#include "CartaLib/Hooks/ProfileResult.h" +#include + + +namespace Carta{ +namespace Data{ + +class ProfileRenderThread : public QThread { + + Q_OBJECT; + +public: + + /** + * Constructor. + * @param pipeFileDescriptor - the file descriptor for the pipe where the + * Profile data should be read. + * @param parent - the parent object. + */ + ProfileRenderThread( int pipeFileDescriptor, QObject* parent = nullptr); + + /** + * Returns the Profile data. + * @return - the computed data for a Profile plot. + */ + Carta::Lib::Hooks::ProfileResult getResult() const; + + + /** + * Run the thread. + */ + void run(); + + /** + * Set a new read file descriptor. + * @param fileDescriptor - a new read file descriptor. + */ + void setFileDescriptor( int fileDescriptor ); + + /** + * Destructor. + */ + ~ProfileRenderThread(); + +private: + int m_fileDescriptor; + Carta::Lib::Hooks::ProfileResult m_result; + + ProfileRenderThread( const ProfileRenderThread& other); + ProfileRenderThread& operator=( const ProfileRenderThread& other ); +}; +} +} + + + diff --git a/carta/cpp/core/Data/Profile/ProfileRenderWorker.cpp b/carta/cpp/core/Data/Profile/ProfileRenderWorker.cpp new file mode 100644 index 00000000..85cdf815 --- /dev/null +++ b/carta/cpp/core/Data/Profile/ProfileRenderWorker.cpp @@ -0,0 +1,92 @@ +#include "ProfileRenderWorker.h" +#include "Globals.h" +#include "PluginManager.h" +#include "CartaLib/Hooks/ProfileHook.h" +#include +#include +#include +#include + +namespace Carta +{ +namespace Data +{ + +ProfileRenderWorker::ProfileRenderWorker(){ +} + + +bool ProfileRenderWorker::setParameters(std::shared_ptr dataSource, + Carta::Lib::RegionInfo& regionInfo, Carta::Lib::ProfileInfo& profInfo ){ + bool paramsChanged = false; + if ( m_regionInfo != regionInfo ){ + m_regionInfo = regionInfo; + paramsChanged = true; + } + if ( m_profileInfo != profInfo ){ + m_profileInfo = profInfo; + paramsChanged = true; + } + if ( m_dataSource.get() != dataSource.get() ){ + m_dataSource = dataSource; + paramsChanged = true; + } + return paramsChanged; +} + + +int ProfileRenderWorker::computeProfile(){ + //Note: we compute the Profile in a separate process because casacore tables + //cannot be accessed by different threads at the same time. + int ProfilePipes [2]; + if (pipe (ProfilePipes)){ + qDebug() << "*** ProfileRenderWorker::run: pipe creation failed: " << strerror (errno); + return -1; + } + + int pid = fork (); + if (pid == -1){ + // Failure + qDebug() << "*** ProfileRenderWorker::run: fork failed: " << strerror (errno); + return -1; + + } + else if (pid != 0){ + // The original process comes here. + close (ProfilePipes [1]); // close write end of the pipe + return ProfilePipes [0]; // return the read end of the pipe + } + + // We're our own process + auto result = Globals::instance()-> pluginManager() + -> prepare (m_dataSource, m_regionInfo, + m_profileInfo); + auto lam = [=] ( const Carta::Lib::Hooks::ProfileResult &data ) { + m_result = data; + }; + try { + result.forEach( lam ); + } + catch( char*& error ){ + qDebug() << "ProfileRenderWorker::run: caught error: " << error; + m_result.setError( QString(error) ); + } + + QFile file; + if ( !file.open( ProfilePipes[1], QIODevice::WriteOnly, QFileDevice::AutoCloseHandle ) ){ + qDebug() << "Could not write Profile results"; + return 0; + } + QDataStream dataStream( &file ); + dataStream << m_result; + file.close(); + exit(0); + return 0; +} + + +ProfileRenderWorker::~ProfileRenderWorker(){ +} +} +} + diff --git a/carta/cpp/core/Data/Profile/ProfileRenderWorker.h b/carta/cpp/core/Data/Profile/ProfileRenderWorker.h new file mode 100644 index 00000000..223796bb --- /dev/null +++ b/carta/cpp/core/Data/Profile/ProfileRenderWorker.h @@ -0,0 +1,70 @@ +/** + * Initiates the work of computing the Profile in a separate process. + **/ + +#pragma once + +#include +#include "CartaLib/Hooks/ProfileResult.h" +#include "CartaLib/RegionInfo.h" +#include "CartaLib/ProfileInfo.h" + +namespace Carta { +namespace Lib { + + +namespace Image { +class ImageInterface; +} +} +} + +namespace Carta{ +namespace Data{ + +class ProfileRenderWorker{ + +public: + + /** + * Constructor. + */ + ProfileRenderWorker(); + + /** + * Store the parameters needed for computing the Profile. + * @param dataSource - the image that will be the source of the Profile. + * @param regionInfo - information about the region used to generate the profile. + * @param profInfo - information about the profile to be generated. + * @return - true if the parameters have changed since the last computation; false, + * otherwise. + */ + bool setParameters(std::shared_ptr dataSource, + Carta::Lib::RegionInfo& regionInfo, Carta::Lib::ProfileInfo& profInfo ); + + /** + * Performs the work of computing the Profile data in a separate process. + * @return - the file descriptor for the pipe that can be used to read the + * Profile data. + */ + int computeProfile(); + + /** + * Destructor. + */ + ~ProfileRenderWorker(); + +private: + std::shared_ptr m_dataSource; + Carta::Lib::RegionInfo m_regionInfo; + Carta::Lib::ProfileInfo m_profileInfo; + Carta::Lib::Hooks::ProfileResult m_result; + + ProfileRenderWorker( const ProfileRenderWorker& other); + ProfileRenderWorker& operator=( const ProfileRenderWorker& other ); +}; +} +} + + + diff --git a/carta/cpp/core/Data/Profile/ProfileStatistics.cpp b/carta/cpp/core/Data/Profile/ProfileStatistics.cpp index ad6ef10b..cf5a2b96 100644 --- a/carta/cpp/core/Data/Profile/ProfileStatistics.cpp +++ b/carta/cpp/core/Data/Profile/ProfileStatistics.cpp @@ -64,6 +64,39 @@ QString ProfileStatistics::getDefault() const { return STAT_MEAN; } + +Carta::Lib::ProfileInfo::AggregateType ProfileStatistics::getTypeFor( const QString& statStr ) const { + Carta::Lib::ProfileInfo::AggregateType agType = Carta::Lib::ProfileInfo::AggregateType::OTHER; + QString actualStat = getActualStatistic( statStr ); + if ( !actualStat.isEmpty() ){ + if ( statStr == STAT_MEAN ){ + agType = Carta::Lib::ProfileInfo::AggregateType::MEAN; + } + if ( statStr == STAT_MEDIAN ){ + agType = Carta::Lib::ProfileInfo::AggregateType::MEDIAN; + } + else if ( statStr == STAT_SUM ){ + agType = Carta::Lib::ProfileInfo::AggregateType::SUM; + } + else if ( statStr == STAT_VARIANCE ){ + agType = Carta::Lib::ProfileInfo::AggregateType::VARIANCE; + } + else if ( statStr == STAT_MIN ){ + agType = Carta::Lib::ProfileInfo::AggregateType::MIN; + } + else if ( statStr == STAT_MAX ){ + agType = Carta::Lib::ProfileInfo::AggregateType::MAX; + } + else if ( statStr == STAT_RMS ){ + agType = Carta::Lib::ProfileInfo::AggregateType::RMS; + } + else if ( statStr == STAT_FLUX_DENSITY ){ + agType = Carta::Lib::ProfileInfo::AggregateType::FLUX_DENSITY; + } + } + return agType; +} + void ProfileStatistics::_initStat( int * index, const QString& name ){ QString key = Carta::State::UtilState::getLookup( STAT_LIST, *index ); m_state.setValue( key, name ); @@ -86,6 +119,34 @@ void ProfileStatistics::_initializeDefaultState(){ m_state.flushState(); } +QString ProfileStatistics::typeToString( Carta::Lib::ProfileInfo::AggregateType agType ) const { + QString statStr; + if ( agType == Carta::Lib::ProfileInfo::AggregateType::MEAN ){ + statStr = STAT_MEAN; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::MEDIAN ){ + statStr = STAT_MEDIAN; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::SUM ){ + statStr = STAT_SUM; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::VARIANCE ){ + statStr = STAT_VARIANCE; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::MIN ){ + statStr = STAT_MIN; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::MAX ){ + statStr = STAT_MAX; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::RMS ){ + statStr = STAT_RMS; + } + else if ( agType == Carta::Lib::ProfileInfo::AggregateType::FLUX_DENSITY ){ + statStr = STAT_FLUX_DENSITY; + } + return statStr; +} ProfileStatistics::~ProfileStatistics(){ diff --git a/carta/cpp/core/Data/Profile/ProfileStatistics.h b/carta/cpp/core/Data/Profile/ProfileStatistics.h index 2950ff05..4aa6050e 100644 --- a/carta/cpp/core/Data/Profile/ProfileStatistics.h +++ b/carta/cpp/core/Data/Profile/ProfileStatistics.h @@ -7,6 +7,7 @@ #include "State/ObjectManager.h" #include "State/StateInterface.h" +#include "CartaLib/ProfileInfo.h" #include namespace Carta { @@ -35,6 +36,21 @@ class ProfileStatistics : public Carta::State::CartaObject { */ QString getActualStatistic( const QString& statStr ) const; + + /** + * Returns the aggregate type corresponding to the string description. + * @param statStr - an identifier for a method of computing profiles. + * @return - the corresponding aggregate type. + */ + Carta::Lib::ProfileInfo::AggregateType getTypeFor( const QString& statStr ) const; + + /** + * Returns a string description of the aggregate type. + * @param agType - a method for generating profiles. + * @return - a string descriptor of the method. + */ + QString typeToString( Carta::Lib::ProfileInfo::AggregateType agType ) const; + const static QString CLASS_NAME; const static QString STAT_LIST; diff --git a/carta/cpp/core/Data/Profile/Profiler.cpp b/carta/cpp/core/Data/Profile/Profiler.cpp index 13e96906..516b5d5c 100755 --- a/carta/cpp/core/Data/Profile/Profiler.cpp +++ b/carta/cpp/core/Data/Profile/Profiler.cpp @@ -1,7 +1,5 @@ #include "Profiler.h" #include "CurveData.h" -#include "IntensityUnits.h" -#include "SpectralUnits.h" #include "GenerateModes.h" #include "ProfilePlotStyles.h" #include "Data/Clips.h" @@ -16,9 +14,12 @@ #include "Data/Plotter/LegendLocations.h" #include "Data/Plotter/Plot2DManager.h" #include "Data/Plotter/LineStyles.h" - +#include "Data/Profile/ProfileRenderService.h" +#include "Data/Profile/ProfileStatistics.h" +#include "Data/Units/UnitsFrequency.h" +#include "Data/Units/UnitsIntensity.h" +#include "Data/Units/UnitsSpectral.h" #include "Plot2D/Plot2DGenerator.h" - #include "CartaLib/Hooks/Plot2DResult.h" #include "CartaLib/Hooks/ConversionIntensityHook.h" #include "CartaLib/Hooks/ConversionSpectralHook.h" @@ -28,6 +29,7 @@ #include "State/UtilState.h" #include "Globals.h" #include "PluginManager.h" +#include #include namespace Carta { @@ -55,8 +57,6 @@ const QString Profiler::ZOOM_MIN = "zoomMin"; const QString Profiler::ZOOM_MAX = "zoomMax"; const QString Profiler::ZOOM_MIN_PERCENT = "zoomMinPercent"; const QString Profiler::ZOOM_MAX_PERCENT = "zoomMaxPercent"; -const double Profiler::ERROR_MARGIN = 0.000001; - class Profiler::Factory : public Carta::State::CartaObjectFactory { @@ -69,9 +69,10 @@ class Profiler::Factory : public Carta::State::CartaObjectFactory { bool Profiler::m_registered = Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new Profiler::Factory()); -SpectralUnits* Profiler::m_spectralUnits = nullptr; -IntensityUnits* Profiler::m_intensityUnits = nullptr; +UnitsSpectral* Profiler::m_spectralUnits = nullptr; +UnitsIntensity* Profiler::m_intensityUnits = nullptr; GenerateModes* Profiler::m_generateModes = nullptr; +ProfileStatistics* Profiler::m_stats = nullptr; QList Profiler::m_curveColors = {Qt::blue, Qt::green, Qt::black, Qt::cyan, @@ -89,12 +90,18 @@ Profiler::Profiler( const QString& path, const QString& id): m_preferences( nullptr), m_plotManager( new Plot2DManager( path, id ) ), m_legendLocations( nullptr), - m_stateData( UtilState::getLookup(path, StateInterface::STATE_DATA)){ + m_stateData( UtilState::getLookup(path, StateInterface::STATE_DATA) ), + m_renderService( new ProfileRenderService() ){ m_oldFrame = 0; m_currentFrame = 0; m_timerId = 0; + connect( m_renderService.get(), + SIGNAL(profileResult(const Carta::Lib::Hooks::ProfileResult&,int,const QString&,bool,std::shared_ptr)), + this, + SLOT(_profileRendered(const Carta::Lib::Hooks::ProfileResult&,int,const QString&,bool, std::shared_ptr))); + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); Settings* prefObj = objMan->createObject(); m_preferences.reset( prefObj ); @@ -113,6 +120,8 @@ Profiler::Profiler( const QString& path, const QString& id): _initializeDefaultState(); _initializeCallbacks(); + _setErrorMargin(); + m_controllerLinked = false; } @@ -226,17 +235,14 @@ void Profiler::_clearData(){ } std::vector Profiler::_convertUnitsX( std::shared_ptr curveData, - const QString& newUnit ) const { - QString bottomUnit = newUnit; - if ( newUnit.isEmpty() ){ - bottomUnit = m_state.getValue( AXIS_UNITS_BOTTOM ); - } + const QString& bottomUnit ) const { + QString oldBottomUnit = m_state.getValue( AXIS_UNITS_BOTTOM ); std::vector converted = curveData->getValuesX(); std::shared_ptr dataSource = curveData->getSource(); - if ( ! m_bottomUnit.isEmpty() ){ - if ( bottomUnit != m_bottomUnit ){ - QString oldUnit = _getUnitUnits( m_bottomUnit ); - QString newUnit = _getUnitUnits( bottomUnit ); + if ( ! bottomUnit.isEmpty() ){ + QString newUnit = _getUnitUnits( bottomUnit ); + QString oldUnit = _getUnitUnits( oldBottomUnit ); + if ( newUnit != oldUnit ){ _convertX ( converted, dataSource, oldUnit, newUnit ); } } @@ -265,41 +271,37 @@ void Profiler::_convertX( std::vector& converted, } -std::vector Profiler::_convertUnitsY( std::shared_ptr curveData ) const { +std::vector Profiler::_convertUnitsY( std::shared_ptr curveData, const QString& newUnit ) const { std::vector converted = curveData->getValuesY(); std::vector plotDataX = curveData->getValuesX(); QString leftUnit = m_state.getValue( AXIS_UNITS_LEFT ); - if ( ! m_leftUnit.isEmpty() ){ - Controller* controller = _getControllerSelected(); - if ( controller ){ - if ( leftUnit != m_leftUnit ){ - std::shared_ptr dataSource = - curveData->getSource(); - if ( dataSource ){ - //First, we need to make sure the x-values are in Hertz. - QString hertzKey = SpectralUnits::NAME_FREQUENCY + "(" + SpectralUnits::UNIT_HZ + ")"; - std::vector hertzVals = _convertUnitsX( curveData, hertzKey ); - bool validBounds = false; - std::pair boundsY = m_plotManager->getPlotBoundsY( curveData->getName(), &validBounds ); - if ( validBounds ){ - QString maxUnit = m_plotManager->getAxisUnitsY(); - auto result = Globals::instance()-> pluginManager() - -> prepare (dataSource, - m_leftUnit, leftUnit, hertzVals, converted, - boundsY.second, maxUnit );; - - auto lam = [&converted] ( const Carta::Lib::Hooks::ConversionIntensityHook::ResultType &data ) { - converted = data; - }; - try { - result.forEach( lam ); - } - catch( char*& error ){ - QString errorStr( error ); - ErrorManager* hr = Util::findSingletonObject(); - hr->registerError( errorStr ); - } - } + Controller* controller = _getControllerSelected(); + if ( controller ){ + std::shared_ptr dataSource = + curveData->getSource(); + if ( dataSource ){ + //First, we need to make sure the x-values are in Hertz. + QString hertzKey = UnitsSpectral::NAME_FREQUENCY + "(" + UnitsFrequency::UNIT_HZ + ")"; + std::vector hertzVals = _convertUnitsX( curveData, hertzKey ); + bool validBounds = false; + std::pair boundsY = m_plotManager->getPlotBoundsY( curveData->getName(), &validBounds ); + if ( validBounds ){ + QString maxUnit = m_plotManager->getAxisUnitsY(); + auto result = Globals::instance()-> pluginManager() + -> prepare (dataSource, + leftUnit, newUnit, hertzVals, converted, + boundsY.second, maxUnit );; + + auto lam = [&converted] ( const Carta::Lib::Hooks::ConversionIntensityHook::ResultType &data ) { + converted = data; + }; + try { + result.forEach( lam ); + } + catch( char*& error ){ + QString errorStr( error ); + ErrorManager* hr = Util::findSingletonObject(); + hr->registerError( errorStr ); } } } @@ -322,9 +324,7 @@ void Profiler::_cursorUpdate( double x, double y ){ } } } - //if ( !cursorText.isEmpty() ){ - m_plotManager->setCursorText( cursorText ); - //} + m_plotManager->setCursorText( cursorText ); } @@ -343,44 +343,75 @@ int Profiler::_findCurveIndex( const QString& name ) const { void Profiler::_generateData( std::shared_ptr layer, bool createNew ){ QString layerName = layer->_getLayerName(); + int curveIndex = _findCurveIndex( layerName ); std::shared_ptr image = layer->_getImage(); + _generateData( image, curveIndex, layerName, createNew ); +} + + + + +void Profiler::_generateData( std::shared_ptr image, + int curveIndex, const QString& layerName, bool createNew ){ std::vector < int > pos( image-> dims().size(), 0 ); int axis = _getExtractionAxisIndex( image ); if ( axis >= 0 ){ - Profiles::PrincipalAxisProfilePath path( axis, pos ); + //Profiles::PrincipalAxisProfilePath path( axis, pos ); Carta::Lib::ProfileInfo profInfo; + if ( curveIndex >= 0 ){ + profInfo = m_plotCurves[curveIndex]->getProfileInfo(); + } + QString bottomUnits = getAxisUnitsBottom(); + + profInfo.setSpectralUnit( _getUnitUnits( bottomUnits) ); + QString typeStr = _getUnitType( bottomUnits ); + if ( typeStr == UnitsSpectral::NAME_FREQUENCY ){ + typeStr = ""; + } + profInfo.setSpectralType( typeStr ); Carta::Lib::RegionInfo regionInfo; - auto result = Globals::instance()-> pluginManager() + m_renderService->renderProfile(image, regionInfo, profInfo, curveIndex, layerName, createNew ); + + /*auto result = Globals::instance()-> pluginManager() -> prepare (image, regionInfo, profInfo); - auto lam = [=] ( const Carta::Lib::Hooks::ProfileHook::ResultType &data ) { + auto lam = [=] ( const Carta::Lib::Hooks::ProfileHook::ResultType &result ) { + std::vector< std::pair > data = result.getData(); int dataCount = data.size(); if ( dataCount > 0 ){ std::vector plotDataX( dataCount ); std::vector plotDataY( dataCount ); for( int i = 0 ; i < dataCount; i ++ ){ - plotDataX[i] = i; - plotDataY[i] = data[i]; + plotDataX[i] = data[i].first; + plotDataY[i] = data[i].second; } - int curveIndex = _findCurveIndex( layerName ); std::shared_ptr profileCurve( nullptr ); if ( curveIndex < 0 || createNew ){ Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); profileCurve.reset( objMan->createObject() ); profileCurve->setImageName( layerName ); + double restFrequency = result.getRestFrequency(); + int significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + double restRounded = Util::roundToDigits( restFrequency, significantDigits ); + QString restUnit = result.getRestUnits(); + profileCurve->setRestQuantity( restRounded, restUnit ); _assignCurveName( profileCurve ); _assignColor( profileCurve ); m_plotCurves.append( profileCurve ); profileCurve->setSource( image ); - _saveCurveState(); + } else { profileCurve = m_plotCurves[curveIndex]; } + profileCurve->setData( plotDataX, plotDataY ); + _saveCurveState(); + _updateZoomRangeBasedOnPercent(); + _updatePlotBounds(); _updatePlotData(); } }; @@ -389,7 +420,7 @@ void Profiler::_generateData( std::shared_ptr layer, bool createNew ){ } catch( char*& error ){ qDebug() << "Profiler could not get data: caught error: " << error; - } + }*/ /*Carta::Lib::NdArray::RawViewInterface * rawView = image-> getDataSlice( SliceND() ); Profiles::ProfileExtractor * extractor = new Profiles::ProfileExtractor( rawView ); @@ -435,7 +466,10 @@ void Profiler::_generateData( std::shared_ptr layer, bool createNew ){ } } - +QString Profiler::getAxisUnitsBottom() const { + QString bottomUnits = m_state.getValue( AXIS_UNITS_BOTTOM ); + return bottomUnits; +} Controller* Profiler::_getControllerSelected() const { @@ -453,6 +487,26 @@ Controller* Profiler::_getControllerSelected() const { return controller; } +std::pair Profiler::_getCurveRangeX() const { + double maxValue = -1 * std::numeric_limits::max(); + double minValue = std::numeric_limits::max(); + int curveCount = m_plotCurves.size(); + for ( int i = 0; i < curveCount; i++ ){ + double curveMinValue = minValue; + double curveMaxValue = maxValue; + double yMin = minValue; + double yMax = maxValue; + m_plotCurves[i]->getMinMax( &curveMinValue,&curveMaxValue,&yMin,&yMax); + if ( curveMinValue < minValue ){ + minValue = curveMinValue; + } + if ( curveMaxValue > maxValue ){ + maxValue = curveMaxValue; + } + } + return std::pair( minValue, maxValue ); +} + std::vector > Profiler::_getDataForGenerateMode( Controller* controller) const { QString generateMode = m_state.getValue( GEN_MODE ); @@ -470,8 +524,8 @@ std::vector > Profiler::_getDataForGenerateMode( Controll std::vector > dSources = controller->getLayers(); int dCount = dSources.size(); for ( int i = 0; i < dCount; i++ ){ - int dim = dSources[i]->_getDimension(); - if ( dim > 2 ){ + int specCount = dSources[i]->_getFrameCount( Carta::Lib::AxisInfo::KnownType::SPECTRAL ); + if ( specCount > 1 ){ dataSources.push_back( dSources[i]); } } @@ -494,6 +548,20 @@ int Profiler::_getExtractionAxisIndex( std::shared_ptrgetPath(); +} + + +QList Profiler::getLinks() const { + return m_linkImpl->getLinkIds(); +} + + +QString Profiler::_getPreferencesId() const { + return m_preferences->getPath(); +} + QString Profiler::getStateString( const QString& sessionId, SnapshotType type ) const{ QString result(""); if ( type == SNAPSHOT_PREFERENCES ){ @@ -509,33 +577,6 @@ QString Profiler::getStateString( const QString& sessionId, SnapshotType type ) return result; } - -QString Profiler::_getLegendLocationsId() const { - return m_legendLocations->getPath(); -} - - -QList Profiler::getLinks() const { - return m_linkImpl->getLinkIds(); -} - -double Profiler::_getMaxFrame() const { - double maxFrame = 0; - int curveCount = m_plotCurves.size(); - for ( int i = 0; i < curveCount; i++ ){ - double curveMax = m_plotCurves[i]->getDataMax(); - if ( curveMax > maxFrame ){ - maxFrame = curveMax; - } - } - return maxFrame; -} - - -QString Profiler::_getPreferencesId() const { - return m_preferences->getPath(); -} - QString Profiler::_getUnitType( const QString& unitStr ){ QString unitType = unitStr; int unitStart = unitStr.indexOf( "("); @@ -558,6 +599,22 @@ QString Profiler::_getUnitUnits( const QString& unitStr ){ return strippedUnit; } +double Profiler::getZoomMax() const { + return m_stateData.getValue( ZOOM_MAX ); +} + +double Profiler::getZoomMin() const { + return m_stateData.getValue( ZOOM_MIN ); +} + +double Profiler::getZoomMinPercent() const { + return m_stateData.getValue( ZOOM_MIN_PERCENT ); +} + +double Profiler::getZoomMaxPercent() const { + return m_stateData.getValue( ZOOM_MAX_PERCENT ); +} + void Profiler::_initializeDefaultState(){ //Data state is the curves @@ -574,14 +631,15 @@ void Profiler::_initializeDefaultState(){ m_stateData.flushState(); //Default units - m_bottomUnit = m_spectralUnits->getDefault(); - QString unitType = _getUnitType( m_bottomUnit ); + QString bottomUnit = m_spectralUnits->getDefault(); + QString unitType = _getUnitType( bottomUnit ); m_plotManager->setTitleAxisX( unitType ); - m_state.insertValue( AXIS_UNITS_BOTTOM, m_bottomUnit ); + m_state.insertValue( AXIS_UNITS_BOTTOM, bottomUnit ); m_state.insertValue( AXIS_UNITS_LEFT, m_intensityUnits->getDefault()); m_state.insertValue(GEN_MODE, m_generateModes->getDefault()); m_state.insertValue(TOOL_TIPS, false ); + //Legend bool external = true; QString legendLoc = m_legendLocations->getDefaultLocation( external ); @@ -597,6 +655,9 @@ void Profiler::_initializeDefaultState(){ m_state.insertValue( Util::TAB_INDEX, 2 ); m_state.insertValue( SHOW_TOOLTIP, true ); + //Significant digits. + m_state.insertValue(Util::SIGNIFICANT_DIGITS, 6 ); + m_state.flushState(); } @@ -619,6 +680,7 @@ void Profiler::_initializeCallbacks(){ addCommandCallback( "setAxisUnitsBottom", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { std::set keys = {Util::UNITS}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); QString unitStr = dataValues[*keys.begin()]; QString result = setAxisUnitsBottom( unitStr ); @@ -693,6 +755,55 @@ void Profiler::_initializeCallbacks(){ return result; }); + addCommandCallback( "setRestFrequency", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {Util::NAME, CurveData::REST_FREQUENCY}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString nameStr = dataValues[Util::NAME]; + QString restFreqStr = dataValues[CurveData::REST_FREQUENCY]; + bool validDouble = false; + double restFreq = restFreqStr.toDouble( &validDouble ); + QString result; + if ( validDouble ){ + result = setRestFrequency( restFreq, nameStr ); + } + else { + result = "Rest frequency must be a valid number: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setRestUnit", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {Util::NAME, CurveData::REST_UNIT_FREQ}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString nameStr = dataValues[Util::NAME]; + QString restUnits = dataValues[CurveData::REST_UNIT_FREQ]; + QString result = setRestUnits( restUnits, nameStr ); + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setRestUnitType", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {Util::NAME, CurveData::REST_FREQUENCY_UNITS}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString nameStr = dataValues[Util::NAME]; + QString restUnitsStr = dataValues[CurveData::REST_FREQUENCY_UNITS]; + bool validBool = false; + bool restUnitsFreq = Util::toBool( restUnitsStr, &validBool ); + QString result; + if ( validBool ){ + result = setRestUnitType( restUnitsFreq, nameStr ); + } + else { + result = "Rest unit type frequency must be true/false: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + addCommandCallback( "newProfile", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { QString result = profileNew(); @@ -711,14 +822,24 @@ void Profiler::_initializeCallbacks(){ }); addCommandCallback( "removeProfile", [=] (const QString & /*cmd*/, - const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {Util::NAME}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString nameStr = dataValues[Util::NAME]; - QString result = profileRemove( nameStr ); - Util::commandPostProcess( result ); - return result; - }); + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {Util::NAME}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString nameStr = dataValues[Util::NAME]; + QString result = profileRemove( nameStr ); + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "resetRestFrequency", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {Util::NAME}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString nameStr = dataValues[Util::NAME]; + QString result = resetRestFrequency( nameStr ); + Util::commandPostProcess( result ); + return result; + }); addCommandCallback( "setCurveColor", [=] (const QString & /*cmd*/, @@ -851,6 +972,35 @@ void Profiler::_initializeCallbacks(){ return result; }); + addCommandCallback( "setSignificantDigits", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + QString result; + std::set keys = {Util::SIGNIFICANT_DIGITS}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString digitsStr = dataValues[Util::SIGNIFICANT_DIGITS]; + bool validDigits = false; + int digits = digitsStr.toInt( &validDigits ); + if ( validDigits ){ + result = setSignificantDigits( digits ); + } + else { + result = "Profile significant digits must be an integer: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setStatistic", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = { CurveData::STATISTIC, Util::NAME}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString statStr = dataValues[CurveData::STATISTIC]; + QString curveName = dataValues[Util::NAME]; + QString result = setStatistic( statStr, curveName ); + Util::commandPostProcess( result ); + return result; + }); + addCommandCallback( "setTabIndex", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result; @@ -924,14 +1074,17 @@ void Profiler::_initializeCallbacks(){ void Profiler::_initializeStatics(){ if ( m_spectralUnits == nullptr ){ - m_spectralUnits = Util::findSingletonObject(); + m_spectralUnits = Util::findSingletonObject(); } if ( m_intensityUnits == nullptr ){ - m_intensityUnits = Util::findSingletonObject(); + m_intensityUnits = Util::findSingletonObject(); } if ( m_generateModes == nullptr ){ m_generateModes = Util::findSingletonObject(); } + if ( m_stats == nullptr ){ + m_stats = Util::findSingletonObject(); + } } @@ -951,30 +1104,11 @@ void Profiler::_loadProfile( Controller* controller ){ } _updateAvailableImages( controller ); std::vector > layers = _getDataForGenerateMode( controller ); - m_plotManager->clearData(); - //Make profiles for any new data that has been loaded. - int dataCount = layers.size(); - for ( int i = 0; i < dataCount; i++ ) { - QString layerName = layers[i]->_getLayerName(); - int curveCount = m_plotCurves.size(); - int profileIndex = -1; - for ( int j = 0; j < curveCount; j++ ){ - QString imageName = m_plotCurves[j]->getNameImage(); - if ( imageName == layerName ){ - profileIndex = j; - break; - } - } - - if ( profileIndex < 0 ){ - _generateData( layers[i]); - } - } - //Go through the old profiles and remove any that are no longer present. int curveCount = m_plotCurves.size(); + int dataCount = layers.size(); QList removeIndices; for ( int i = 0; i < curveCount; i++ ){ QString imageName = m_plotCurves[i]->getNameImage(); @@ -994,7 +1128,31 @@ void Profiler::_loadProfile( Controller* controller ){ for ( int i = removeCount - 1; i >= 0; i-- ){ m_plotCurves.removeAt( removeIndices[i] ); } - _updatePlotData(); + + //Make profiles for any new data that has been loaded. + bool generates = false; + for ( int i = 0; i < dataCount; i++ ) { + QString layerName = layers[i]->_getLayerName(); + int curveCount = m_plotCurves.size(); + int profileIndex = -1; + for ( int j = 0; j < curveCount; j++ ){ + QString imageName = m_plotCurves[j]->getNameImage(); + if ( imageName == layerName ){ + profileIndex = j; + break; + } + } + if ( profileIndex < 0 ){ + generates = true; + _generateData( layers[i]); + } + } + _saveCurveState(); + //If we removed some curves but did not generate any new ones, the plot + //needs to get updated (it will be updated automatically if a new curve is generated. + if ( removeIndices.size() > 0 && !generates ){ + _updatePlotData(); + } } @@ -1067,6 +1225,7 @@ QString Profiler::profileRemove( const QString& name ){ m_plotCurves.removeAt( curveIndex ); m_plotManager->removeData( name ); _saveCurveState(); + _updateZoomRangeBasedOnPercent(); _updatePlotData(); } else { @@ -1076,6 +1235,56 @@ QString Profiler::profileRemove( const QString& name ){ } +void Profiler::_profileRendered(const Carta::Lib::Hooks::ProfileResult& result, + int curveIndex, const QString& layerName, bool createNew, + std::shared_ptr image){ + QString errorMessage = result.getError(); + if ( !errorMessage.isEmpty() ){ + ErrorManager* hr = Util::findSingletonObject(); + hr->registerError( errorMessage ); + } + else { + std::vector< std::pair > data = result.getData(); + int dataCount = data.size(); + if ( dataCount > 0 ){ + std::vector plotDataX( dataCount ); + std::vector plotDataY( dataCount ); + + for( int i = 0 ; i < dataCount; i ++ ){ + plotDataX[i] = data[i].first; + plotDataY[i] = data[i].second; + } + + std::shared_ptr profileCurve( nullptr ); + if ( curveIndex < 0 || createNew ){ + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + profileCurve.reset( objMan->createObject() ); + profileCurve->setImageName( layerName ); + double restFrequency = result.getRestFrequency(); + int significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + double restRounded = Util::roundToDigits( restFrequency, significantDigits ); + QString restUnit = result.getRestUnits(); + profileCurve->setRestQuantity( restRounded, restUnit ); + _assignCurveName( profileCurve ); + _assignColor( profileCurve ); + m_plotCurves.append( profileCurve ); + profileCurve->setSource( image ); + + } + else { + profileCurve = m_plotCurves[curveIndex]; + } + + profileCurve->setData( plotDataX, plotDataY ); + _saveCurveState(); + _updateZoomRangeBasedOnPercent(); + _updatePlotBounds(); + _updatePlotData(); + } + } +} + + QString Profiler::removeLink( CartaObject* cartaObject){ bool removed = false; QString result; @@ -1094,6 +1303,22 @@ QString Profiler::removeLink( CartaObject* cartaObject){ return result; } +QString Profiler::resetRestFrequency( const QString& curveName ){ + QString result; + int index = _findCurveIndex( curveName ); + if ( index >= 0 ){ + m_plotCurves[index]->resetRestFrequency(); + _saveCurveState( index ); + m_stateData.flushState(); + _generateData( m_plotCurves[index]->getImage(), + index, m_plotCurves[index]->getNameImage(), false ); + } + else { + result = "Could not reset rest frequency, unrecognized profile curve:"+curveName; + } + return result; +} + void Profiler::resetState( const QString& state ){ StateInterface restoredState( ""); restoredState.setState( state ); @@ -1127,10 +1352,28 @@ QString Profiler::setAxisUnitsBottom( const QString& unitStr ){ if ( !actualUnits.isEmpty() ){ QString oldBottomUnits = m_state.getValue( AXIS_UNITS_BOTTOM ); if ( actualUnits != oldBottomUnits ){ + //Change the units in the curves. + int curveCount = m_plotCurves.size(); + for ( int i = 0; i < curveCount; i++ ){ + std::vector converted = _convertUnitsX( m_plotCurves[i], actualUnits ); + m_plotCurves[i]->setDataX( converted ); + } + + //Update the state & graph m_state.setValue( AXIS_UNITS_BOTTOM, actualUnits); m_plotManager->setTitleAxisX( _getUnitType( actualUnits ) ); m_state.flushState(); + + //Set the zoom min & max based on new units + _updateZoomRangeBasedOnPercent(); + + //Tell the plot about the new bounds. + _updatePlotBounds(); + + //Put the data into the plot _updatePlotData(); + + //Update channel line _updateChannel( _getControllerSelected(), Carta::Lib::AxisInfo::KnownType::SPECTRAL ); } } @@ -1146,10 +1389,16 @@ QString Profiler::setAxisUnitsLeft( const QString& unitStr ){ if ( !actualUnits.isEmpty() ){ QString oldLeftUnits = m_state.getValue( AXIS_UNITS_LEFT ); if ( oldLeftUnits != actualUnits ){ + //Convert the units in the curves. + int curveCount = m_plotCurves.size(); + for ( int i = 0; i < curveCount; i++ ){ + std::vector converted = _convertUnitsY( m_plotCurves[i], actualUnits ); + m_plotCurves[i]->setDataY( converted ); + } + //Update the state and plot m_state.setValue( AXIS_UNITS_LEFT, actualUnits ); m_state.flushState(); _updatePlotData(); - //_updateChannel( _getControllerSelected(), Carta::Lib::AxisInfo::KnownType::SPECTRAL ); m_plotManager->setTitleAxisY( actualUnits ); } } @@ -1195,6 +1444,7 @@ QStringList Profiler::setCurveColor( const QString& name, int redAmount, int gre return result; } + QString Profiler::setCurveName( const QString& id, const QString& newName ){ QString result; int curveIndex = _findCurveIndex( id ); @@ -1210,6 +1460,11 @@ QString Profiler::setCurveName( const QString& id, const QString& newName ){ return result; } +void Profiler::_setErrorMargin(){ + int significantDigits = m_state.getValue(Util::SIGNIFICANT_DIGITS ); + m_errorMargin = 1.0/qPow(10,significantDigits); +} + QString Profiler::setGenerateMode( const QString& modeStr ){ QString result; QString actualMode = m_generateModes->getActualMode( modeStr ); @@ -1218,6 +1473,8 @@ QString Profiler::setGenerateMode( const QString& modeStr ){ if ( actualMode != oldMode ){ m_state.setValue( GEN_MODE, actualMode); m_state.flushState(); + Controller* controller = _getControllerSelected(); + _loadProfile( controller ); } } else { @@ -1328,6 +1585,95 @@ QString Profiler::setPlotStyle( const QString& name, const QString& plotStyle ){ return result; } +QString Profiler::setRestFrequency( double freq, const QString& curveName ){ + QString result; + int index = _findCurveIndex( curveName ); + if ( index >= 0 ){ + int significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + double roundedFreq = Util::roundToDigits( freq, significantDigits ); + bool freqSet = false; + result = m_plotCurves[index]->setRestFrequency( roundedFreq, m_errorMargin, &freqSet ); + if ( freqSet ){ + _saveCurveState( index ); + m_stateData.flushState(); + _generateData( m_plotCurves[index]->getImage(), + index, m_plotCurves[index]->getNameImage(), false ); + } + } + else { + result = "Unrecognized profile curve: "+curveName; + } + return result; +} + +QString Profiler::setRestUnits( const QString& restUnits, const QString& curveName ){ + QString result; + int index = _findCurveIndex( curveName ); + if ( index >= 0 ){ + int signDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + result = m_plotCurves[index]->setRestUnits( restUnits, signDigits, m_errorMargin ); + if ( result.isEmpty() ){ + _saveCurveState( index ); + m_stateData.flushState(); + } + } + else { + result = "Unrecognized profile curve: "+curveName; + } + return result; +} + + +QString Profiler::setRestUnitType( bool restUnitsFreq, const QString& curveName ){ + QString result; + int index = _findCurveIndex( curveName ); + if ( index >= 0 ){ + int signDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + m_plotCurves[index]->setRestUnitType( restUnitsFreq, signDigits, m_errorMargin ); + _saveCurveState( index ); + m_stateData.flushState(); + } + else { + result = "Unrecognized profile curve:"+curveName; + } + return result; +} + +QString Profiler::setSignificantDigits( int digits ){ + QString result; + if ( digits <= 0 ){ + result = "Invalid significant digits; must be positive: "+QString::number( digits ); + } + else { + if ( m_state.getValue(Util::SIGNIFICANT_DIGITS) != digits ){ + m_state.setValue(Util::SIGNIFICANT_DIGITS, digits ); + _setErrorMargin(); + } + } + return result; +} + +QString Profiler::setStatistic( const QString& statStr, const QString& curveName ){ + QString result; + int index = _findCurveIndex( curveName ); + if ( index >= 0 ){ + result = m_plotCurves[index]->setStatistic( statStr ); + if ( result.isEmpty() ){ + _saveCurveState( index ); + m_stateData.flushState(); + Carta::Lib::ProfileInfo::AggregateType agType = m_stats->getTypeFor( statStr ); + m_intensityUnits->resetUnits( agType ); + QString unitDefault = m_intensityUnits->getDefault(); + setAxisUnitsLeft( unitDefault ); + _generateData( m_plotCurves[index]->getImage(), + index, m_plotCurves[index]->getNameImage(), false ); + } + } + else { + result = "Could not set the profile statistic - unrecognized curve: "+curveName; + } + return result; +} QString Profiler::setTabIndex( int index ){ QString result; @@ -1350,6 +1696,7 @@ void Profiler::setZoomBuffer( bool zoomBuffer ){ if ( oldZoomBuffer != zoomBuffer ){ m_stateData.setValue( ZOOM_BUFFER, zoomBuffer ); m_stateData.flushState(); + _updatePlotBounds(); } } @@ -1358,9 +1705,12 @@ QString Profiler::setZoomBufferSize( double zoomBufferSize ){ QString result; if ( zoomBufferSize >= 0 && zoomBufferSize < 100 ){ double oldBufferSize = m_stateData.getValue( ZOOM_BUFFER_SIZE ); - if ( qAbs( zoomBufferSize - oldBufferSize) > ERROR_MARGIN ){ - m_stateData.setValue( ZOOM_BUFFER_SIZE, zoomBufferSize ); + int significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + double roundedSize = Util::roundToDigits( zoomBufferSize, significantDigits ); + if ( qAbs( roundedSize - oldBufferSize) > m_errorMargin ){ + m_stateData.setValue( ZOOM_BUFFER_SIZE, roundedSize ); m_stateData.flushState(); + _updatePlotBounds(); } } else { @@ -1372,30 +1722,38 @@ QString Profiler::setZoomBufferSize( double zoomBufferSize ){ QString Profiler::setZoomRange( double zoomMin, double zoomMax ){ QString result; - if ( zoomMin < zoomMax ){ + double significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + double zoomMinRounded = Util::roundToDigits( zoomMin, significantDigits ); + double zoomMaxRounded = Util::roundToDigits( zoomMax, significantDigits ); + if ( zoomMinRounded < zoomMaxRounded ){ bool changed = false; double oldZoomMin = m_stateData.getValue( ZOOM_MIN ); - if ( qAbs( zoomMin - oldZoomMin ) > ERROR_MARGIN ){ + if ( qAbs( zoomMinRounded - oldZoomMin ) > m_errorMargin ){ changed = true; - m_stateData.setValue( ZOOM_MIN, zoomMin ); + m_stateData.setValue( ZOOM_MIN, zoomMinRounded ); } double oldZoomMax = m_stateData.getValue( ZOOM_MAX ); - if ( qAbs( zoomMax - oldZoomMax ) > ERROR_MARGIN ){ + if ( qAbs( zoomMaxRounded - oldZoomMax ) > m_errorMargin ){ changed = true; - m_stateData.setValue( ZOOM_MAX, zoomMax ); + m_stateData.setValue( ZOOM_MAX, zoomMaxRounded ); } if ( changed ){ //Update the percents to match. - double maxChannel = _getMaxFrame(); + std::pair curveRange = _getCurveRangeX(); + double lowerPercent = 0; double upperPercent = 100; - if ( maxChannel > 0 ){ - if ( zoomMin > 0 ){ - lowerPercent = zoomMin / maxChannel; + double curveSpan = curveRange.second - curveRange.first; + if ( curveSpan > 0 ){ + if ( curveRange.first < zoomMinRounded ){ + double diff = zoomMinRounded - curveRange.first; + lowerPercent = diff / curveSpan * 100; + lowerPercent = Util::roundToDigits( lowerPercent, significantDigits ); } - double diffUpper = maxChannel - zoomMax; - if ( diffUpper > 0 ){ - upperPercent = 100 - diffUpper / maxChannel; + if ( curveRange.second > zoomMaxRounded ){ + double diff = curveRange.second - zoomMaxRounded; + upperPercent = 100 - diff / curveSpan * 100; + upperPercent = Util::roundToDigits( upperPercent, significantDigits ); } } m_stateData.setValue( ZOOM_MIN_PERCENT, lowerPercent ); @@ -1411,51 +1769,30 @@ QString Profiler::setZoomRange( double zoomMin, double zoomMax ){ return result; } -void Profiler::_updatePlotBounds(){ - //Update the graph. - //See if we need to add an additional buffer. - double graphMin = m_stateData.getValue( ZOOM_MIN ); - double graphMax = m_stateData.getValue( ZOOM_MAX ); - double maxChannel = _getMaxFrame(); - if ( m_stateData.getValue( ZOOM_BUFFER) ){ - double bufferSize = m_stateData.getValue( ZOOM_BUFFER_SIZE ); - double halfSize = bufferSize / 2; - double buffAmount = maxChannel * halfSize / 100; - graphMin = graphMin - buffAmount; - graphMax = graphMax + buffAmount; - } - m_plotManager->setAxisXRange( graphMin, graphMax ); -} QString Profiler::setZoomRangePercent( double zoomMinPercent, double zoomMaxPercent ){ QString result; + if ( 0 <= zoomMinPercent && zoomMinPercent <= 100 ){ if ( 0 <= zoomMaxPercent && zoomMaxPercent <= 100 ){ - if ( zoomMinPercent < zoomMaxPercent ){ + int significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + double zoomMinPercentRounded = Util::roundToDigits( zoomMinPercent, significantDigits ); + double zoomMaxPercentRounded = Util::roundToDigits( zoomMaxPercent, significantDigits ); + if ( zoomMinPercentRounded < zoomMaxPercentRounded ){ bool changed = false; double oldZoomMinPercent = m_stateData.getValue( ZOOM_MIN_PERCENT ); - if ( qAbs( zoomMinPercent - oldZoomMinPercent ) > ERROR_MARGIN ){ + if ( qAbs( zoomMinPercentRounded - oldZoomMinPercent ) > m_errorMargin ){ changed = true; - m_stateData.setValue( ZOOM_MIN_PERCENT, zoomMinPercent ); + m_stateData.setValue( ZOOM_MIN_PERCENT, zoomMinPercentRounded ); } double oldZoomMaxPercent = m_stateData.getValue( ZOOM_MAX_PERCENT ); - if ( qAbs( zoomMaxPercent - oldZoomMaxPercent ) > ERROR_MARGIN ){ + if ( qAbs( zoomMaxPercentRounded - oldZoomMaxPercent ) > m_errorMargin ){ changed = true; - m_stateData.setValue( ZOOM_MAX_PERCENT, zoomMaxPercent ); + m_stateData.setValue( ZOOM_MAX_PERCENT, zoomMaxPercentRounded ); } if ( changed ){ - //Update the values to match. - double maxChannel = _getMaxFrame(); - double minZoom = 0; - double maxZoom = 1; - if ( maxChannel > 0 ){ - minZoom = maxChannel * zoomMinPercent/100; - maxZoom = maxChannel * zoomMaxPercent / 100; - m_stateData.setValue( ZOOM_MIN, minZoom ); - m_stateData.setValue( ZOOM_MAX, maxZoom ); - } - m_stateData.flushState(); - + //Update the zoom min and max. + _updateZoomRangeBasedOnPercent(); //Update the graph. _updatePlotBounds(); } @@ -1526,30 +1863,60 @@ void Profiler::_updateChannel( Controller* controller, Carta::Lib::AxisInfo::Kno } } +void Profiler::_updatePlotBounds(){ + //Update the graph. + //See if we need to add an additional buffer. + double graphMin = m_stateData.getValue( ZOOM_MIN ); + double graphMax = m_stateData.getValue( ZOOM_MAX ); + double plotRange = graphMax - graphMin; + if ( m_stateData.getValue( ZOOM_BUFFER) ){ + double bufferSize = m_stateData.getValue( ZOOM_BUFFER_SIZE ); + double halfSize = bufferSize / 2; + double buffAmount = plotRange * halfSize / 100; + graphMin = graphMin - buffAmount; + graphMax = graphMax + buffAmount; + } + m_plotManager->setAxisXRange( graphMin, graphMax ); +} + +void Profiler::_updateZoomRangeBasedOnPercent(){ + std::pair range = _getCurveRangeX(); + double curveSpan = range.second - range.first; + double minPercent = getZoomMinPercent(); + double maxPercent = getZoomMaxPercent(); + double zoomMin = range.first + minPercent* curveSpan / 100; + double zoomMax = range.second -(100 - maxPercent)* curveSpan / 100; + int significantDigits = m_state.getValue( Util::SIGNIFICANT_DIGITS ); + zoomMin = Util::roundToDigits( zoomMin, significantDigits ); + zoomMax = Util::roundToDigits( zoomMax, significantDigits ); + double oldZoomMin = getZoomMin(); + double oldZoomMax = getZoomMax(); + bool changed = false; + if ( qAbs( oldZoomMin - zoomMin ) > m_errorMargin ){ + m_stateData.setValue( ZOOM_MIN, zoomMin ); + changed = true; + } + if ( qAbs( oldZoomMax - zoomMax ) > m_errorMargin ){ + m_stateData.setValue( ZOOM_MAX, zoomMax ); + changed = true; + } + if ( changed ){ + m_stateData.flushState(); + } +} void Profiler::_updatePlotData(){ - //m_plotManager->clearData(); int curveCount = m_plotCurves.size(); + //Put the data into the plot. for ( int i = 0; i < curveCount; i++ ){ - //Convert the data units, if necessary. - std::vector convertedX = _convertUnitsX( m_plotCurves[i] ); - std::vector convertedY = _convertUnitsY( m_plotCurves[i] ); - int dataCount = convertedX.size(); - std::vector< std::pair > plotData; - for ( int i = 0; i < dataCount; i++ ){ - if ( !std::isinf(convertedX[i]) && !std::isinf(convertedY[i]) ){ - std::pair data( convertedX[i], convertedY[i]); - plotData.push_back( data ); - } - } - - //Put the data into the plot. + std::vector< std::pair > plotData = m_plotCurves[i]->getPlotData(); QString dataId = m_plotCurves[i]->getName(); Carta::Lib::Hooks::Plot2DResult plotResult( dataId, "", "", plotData ); m_plotManager->addData( &plotResult ); m_plotManager->setColor( m_plotCurves[i]->getColor(), dataId ); } + QString bottomUnit = m_state.getValue( AXIS_UNITS_BOTTOM ); bottomUnit = _getUnitUnits( bottomUnit ); QString leftUnit = m_state.getValue( AXIS_UNITS_LEFT ); diff --git a/carta/cpp/core/Data/Profile/Profiler.h b/carta/cpp/core/Data/Profile/Profiler.h index d5aab7c9..f7a05235 100755 --- a/carta/cpp/core/Data/Profile/Profiler.h +++ b/carta/cpp/core/Data/Profile/Profiler.h @@ -10,7 +10,7 @@ #include "State/StateInterface.h" #include "Data/ILinkable.h" #include "CartaLib/IImage.h" -#include "ProfileExtractor.h" +#include "CartaLib/Hooks/ProfileResult.h" #include @@ -38,12 +38,16 @@ class Plot2DManager; class Controller; class CurveData; class GenerateModes; -class IntensityUnits; class LegendLocations; class LinkableImpl; class Layer; +class ProfileRenderService; +class ProfileStatistics; class Settings; -class SpectralUnits; + +class UnitsIntensity; +class UnitsSpectral; + class Profiler : public QObject, public Carta::State::CartaObject, public ILinkable { @@ -56,9 +60,38 @@ class Profiler : public QObject, public Carta::State::CartaObject, public ILinka QString removeLink( CartaObject* cartaObject) Q_DECL_OVERRIDE; virtual QList getLinks() const Q_DECL_OVERRIDE; + /** + * Returns the units on the bottom axis of the profile. + * @return - the profile bottom axis units. + */ + QString getAxisUnitsBottom() const; virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; + /** + * Returns the upper limit of the zoom range on the x-axis. + * @return - the upper limit of the zoom range on the x-axis. + */ + double getZoomMax() const; + + /** + * Returns the lower limit of the zoom range on the x-axis. + * @return - the lower limit of the zoom range on the x-axis. + */ + double getZoomMin() const; + + /** + * Returns the percentile zoom for the lower bound of the x-axis. + * @return - the percentile zoom for the lower bound of the x-axis. + */ + double getZoomMinPercent() const; + + /** + * Returns the percentilee zoom for the upper bound of the x-axis. + * @return - the percentile zoom for the upper bound of the x-axis. + */ + double getZoomMaxPercent() const; + /** * Returns whether or not the object with the given id is already linked to this object. * @param linkId - a QString identifier for an object. @@ -89,6 +122,14 @@ class Profiler : public QObject, public Carta::State::CartaObject, public ILinka */ QString profileRemove( const QString& name ); + /** + * Set the rest frequency back to its original value for the given curve. + * @param curveName - an identifier for a profile curve. + * @return - an error message if the rest frequency could not be reset; otherwise, an + * empty string. + */ + QString resetRestFrequency( const QString& curveName ); + virtual void resetState( const QString& state ) Q_DECL_OVERRIDE; @@ -184,6 +225,51 @@ class Profiler : public QObject, public Carta::State::CartaObject, public ILinka */ QString setPlotStyle( const QString& name, const QString& plotStyle ); + /** + * Set the rest frequency used to generate a profile for the given curve. + * @param freq - the rest frequency. + * @param curveName - an identifier for a profile curve. + * @return - an error message if the rest frequency could not be set; otherwise, + * an empty string. + */ + QString setRestFrequency( double freq, const QString& curveName ); + + /** + * Set the rest frequency units used to generate a profile for the given curve. + * @param restUnits - the rest frequency units. + * @param curveName - an identifier for a profile curve. + * @return - an error message if the rest frequency units could not be set; otherwise, + * an empty string. + */ + QString setRestUnits( const QString& restUnits, const QString& curveName ); + + /** + * Set whether or not rest frequency units are given in frequency or wavelength. + * @param restUnitsFreq - true if rest frequency units are specified as frequency; + * false otherwise. + * @param curveName - an identifier for a profile curve. + * @return - an error message if the type of rest frequency units could not be set; + * otherwise, an empty string. + */ + QString setRestUnitType( bool restUnitsFreq, const QString& curveName ); + + /** + * Set the number of significant digits to use in storing numbers. + * @param digits - the number of significant digits to use in storing numbers. + * @return - an error message if the number of significant digits could not be set; + * otherwise, an empty string. + */ + QString setSignificantDigits( int digits ); + + /** + * Set the method used to compute the profile. + * @param statStr - an identifier for a method used to summarize a profile. + * @param curveName - an identifier for a profile curve. + * @return - an error method if the statistic could not be set; otherwise, + * an empty string. + */ + QString setStatistic( const QString& statStr, const QString& curveName ); + /** * Set the index of the profile settings tab that should be selected. * @param index - the index of the profile settings tab that should be selected. @@ -204,7 +290,21 @@ class Profiler : public QObject, public Carta::State::CartaObject, public ILinka */ QString setZoomBufferSize( double zoomBufferSize ); + /** + * Set the zoom range in world coordinates. + * @param zoomMin - the lower boundary of the zoom window. + * @param zoomMax - the upper boundary of the zoom window. + * @return - an error message if the zoom range could not be set; otherwise, an empty string. + */ QString setZoomRange( double zoomMin, double zoomMax ); + + /** + * Set the zoom range as a percentage of the plot range. + * @param zoomMinPercent - a value in [0,100] indicating the lower boundary of the zoom window. + * @param zoomMaxPercent - a value in [0,100] indicating the upper boundary of the zoom window. + * @return - an error message if the zoom range could not be set as a percentage; otherwise, + * an empty string. + */ QString setZoomRangePercent( double zoomMinPercent, double zoomMaxPercent ); @@ -219,7 +319,11 @@ private slots: void _cursorUpdate( double x, double y ); void _loadProfile( Controller* controller); void _movieFrame(); + void _profileRendered( const Carta::Lib::Hooks::ProfileResult& result, + int curveIndex, const QString& layerName, bool createNew, + std::shared_ptr image); void _updateChannel( Controller* controller, Carta::Lib::AxisInfo::KnownType type ); + void _updateZoomRangeBasedOnPercent(); QString _zoomToSelection(); private: @@ -244,7 +348,6 @@ private slots: const static QString ZOOM_MAX; const static QString ZOOM_MIN_PERCENT; const static QString ZOOM_MAX_PERCENT; - const static double ERROR_MARGIN; //Assign a color to the curve. void _assignColor( std::shared_ptr curveData ); @@ -257,16 +360,20 @@ private slots: std::shared_ptr dataSource, const QString& oldUnit, const QString& newUnit ) const; std::vector _convertUnitsX( std::shared_ptr curveData, - const QString& newUnit = QString() ) const; - std::vector _convertUnitsY( std::shared_ptr curveData ) const; + const QString& newUnit ) const; + std::vector _convertUnitsY( std::shared_ptr curveData, + const QString& newUnit ) const; void _generateData( std::shared_ptr layer, bool createNew = false ); + void _generateData( std::shared_ptr image, + int curveIndex, const QString& layerName, bool createNew = false ); Controller* _getControllerSelected() const; + std::pair _getCurveRangeX() const; std::vector > _getDataForGenerateMode( Controller* controller) const; int _getExtractionAxisIndex( std::shared_ptr image ) const; - double _getMaxFrame() const; QString _getLegendLocationsId() const; + /** * Returns the server side id of the Profiler user preferences. * @return the unique server side id of the user preferences. @@ -282,6 +389,8 @@ private slots: void _saveCurveState(); void _saveCurveState( int index ); + void _setErrorMargin(); + void _updateAvailableImages( Controller* controller ); void _updatePlotBounds(); @@ -317,26 +426,30 @@ private slots: //Plot data QList< std::shared_ptr > m_plotCurves; - QString m_leftUnit; - QString m_bottomUnit; - //For a movie. int m_oldFrame; int m_currentFrame; int m_timerId; + //When two items with decimals are judged to be the same. + double m_errorMargin; + //State specific to the data that is loaded. Carta::State::StateInterface m_stateData; - static SpectralUnits* m_spectralUnits; - static IntensityUnits* m_intensityUnits; + static UnitsSpectral* m_spectralUnits; + static UnitsIntensity* m_intensityUnits; + static ProfileStatistics* m_stats; static GenerateModes* m_generateModes; static QList m_curveColors; + //Compute the profile in a thread + std::unique_ptr m_renderService; + Profiler( const Profiler& other); - Profiler operator=( const Profiler& other ); + Profiler& operator=( const Profiler& other ); }; } } diff --git a/carta/cpp/core/Data/Profile/SpectralUnits.cpp b/carta/cpp/core/Data/Profile/SpectralUnits.cpp deleted file mode 100644 index 9023c8dc..00000000 --- a/carta/cpp/core/Data/Profile/SpectralUnits.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "SpectralUnits.h" -#include "CartaLib/CartaLib.h" -#include "State/UtilState.h" -#include -#include - -namespace Carta { - -namespace Data { - -const QString SpectralUnits::UNIT_LIST = "spectralUnits"; -const QString SpectralUnits::CLASS_NAME = "SpectralUnits"; -const QString SpectralUnits::NAME_VELOCITY_RADIO = "Radio Velocity"; -const QString SpectralUnits::NAME_VELOCITY_OPTICAL = "Optical Velocity"; -const QString SpectralUnits::NAME_FREQUENCY = "Frequency"; -const QString SpectralUnits::NAME_WAVELENGTH = "Wavelength"; -const QString SpectralUnits::NAME_CHANNEL = "Channel"; -const QString SpectralUnits::UNIT_MS = "m/s"; -const QString SpectralUnits::UNIT_KMS = "km/s"; -const QString SpectralUnits::UNIT_HZ = "Hz"; -const QString SpectralUnits::UNIT_MHZ = "MHz"; -const QString SpectralUnits::UNIT_GHZ = "GHz"; -const QString SpectralUnits::UNIT_MM = "mm"; -const QString SpectralUnits::UNIT_UM = "um"; -const QString SpectralUnits::UNIT_NM = "nm"; -const QString SpectralUnits::UNIT_ANGSTROM = "Angstrom"; - -class SpectralUnits::Factory : public Carta::State::CartaObjectFactory { -public: - - Factory(): - CartaObjectFactory(CLASS_NAME){ - }; - - Carta::State::CartaObject * create (const QString & path, const QString & id){ - return new SpectralUnits (path, id); - } -}; - - -bool SpectralUnits::m_registered = - Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new SpectralUnits::Factory()); - - -SpectralUnits::SpectralUnits( const QString& path, const QString& id): - CartaObject( CLASS_NAME, path, id ){ - _initializeDefaultState(); -} - - -QString SpectralUnits::getActualUnits( const QString& unitStr ) const { - QString actualUnits; - int dataCount = m_state.getArraySize( UNIT_LIST ); - for ( int i = 0; i < dataCount; i++ ){ - QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); - QString unitName = m_state.getValue( key ); - int result = QString::compare( unitName, unitStr, Qt::CaseInsensitive ); - if ( result == 0 ){ - actualUnits = unitName; - break; - } - } - return actualUnits; -} - - -QString SpectralUnits::getDefault() const { - return NAME_CHANNEL; -} - - -void SpectralUnits::_initUnit( int * index, const QString& name, const QString& unit ){ - QString key = Carta::State::UtilState::getLookup( UNIT_LIST, *index ); - QString value = name; - if ( unit.length() > 0 ){ - value = value + "("+unit+")"; - } - m_state.setValue( key, value ); - *index = *index + 1; -} - - -void SpectralUnits::_initializeDefaultState(){ - m_state.insertArray( UNIT_LIST, 13 ); - int i = 0; - _initUnit( &i, NAME_VELOCITY_RADIO, UNIT_MS ); - _initUnit( &i, NAME_VELOCITY_RADIO, UNIT_KMS ); - _initUnit( &i, NAME_VELOCITY_OPTICAL, UNIT_MS ); - _initUnit( &i, NAME_VELOCITY_OPTICAL, UNIT_KMS ); - _initUnit( &i, NAME_FREQUENCY, UNIT_HZ ); - _initUnit( &i, NAME_FREQUENCY, UNIT_MHZ ); - _initUnit( &i, NAME_FREQUENCY, UNIT_GHZ ); - _initUnit( &i, NAME_FREQUENCY, UNIT_HZ ); - _initUnit( &i, NAME_WAVELENGTH, UNIT_MM ); - _initUnit( &i, NAME_WAVELENGTH, UNIT_UM ); - _initUnit( &i, NAME_WAVELENGTH, UNIT_NM ); - _initUnit( &i, NAME_WAVELENGTH, UNIT_ANGSTROM ); - _initUnit( &i, NAME_CHANNEL, "" ); - m_state.flushState(); -} - - -SpectralUnits::~SpectralUnits(){ - -} -} -} diff --git a/carta/cpp/core/Data/Region/Region.cpp b/carta/cpp/core/Data/Region/Region.cpp index 9168867e..066c2b80 100644 --- a/carta/cpp/core/Data/Region/Region.cpp +++ b/carta/cpp/core/Data/Region/Region.cpp @@ -14,8 +14,6 @@ const QString Region::CORNERS = "corners"; const QString Region::REGION_POLYGON = "Polygon"; const QString Region::REGION_ELLIPSE = "Ellipse"; const QString Region::REGION_TYPE = "regionType"; -const QString Region::XCOORD = "x"; -const QString Region::YCOORD = "y"; class Region::Factory : public Carta::State::CartaObjectFactory { public: @@ -42,8 +40,8 @@ void Region::addCorners( const std::vector< std::pair >& corners m_state.resizeArray( CORNERS, cornerCount ); for ( int i = 0; i < cornerCount; i++ ){ QString eleLookup = Carta::State::UtilState::getLookup( CORNERS, i ); - QString xLookup = Carta::State::UtilState::getLookup( eleLookup, XCOORD ); - QString yLookup = Carta::State::UtilState::getLookup( eleLookup, YCOORD ); + QString xLookup = Carta::State::UtilState::getLookup( eleLookup, Util::XCOORD ); + QString yLookup = Carta::State::UtilState::getLookup( eleLookup, Util::YCOORD ); m_state.insertValue( xLookup, corners[i].first ); m_state.insertValue( yLookup, corners[i].second ); } @@ -57,8 +55,8 @@ std::shared_ptr Region::getInfo() const { std::vector< std::pair > corners( cornerCount ); for( int i = 0; i < cornerCount; i++ ){ QString eleLookup = Carta::State::UtilState::getLookup( CORNERS, i ); - QString xLookup = Carta::State::UtilState::getLookup( eleLookup, XCOORD ); - QString yLookup = Carta::State::UtilState::getLookup( eleLookup, YCOORD ); + QString xLookup = Carta::State::UtilState::getLookup( eleLookup, Util::XCOORD ); + QString yLookup = Carta::State::UtilState::getLookup( eleLookup, Util::YCOORD ); double xValue = m_state.getValue( xLookup ); double yValue = m_state.getValue( yLookup ); corners[i] = std::pair( xValue, yValue ); @@ -133,8 +131,8 @@ void Region::_restoreState( const QString& stateStr ){ m_state.resizeArray( CORNERS, cornerCount ); for ( int i = 0; i < cornerCount; i++ ){ QString eleLookup = Carta::State::UtilState::getLookup( CORNERS, i ); - QString xLookup = Carta::State::UtilState::getLookup( eleLookup, XCOORD ); - QString yLookup = Carta::State::UtilState::getLookup( eleLookup, YCOORD ); + QString xLookup = Carta::State::UtilState::getLookup( eleLookup, Util::XCOORD ); + QString yLookup = Carta::State::UtilState::getLookup( eleLookup, Util::YCOORD ); double xValue = dataState.getValue( xLookup ); double yValue = dataState.getValue( yLookup ); m_state.insertValue( xLookup, xValue ); diff --git a/carta/cpp/core/Data/Region/Region.h b/carta/cpp/core/Data/Region/Region.h index 9be3a580..c6f703a7 100644 --- a/carta/cpp/core/Data/Region/Region.h +++ b/carta/cpp/core/Data/Region/Region.h @@ -14,6 +14,7 @@ namespace Data { class Region : public Carta::State::CartaObject { friend class RegionFactory; + friend class DataFactory; friend class Stack; public: @@ -76,8 +77,6 @@ class Region : public Carta::State::CartaObject { const static QString REGION_POLYGON; const static QString REGION_ELLIPSE; - const static QString XCOORD; - const static QString YCOORD; void _initializeCallbacks(); void _initializeState(); @@ -106,6 +105,8 @@ class Region : public Carta::State::CartaObject { // is created graphically, the id will be just an index. void _setUserId( const QString& fileName, int index ); + Region( const Region& other); + Region& operator=( const Region& other ); }; } diff --git a/carta/cpp/core/Data/Region/RegionFactory.h b/carta/cpp/core/Data/Region/RegionFactory.h index 0030f4a4..097c90c9 100644 --- a/carta/cpp/core/Data/Region/RegionFactory.h +++ b/carta/cpp/core/Data/Region/RegionFactory.h @@ -43,6 +43,9 @@ class RegionFactory { */ RegionFactory(); + RegionFactory( const RegionFactory& other); + RegionFactory& operator=( const RegionFactory& other ); + }; } } diff --git a/carta/cpp/core/Data/Snapshot/Snapshot.h b/carta/cpp/core/Data/Snapshot/Snapshot.h index 48ae130d..f4c37051 100644 --- a/carta/cpp/core/Data/Snapshot/Snapshot.h +++ b/carta/cpp/core/Data/Snapshot/Snapshot.h @@ -74,7 +74,6 @@ class Snapshot { void _initializeState( const QString& name ); Carta::State::StateInterface m_state; - }; diff --git a/carta/cpp/core/Data/Statistics/Statistics.h b/carta/cpp/core/Data/Statistics/Statistics.h index d5a6ed87..e1747cdd 100755 --- a/carta/cpp/core/Data/Statistics/Statistics.h +++ b/carta/cpp/core/Data/Statistics/Statistics.h @@ -133,7 +133,7 @@ private slots: Carta::State::StateInterface m_stateData; Statistics( const Statistics& other); - Statistics operator=( const Statistics& other ); + Statistics& operator=( const Statistics& other ); }; } } diff --git a/carta/cpp/core/Data/Units/UnitsFrequency.cpp b/carta/cpp/core/Data/Units/UnitsFrequency.cpp new file mode 100644 index 00000000..d3f03006 --- /dev/null +++ b/carta/cpp/core/Data/Units/UnitsFrequency.cpp @@ -0,0 +1,83 @@ +#include "UnitsFrequency.h" +#include "CartaLib/CartaLib.h" +#include "State/UtilState.h" +#include +#include + +namespace Carta { + +namespace Data { + +const QString UnitsFrequency::UNIT_LIST = "units"; +const QString UnitsFrequency::CLASS_NAME = "UnitsFrequency"; +const QString UnitsFrequency::UNIT_HZ = "Hz"; +const QString UnitsFrequency::UNIT_MHZ = "MHz"; +const QString UnitsFrequency::UNIT_GHZ = "GHz"; + + +class UnitsFrequency::Factory : public Carta::State::CartaObjectFactory { +public: + + Factory(): + CartaObjectFactory(CLASS_NAME){ + }; + + Carta::State::CartaObject * create (const QString & path, const QString & id){ + return new UnitsFrequency (path, id); + } +}; + + +bool UnitsFrequency::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new UnitsFrequency::Factory()); + + +UnitsFrequency::UnitsFrequency( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + _initializeDefaultState(); +} + + +QString UnitsFrequency::getActualUnits( const QString& unitStr ) const { + QString actualUnits; + int dataCount = m_state.getArraySize( UNIT_LIST ); + for ( int i = 0; i < dataCount; i++ ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); + QString unitName = m_state.getValue( key ); + int result = QString::compare( unitName, unitStr, Qt::CaseInsensitive ); + if ( result == 0 ){ + actualUnits = unitName; + break; + } + } + return actualUnits; +} + + +QString UnitsFrequency::getDefault() const { + return UNIT_HZ; +} + + +void UnitsFrequency::_initUnit( int * index, const QString& unit ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, *index ); + m_state.setValue( key, unit ); + *index = *index + 1; +} + + +void UnitsFrequency::_initializeDefaultState(){ + m_state.insertArray( UNIT_LIST, 3 ); + int i = 0; + _initUnit( &i, UNIT_HZ ); + _initUnit( &i, UNIT_MHZ ); + _initUnit( &i, UNIT_GHZ ); + m_state.flushState(); +} + + +UnitsFrequency::~UnitsFrequency(){ + +} +} +} diff --git a/carta/cpp/core/Data/Units/UnitsFrequency.h b/carta/cpp/core/Data/Units/UnitsFrequency.h new file mode 100644 index 00000000..90329634 --- /dev/null +++ b/carta/cpp/core/Data/Units/UnitsFrequency.h @@ -0,0 +1,57 @@ +/*** + * List of available frequency units. + * + */ + +#pragma once + +#include "State/ObjectManager.h" +#include "State/StateInterface.h" +#include + +namespace Carta { + +namespace Data { + +class UnitsFrequency : public Carta::State::CartaObject { + +public: + + /** + * Returns the default frequency unit. + * @return the default frequency unit. + */ + QString getDefault() const; + + /** + * Translates a possible case insensitive unit string into one + * that matches the actual units exactly. + * @param unitStr - an identifier for frequency units that may not match + * in case. + * @return - the actual frequency units or an empty string if the units are + * not recognized. + */ + QString getActualUnits( const QString& unitStr ) const; + + const static QString CLASS_NAME; + const static QString UNIT_HZ; + const static QString UNIT_MHZ; + const static QString UNIT_GHZ; + const static QString UNIT_LIST; + virtual ~UnitsFrequency(); + +private: + + void _initializeDefaultState(); + void _initUnit( int * index, const QString& unit ); + + static bool m_registered; + UnitsFrequency( const QString& path, const QString& id ); + class Factory; + + + UnitsFrequency( const UnitsFrequency& other); + UnitsFrequency& operator=( const UnitsFrequency& other ); +}; +} +} diff --git a/carta/cpp/core/Data/Units/UnitsIntensity.cpp b/carta/cpp/core/Data/Units/UnitsIntensity.cpp new file mode 100644 index 00000000..82711fee --- /dev/null +++ b/carta/cpp/core/Data/Units/UnitsIntensity.cpp @@ -0,0 +1,120 @@ +#include "UnitsIntensity.h" +#include "CartaLib/CartaLib.h" +#include "State/UtilState.h" +#include +#include + +namespace Carta { + +namespace Data { + +const QString UnitsIntensity::UNIT_LIST = "units"; +const QString UnitsIntensity::CLASS_NAME = "UnitsIntensity"; +const QString UnitsIntensity::NAME_PEAK = "Fraction of Peak"; +const QString UnitsIntensity::NAME_JYBEAM = "Jy/beam"; +const QString UnitsIntensity::NAME_JYSR = "MJy/sr"; +const QString UnitsIntensity::NAME_JYARCSEC = "Jy/arcsec^2"; +const QString UnitsIntensity::NAME_JY = "Jy"; +const QString UnitsIntensity::NAME_KELVIN = "Kelvin"; + + +class UnitsIntensity::Factory : public Carta::State::CartaObjectFactory { +public: + + Factory(): + CartaObjectFactory(CLASS_NAME){ + }; + + Carta::State::CartaObject * create (const QString & path, const QString & id) + { + return new UnitsIntensity (path, id); + } +}; + + + +bool UnitsIntensity::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new UnitsIntensity::Factory()); + +UnitsIntensity::UnitsIntensity( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + _initializeDefaultState(); +} + +QString UnitsIntensity::getActualUnits( const QString& unitStr ) const { + QString actualUnits; + int dataCount = m_state.getArraySize( UNIT_LIST ); + for ( int i = 0; i < dataCount; i++ ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); + QString unitName = m_state.getValue( key ); + int result = QString::compare( unitName, unitStr, Qt::CaseInsensitive ); + if ( result == 0 ){ + actualUnits = unitName; + break; + } + } + return actualUnits; +} + + +QString UnitsIntensity::getDefault() const { + return m_defaultUnit; +} + +void UnitsIntensity::_initUnit( int * index, const QString& name ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, *index ); + m_state.setValue( key, name ); + *index = *index + 1; +} + +void UnitsIntensity::_initializeDefaultState(){ + m_state.insertArray( UNIT_LIST, 4 ); + int i = 0; + _initUnit( &i, NAME_JYBEAM ); + _initUnit( &i, NAME_JYSR ); + _initUnit( &i, NAME_JYARCSEC ); + _initUnit( &i, NAME_KELVIN ); + m_defaultUnit = NAME_JYBEAM; + m_state.flushState(); +} + + + +void UnitsIntensity::resetUnits( Carta::Lib::ProfileInfo::AggregateType stat ){ + QStringList units; + if ( stat == Carta::Lib::ProfileInfo::AggregateType::SUM ){ + const QString PIXELS = "*pixels"; + units.append( NAME_JYBEAM + PIXELS ); + units.append( NAME_JYSR + PIXELS ); + units.append( NAME_JYARCSEC + PIXELS ); + units.append( NAME_KELVIN + PIXELS ); + m_defaultUnit = NAME_JYBEAM + PIXELS; + } + else if ( stat == Carta::Lib::ProfileInfo::AggregateType::FLUX_DENSITY ){ + units.append( NAME_JY ); + m_defaultUnit = NAME_JY; + } + else { + units.append( NAME_JYBEAM ); + units.append( NAME_JYSR ); + units.append( NAME_JYARCSEC ); + units.append( NAME_KELVIN ); + m_defaultUnit = NAME_JYBEAM; + } + int unitCount = units.size(); + m_state.resizeArray( UNIT_LIST, unitCount ); + for ( int i = 0; i < unitCount; i++ ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); + m_state.setValue( key, units[i] ); + } + m_state.flushState(); +} + + + + +UnitsIntensity::~UnitsIntensity(){ + +} +} +} diff --git a/carta/cpp/core/Data/Profile/IntensityUnits.h b/carta/cpp/core/Data/Units/UnitsIntensity.h similarity index 66% rename from carta/cpp/core/Data/Profile/IntensityUnits.h rename to carta/cpp/core/Data/Units/UnitsIntensity.h index 87256239..5846074e 100644 --- a/carta/cpp/core/Data/Profile/IntensityUnits.h +++ b/carta/cpp/core/Data/Units/UnitsIntensity.h @@ -7,13 +7,14 @@ #include "State/ObjectManager.h" #include "State/StateInterface.h" +#include "CartaLib/ProfileInfo.h" #include namespace Carta { namespace Data { -class IntensityUnits : public Carta::State::CartaObject { +class UnitsIntensity : public Carta::State::CartaObject { public: @@ -33,10 +34,17 @@ class IntensityUnits : public Carta::State::CartaObject { */ QString getActualUnits( const QString& unitStr ) const; + /** + * Intensity units depend on how profile points are aggregate; reset + * the available intensity units based on the aggregation method. + * @param stat - the method used to aggregate profile points. + */ + void resetUnits( Carta::Lib::ProfileInfo::AggregateType stat ); + const static QString CLASS_NAME; const static QString UNIT_LIST; - virtual ~IntensityUnits(); + virtual ~UnitsIntensity(); private: @@ -47,15 +55,17 @@ class IntensityUnits : public Carta::State::CartaObject { const static QString NAME_JY; const static QString NAME_KELVIN; + QString m_defaultUnit; + void _initializeDefaultState(); void _initUnit( int * index, const QString& name); static bool m_registered; - IntensityUnits( const QString& path, const QString& id ); + UnitsIntensity( const QString& path, const QString& id ); class Factory; - IntensityUnits( const IntensityUnits& other); - IntensityUnits& operator=( const IntensityUnits& other ); + UnitsIntensity( const UnitsIntensity& other); + UnitsIntensity& operator=( const UnitsIntensity& other ); }; } } diff --git a/carta/cpp/core/Data/Units/UnitsSpectral.cpp b/carta/cpp/core/Data/Units/UnitsSpectral.cpp new file mode 100644 index 00000000..4ca55bd4 --- /dev/null +++ b/carta/cpp/core/Data/Units/UnitsSpectral.cpp @@ -0,0 +1,107 @@ +#include "CartaLib/CartaLib.h" +#include "State/UtilState.h" +#include +#include +#include "UnitsSpectral.h" +#include "UnitsFrequency.h" +#include "UnitsWavelength.h" + +namespace Carta { + +namespace Data { + +const QString UnitsSpectral::UNIT_LIST = "units"; +const QString UnitsSpectral::CLASS_NAME = "UnitsSpectral"; +const QString UnitsSpectral::NAME_VELOCITY_RADIO = "Radio Velocity"; +const QString UnitsSpectral::NAME_VELOCITY_OPTICAL = "Optical Velocity"; +const QString UnitsSpectral::NAME_FREQUENCY = "Frequency"; +const QString UnitsSpectral::NAME_WAVELENGTH = "Wavelength"; +const QString UnitsSpectral::NAME_WAVELENGTH_OPTICAL = "Air Wavelength"; +const QString UnitsSpectral::NAME_CHANNEL = "Channel"; +const QString UnitsSpectral::UNIT_MS = "m/s"; +const QString UnitsSpectral::UNIT_KMS = "km/s"; + + +class UnitsSpectral::Factory : public Carta::State::CartaObjectFactory { +public: + + Factory(): + CartaObjectFactory(CLASS_NAME){ + }; + + Carta::State::CartaObject * create (const QString & path, const QString & id){ + return new UnitsSpectral (path, id); + } +}; + + +bool UnitsSpectral::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new UnitsSpectral::Factory()); + + +UnitsSpectral::UnitsSpectral( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + _initializeDefaultState(); +} + + +QString UnitsSpectral::getActualUnits( const QString& unitStr ) const { + QString actualUnits; + int dataCount = m_state.getArraySize( UNIT_LIST ); + for ( int i = 0; i < dataCount; i++ ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); + QString unitName = m_state.getValue( key ); + int result = QString::compare( unitName, unitStr, Qt::CaseInsensitive ); + if ( result == 0 ){ + actualUnits = unitName; + break; + } + } + return actualUnits; +} + + +QString UnitsSpectral::getDefault() const { + return NAME_CHANNEL; +} + + +void UnitsSpectral::_initUnit( int * index, const QString& name, const QString& unit ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, *index ); + QString value = name; + if ( unit.length() > 0 ){ + value = value + "("+unit+")"; + } + m_state.setValue( key, value ); + *index = *index + 1; +} + + +void UnitsSpectral::_initializeDefaultState(){ + m_state.insertArray( UNIT_LIST, 16 ); + int i = 0; + _initUnit( &i, NAME_VELOCITY_RADIO, UNIT_MS ); + _initUnit( &i, NAME_VELOCITY_RADIO, UNIT_KMS ); + _initUnit( &i, NAME_VELOCITY_OPTICAL, UNIT_MS ); + _initUnit( &i, NAME_VELOCITY_OPTICAL, UNIT_KMS ); + _initUnit( &i, NAME_FREQUENCY, UnitsFrequency::UNIT_HZ ); + _initUnit( &i, NAME_FREQUENCY, UnitsFrequency::UNIT_MHZ ); + _initUnit( &i, NAME_FREQUENCY, UnitsFrequency::UNIT_GHZ ); + _initUnit( &i, NAME_WAVELENGTH, UnitsWavelength::UNIT_MM ); + _initUnit( &i, NAME_WAVELENGTH, UnitsWavelength::UNIT_UM ); + _initUnit( &i, NAME_WAVELENGTH, UnitsWavelength::UNIT_NM ); + _initUnit( &i, NAME_WAVELENGTH, UnitsWavelength::UNIT_ANGSTROM ); + _initUnit( &i, NAME_WAVELENGTH_OPTICAL, UnitsWavelength::UNIT_MM ); + _initUnit( &i, NAME_WAVELENGTH_OPTICAL, UnitsWavelength::UNIT_UM ); + _initUnit( &i, NAME_WAVELENGTH_OPTICAL, UnitsWavelength::UNIT_NM ); + _initUnit( &i, NAME_WAVELENGTH_OPTICAL, UnitsWavelength::UNIT_ANGSTROM ); + _initUnit( &i, NAME_CHANNEL, "" ); + m_state.flushState(); +} + + +UnitsSpectral::~UnitsSpectral(){ + +} +} +} diff --git a/carta/cpp/core/Data/Profile/SpectralUnits.h b/carta/cpp/core/Data/Units/UnitsSpectral.h similarity index 71% rename from carta/cpp/core/Data/Profile/SpectralUnits.h rename to carta/cpp/core/Data/Units/UnitsSpectral.h index b31b93d0..120c16b7 100644 --- a/carta/cpp/core/Data/Profile/SpectralUnits.h +++ b/carta/cpp/core/Data/Units/UnitsSpectral.h @@ -13,7 +13,7 @@ namespace Carta { namespace Data { -class SpectralUnits : public Carta::State::CartaObject { +class UnitsSpectral : public Carta::State::CartaObject { public: @@ -35,35 +35,29 @@ class SpectralUnits : public Carta::State::CartaObject { const static QString CLASS_NAME; const static QString NAME_FREQUENCY; - const static QString UNIT_HZ; const static QString UNIT_LIST; - virtual ~SpectralUnits(); + virtual ~UnitsSpectral(); private: const static QString NAME_VELOCITY_RADIO; const static QString NAME_VELOCITY_OPTICAL; const static QString NAME_WAVELENGTH; + const static QString NAME_WAVELENGTH_OPTICAL; const static QString NAME_CHANNEL; const static QString UNIT_MS; const static QString UNIT_KMS; - const static QString UNIT_MHZ; - const static QString UNIT_GHZ; - const static QString UNIT_MM; - const static QString UNIT_UM; - const static QString UNIT_NM; - const static QString UNIT_ANGSTROM; void _initializeDefaultState(); void _initUnit( int * index, const QString& name, const QString& unit ); static bool m_registered; - SpectralUnits( const QString& path, const QString& id ); + UnitsSpectral( const QString& path, const QString& id ); class Factory; - SpectralUnits( const SpectralUnits& other); - SpectralUnits& operator=( const SpectralUnits& other ); + UnitsSpectral( const UnitsSpectral& other); + UnitsSpectral& operator=( const UnitsSpectral& other ); }; } } diff --git a/carta/cpp/core/Data/Units/UnitsWavelength.cpp b/carta/cpp/core/Data/Units/UnitsWavelength.cpp new file mode 100644 index 00000000..3780d913 --- /dev/null +++ b/carta/cpp/core/Data/Units/UnitsWavelength.cpp @@ -0,0 +1,84 @@ +#include "UnitsWavelength.h" +#include "CartaLib/CartaLib.h" +#include "State/UtilState.h" +#include +#include + +namespace Carta { + +namespace Data { + +const QString UnitsWavelength::UNIT_LIST = "units"; +const QString UnitsWavelength::CLASS_NAME = "UnitsWavelength"; +const QString UnitsWavelength::UNIT_MM = "mm"; +const QString UnitsWavelength::UNIT_UM = "um"; +const QString UnitsWavelength::UNIT_NM = "nm"; +const QString UnitsWavelength::UNIT_ANGSTROM = "Angstrom"; + +class UnitsWavelength::Factory : public Carta::State::CartaObjectFactory { +public: + + Factory(): + CartaObjectFactory(CLASS_NAME){ + }; + + Carta::State::CartaObject * create (const QString & path, const QString & id){ + return new UnitsWavelength (path, id); + } +}; + + +bool UnitsWavelength::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new UnitsWavelength::Factory()); + + +UnitsWavelength::UnitsWavelength( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + _initializeDefaultState(); +} + + +QString UnitsWavelength::getActualUnits( const QString& unitStr ) const { + QString actualUnits; + int dataCount = m_state.getArraySize( UNIT_LIST ); + for ( int i = 0; i < dataCount; i++ ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, i ); + QString unitName = m_state.getValue( key ); + int result = QString::compare( unitName, unitStr, Qt::CaseInsensitive ); + if ( result == 0 ){ + actualUnits = unitName; + break; + } + } + return actualUnits; +} + + +QString UnitsWavelength::getDefault() const { + return UNIT_MM; +} + + +void UnitsWavelength::_initUnit( int * index, const QString& unit ){ + QString key = Carta::State::UtilState::getLookup( UNIT_LIST, *index ); + m_state.setValue( key, unit ); + *index = *index + 1; +} + + +void UnitsWavelength::_initializeDefaultState(){ + m_state.insertArray( UNIT_LIST, 4 ); + int i = 0; + _initUnit( &i, UNIT_MM ); + _initUnit( &i, UNIT_UM ); + _initUnit( &i, UNIT_NM ); + _initUnit( &i, UNIT_ANGSTROM ); + m_state.flushState(); +} + + +UnitsWavelength::~UnitsWavelength(){ + +} +} +} diff --git a/carta/cpp/core/Data/Units/UnitsWavelength.h b/carta/cpp/core/Data/Units/UnitsWavelength.h new file mode 100644 index 00000000..8661bdbf --- /dev/null +++ b/carta/cpp/core/Data/Units/UnitsWavelength.h @@ -0,0 +1,58 @@ +/*** + * List of available wavelength units. + * + */ + +#pragma once + +#include "State/ObjectManager.h" +#include "State/StateInterface.h" +#include + +namespace Carta { + +namespace Data { + +class UnitsWavelength : public Carta::State::CartaObject { + +public: + + /** + * Returns the default wavelength unit. + * @return the default wavelength unit. + */ + QString getDefault() const; + + /** + * Translates a possible case insensitive unit string into one + * that matches the actual units exactly. + * @param unitStr - an identifier for wavelength units that may not match + * in case. + * @return - the actual wavelength units or an empty string if the units are + * not recognized. + */ + QString getActualUnits( const QString& unitStr ) const; + + const static QString CLASS_NAME; + const static QString UNIT_LIST; + const static QString UNIT_MM; + const static QString UNIT_UM; + const static QString UNIT_NM; + const static QString UNIT_ANGSTROM; + virtual ~UnitsWavelength(); + +private: + + void _initializeDefaultState(); + void _initUnit( int * index, const QString& unit ); + + static bool m_registered; + UnitsWavelength( const QString& path, const QString& id ); + class Factory; + + + UnitsWavelength( const UnitsWavelength& other); + UnitsWavelength& operator=( const UnitsWavelength& other ); +}; +} +} diff --git a/carta/cpp/core/Data/Util.cpp b/carta/cpp/core/Data/Util.cpp index 88d919cb..179b28b9 100644 --- a/carta/cpp/core/Data/Util.cpp +++ b/carta/cpp/core/Data/Util.cpp @@ -26,7 +26,10 @@ const QString Util::TYPE = "type"; const QString Util::UNITS = "units"; const QString Util::VISIBLE = "visible"; const QString Util::POINTER_MOVE = "pointer-move"; +const QString Util::SIGNIFICANT_DIGITS = "significantDigits"; const QString Util::VIEW = "view"; +const QString Util::XCOORD = "x"; +const QString Util::YCOORD = "y"; const int Util::MAX_COLOR = 255; Util::Util( ) { diff --git a/carta/cpp/core/Data/Util.h b/carta/cpp/core/Data/Util.h index 5d0833b2..394304ac 100644 --- a/carta/cpp/core/Data/Util.h +++ b/carta/cpp/core/Data/Util.h @@ -122,16 +122,21 @@ class Util { static const QString NAME; static const QString PEN_WIDTH; static const QString POINTER_MOVE; + static const QString SIGNIFICANT_DIGITS; static const QString TAB_INDEX; static const QString TYPE; static const QString UNITS; static const QString VISIBLE; static const QString VIEW; + static const QString XCOORD; + static const QString YCOORD; static const int MAX_COLOR; private: Util(); virtual ~Util(); + Util( const Util& other); + Util& operator=( const Util& other ); static const QString TRUE; static const QString FALSE; diff --git a/carta/cpp/core/Data/ViewManager.cpp b/carta/cpp/core/Data/ViewManager.cpp index f02439e0..542fb822 100644 --- a/carta/cpp/core/Data/ViewManager.cpp +++ b/carta/cpp/core/Data/ViewManager.cpp @@ -6,6 +6,7 @@ #include "Data/Image/Controller.h" #include "Data/Image/CoordinateSystems.h" #include "Data/Image/Grid/Themes.h" +#include "Data/Image/Grid/Fonts.h" #include "Data/Image/Grid/LabelFormats.h" #include "Data/Image/Contour/ContourGenerateModes.h" #include "Data/Image/Contour/ContourSpacingModes.h" @@ -13,6 +14,7 @@ #include "Data/Image/LayerCompositionModes.h" #include "Data/Histogram/ChannelUnits.h" #include "Data/DataLoader.h" +#include "Data/Colormap/Gamma.h" #include "Data/Colormap/TransformsData.h" #include "Data/Colormap/TransformsImage.h" #include "Data/Error/ErrorManager.h" @@ -25,18 +27,21 @@ #include "Data/Preferences/Preferences.h" #include "Data/Preferences/PreferencesSave.h" #include "Data/Profile/ProfilePlotStyles.h" -#include "Data/Profile/IntensityUnits.h" #include "Data/Profile/Profiler.h" #include "Data/Profile/ProfileStatistics.h" #include "Data/Profile/GenerateModes.h" -#include "Data/Profile/SpectralUnits.h" #include "Data/Snapshot/Snapshots.h" #include "Data/Statistics/Statistics.h" #include "Data/ViewPlugins.h" +#include "Data/Units/UnitsFrequency.h" +#include "Data/Units/UnitsIntensity.h" +#include "Data/Units/UnitsSpectral.h" +#include "Data/Units/UnitsWavelength.h" #include "Data/Util.h" #include "State/UtilState.h" #include +#include #include namespace Carta { @@ -79,7 +84,9 @@ ViewManager::ViewManager( const QString& path, const QString& id) Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); + Util::findSingletonObject(); Util::findSingletonObject(); + Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); @@ -92,12 +99,13 @@ ViewManager::ViewManager( const QString& path, const QString& id) Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); - - Util::findSingletonObject(); - Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); + Util::findSingletonObject(); + Util::findSingletonObject(); + Util::findSingletonObject(); + Util::findSingletonObject(); _initCallbacks(); _initializeDefaultState(); _makeDataLoader(); @@ -1089,7 +1097,62 @@ ViewManager::~ViewManager(){ _clearProfilers( 0, m_profilers.size() ); _clearControllers( 0, m_controllers.size() ); - //objMan->printObjects(); + //Delete the statics + CartaObject* obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + obj = Util::findSingletonObject(); + delete obj; + + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + objMan->printObjects(); } } } diff --git a/carta/cpp/core/Plot2D/Plot.h b/carta/cpp/core/Plot2D/Plot.h index aa275e51..00ea76ea 100644 --- a/carta/cpp/core/Plot2D/Plot.h +++ b/carta/cpp/core/Plot2D/Plot.h @@ -60,6 +60,8 @@ class Plot : public QwtPlot { QwtPlot::LegendPosition _calculatePosition( const QString& pos ) const; QwtLegend* m_externalLegend; QwtPlotLegendItem* m_legendItem; + Plot( const Plot& other); + Plot& operator=( const Plot& other ); }; } diff --git a/carta/cpp/core/Plot2D/Plot2D.h b/carta/cpp/core/Plot2D/Plot2D.h index 1698d006..22ddae39 100644 --- a/carta/cpp/core/Plot2D/Plot2D.h +++ b/carta/cpp/core/Plot2D/Plot2D.h @@ -126,6 +126,9 @@ class Plot2D { double m_minValueY; QString m_id; + Plot2D( const Plot2D& other); + Plot2D& operator=( const Plot2D& other ); + }; } diff --git a/carta/cpp/core/Plot2D/Plot2DGenerator.cpp b/carta/cpp/core/Plot2D/Plot2DGenerator.cpp index 4370d4ce..2bf7e16f 100755 --- a/carta/cpp/core/Plot2D/Plot2DGenerator.cpp +++ b/carta/cpp/core/Plot2D/Plot2DGenerator.cpp @@ -165,10 +165,8 @@ std::pair Plot2DGenerator::getRangeColor(bool* valid ) const { bool Plot2DGenerator::isSelectionOnCanvas( int xPos ) const { bool selectionOnCanvas = false; if ( xPos >= 0 ){ - //Get the ratio of the canvas margin to the plot width; - float plotWidth = m_plot->size().width(); - float canvasWidth = m_plot->canvas()->size().width(); - float plotMargin = plotWidth - canvasWidth; + //Make sure the point is beyond the left canvas margin + float plotMargin = m_plot->axisWidget( QwtPlot::yLeft )->size().width(); if ( xPos > plotMargin ){ selectionOnCanvas = true; } @@ -201,8 +199,6 @@ double Plot2DGenerator::getVLinePosition( bool* valid ) const { std::pair Plot2DGenerator::getWorldPt(int x, int y, int width, int height ) const { QSize plotSize = m_plot->size(); - //qDebug() << "plotSize width="<canvas(); QSize canvasSize = canvas->size(); double xMargin = plotSize.width() - canvasSize.width(); @@ -562,6 +558,11 @@ Plot2DGenerator::~Plot2DGenerator(){ m_vLine->detach(); delete m_vLine; } + if ( m_gridLines ){ + m_gridLines->detach(); + delete m_gridLines; + } + delete m_gridLines; delete m_range; delete m_plot; } diff --git a/carta/cpp/core/Plot2D/Plot2DGenerator.h b/carta/cpp/core/Plot2D/Plot2DGenerator.h index 752253d3..f2c576a8 100755 --- a/carta/cpp/core/Plot2D/Plot2DGenerator.h +++ b/carta/cpp/core/Plot2D/Plot2DGenerator.h @@ -333,6 +333,9 @@ class Plot2DGenerator{ bool m_logScale; QFont m_font; PlotType m_plotType; + + Plot2DGenerator( const Plot2DGenerator& other); + Plot2DGenerator& operator=( const Plot2DGenerator& other ); }; } } diff --git a/carta/cpp/core/Plot2D/Plot2DHistogram.h b/carta/cpp/core/Plot2D/Plot2DHistogram.h index 42c80f14..7d8cbe21 100644 --- a/carta/cpp/core/Plot2D/Plot2DHistogram.h +++ b/carta/cpp/core/Plot2D/Plot2DHistogram.h @@ -71,6 +71,8 @@ class Plot2DHistogram : public Plot2D, public QwtPlotHistogram { QVector< QwtIntervalSample > m_data; mutable double m_lastY; mutable double m_lastX; + Plot2DHistogram( const Plot2DHistogram& other); + Plot2DHistogram& operator=( const Plot2DHistogram& other ); }; } diff --git a/carta/cpp/core/Plot2D/Plot2DProfile.cpp b/carta/cpp/core/Plot2D/Plot2DProfile.cpp index 664933ff..93944c1c 100644 --- a/carta/cpp/core/Plot2D/Plot2DProfile.cpp +++ b/carta/cpp/core/Plot2D/Plot2DProfile.cpp @@ -1,7 +1,9 @@ #include "Plot2DProfile.h" #include "Data/Profile/ProfilePlotStyles.h" -#include #include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" +#include +#include + #include namespace Carta { @@ -30,17 +32,35 @@ void Plot2DProfile::drawLines (QPainter *painter, const QwtScaleMap &xMap, QPen curvePen( m_defaultColor ); curvePen.setStyle( m_penStyle ); painter->setPen( curvePen ); - - QwtPlotCurve::drawLines( painter, xMap, yMap, canvasRect, from, to ); + if ( from != to ){ + QwtPlotCurve::drawLines( painter, xMap, yMap, canvasRect, from, to ); + } + else { + drawSymbol( painter, xMap, yMap, canvasRect, from, to ); + } } -void Plot2DProfile::drawSteps (QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRectF &canvasRect, int from, int to) const { + +void Plot2DProfile::drawSteps (QPainter *painter, const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to) const { QPen curvePen( m_defaultColor ); curvePen.setStyle( m_penStyle ); painter->setPen( curvePen ); + if ( from != to ){ + QwtPlotCurve::drawSteps( painter, xMap, yMap, canvasRect, from, to ); + } + else { + drawSymbol( painter, xMap, yMap, canvasRect, from, to ); + } +} - QwtPlotCurve::drawSteps( painter, xMap, yMap, canvasRect, from, to ); +void Plot2DProfile::drawSymbol( QPainter* painter, const QwtScaleMap & xMap, + const QwtScaleMap & yMap, const QRectF & canvasRect, int from, int to ) const{ + QPen curvePen( m_defaultColor ); + QBrush brush( m_defaultColor ); + QSize symbolSize( 10, 10 ); + QwtSymbol symbol ( QwtSymbol::Diamond, brush, curvePen, symbolSize ); + drawSymbols( painter, symbol, xMap, yMap, canvasRect, from, to ); } QwtGraphic Plot2DProfile::legendIcon( int /*index*/, const QSizeF& iconSize ) const { @@ -67,7 +87,7 @@ void Plot2DProfile::setData ( std::vector > datas ){ int dataCount = datas.size(); m_datasX.resize( dataCount ); m_datasY.resize( dataCount ); - if ( dataCount > 0 ){ + if ( dataCount > 1 ){ m_maxValueY = -1 * std::numeric_limits::max(); m_minValueY = std::numeric_limits::max(); for ( int i = 0; i < dataCount; i++ ){ @@ -81,6 +101,11 @@ void Plot2DProfile::setData ( std::vector > datas ){ } } } + else if ( dataCount == 1 ){ + const double INC = 0.0000001; + m_minValueY = m_datasY[0] - INC; + m_maxValueY = m_datasY[0] + INC; + } //No data so just use bogus bounds else { m_maxValueY = 1; diff --git a/carta/cpp/core/Plot2D/Plot2DProfile.h b/carta/cpp/core/Plot2D/Plot2DProfile.h index 2e437167..3d59baf4 100644 --- a/carta/cpp/core/Plot2D/Plot2DProfile.h +++ b/carta/cpp/core/Plot2D/Plot2DProfile.h @@ -90,11 +90,16 @@ class Plot2DProfile : public Plot2D, public QwtPlotCurve { const QRectF &canvasRect, int from, int to) const; virtual void drawSteps (QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to) const; + //This method was put in so that profiles consisting of a single point could be drawn. + void drawSymbol( QPainter* painter, const QwtScaleMap & xMap, + const QwtScaleMap & yMap, const QRectF & canvasRect, int from, int to ) const; private: std::vector m_datasX; std::vector m_datasY; + Plot2DProfile( const Plot2DProfile& other); + Plot2DProfile& operator=( const Plot2DProfile& other ); }; diff --git a/carta/cpp/core/ScriptedClient/ScriptFacade.cpp b/carta/cpp/core/ScriptedClient/ScriptFacade.cpp index ea0e237b..3538717b 100644 --- a/carta/cpp/core/ScriptedClient/ScriptFacade.cpp +++ b/carta/cpp/core/ScriptedClient/ScriptFacade.cpp @@ -268,6 +268,33 @@ QStringList ScriptFacade::invertColorMap( const QString& colormapId, const QStri return resultList; } +QStringList ScriptFacade::setNanDefault( const QString& colormapId, const QString& nanDefaultStr ) { + QStringList resultList; + Carta::State::CartaObject* obj = _getObject( colormapId ); + if ( obj != nullptr ){ + Carta::Data::Colormap* colormap = dynamic_cast(obj); + if ( colormap != nullptr ){ + bool nanDefault = false; + bool validBool = true; + nanDefault = Carta::Data::Util::toBool( nanDefaultStr, &validBool ); + if ( validBool ){ + QString result = colormap->setNanDefault(nanDefault); + resultList = QStringList( result ); + } + else { + resultList = _logErrorMessage( ERROR, "An unrecognized parameter was passed to set nan default: " + nanDefaultStr ); + } + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, COLORMAP_VIEW_NOT_FOUND + colormapId ); + } + return resultList; +} + QStringList ScriptFacade::setColorMix( const QString& colormapId, double red, double green, double blue ){ QStringList resultList; Carta::State::CartaObject* obj = _getObject( colormapId ); @@ -937,6 +964,206 @@ QStringList ScriptFacade::closeImage( const QString& controlId, const QString& i return resultList; } +QStringList ScriptFacade::showImage( const QString& controlId, const QString& imageName ) { + QStringList resultList; + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + QString result = controller->setImageVisibility( imageName, true ); + resultList = QStringList( result ); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + return resultList; +} + +QStringList ScriptFacade::hideImage( const QString& controlId, const QString& imageName ) { + QStringList resultList; + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + QString result = controller->setImageVisibility( imageName, false ); + resultList = QStringList( result ); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + return resultList; +} + + +QStringList ScriptFacade::setPanZoomAll(const QString& controlId, const QString& setPanZoomAllFlagStr) { + QStringList resultList; + + bool validBool = false; + bool setPanZoomAllFlag= Carta::Data::Util::toBool( setPanZoomAllFlagStr, &validBool ); + if ( validBool ) { + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + controller->setPanZoomAll( setPanZoomAllFlag ); + resultList = QStringList( "" ); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + } + else { + resultList = _logErrorMessage( ERROR, "Set Pan Zoom All Flag must be true/false " + setPanZoomAllFlagStr); + } + return resultList; +} + + +QStringList ScriptFacade::setMaskAlpha(const QString& controlId, const QString& imageName, const QString& alphaAmountStr) { + QStringList resultList; + + bool validInt = false; + int alphaAmount = alphaAmountStr.toInt( &validInt ); + if ( validInt && alphaAmount >= 0 && alphaAmount <= 255 ) { + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + resultList = QStringList(controller->setMaskAlpha( imageName, alphaAmount )); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + } + else { + resultList = _logErrorMessage( ERROR, "alphaAmount must be valid int between 0 and 255 " + alphaAmountStr ); + } + return resultList; +} + +QStringList ScriptFacade::setMaskColor(const QString& controlId, const QString& imageName, + const QString& redAmountStr, const QString& greenAmountStr, + const QString& blueAmountStr) { + QStringList resultList; + + bool validIntRed = false; + int redAmount = redAmountStr.toInt( &validIntRed ); + + bool validIntGreen = false; + int greenAmount = greenAmountStr.toInt(&validIntRed ); + + bool validIntBlue = false; + int blueAmount = blueAmountStr.toInt( &validIntRed ); + + if ( validIntRed && validIntGreen && validIntBlue && + redAmount >= 0 && redAmount <= 255 && + greenAmount >= 0 && greenAmount <= 255 && + blueAmount >= 0 && blueAmount <= 255 ) { + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + resultList = controller->setMaskColor( imageName, redAmount, greenAmount, blueAmount ); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + } + else { + resultList = _logErrorMessage( ERROR, "redAmount/greenAmount/blueAmount must be valid int between 0 and 255 " + redAmountStr + + "/" + greenAmountStr + "/" + blueAmountStr); + } + return resultList; +} + +QStringList ScriptFacade::setStackSelectAuto( const QString& controlId, const QString& stackSelectFlagStr) { + QStringList resultList; + + bool validBool = false; + bool stackSelectFlag = Carta::Data::Util::toBool( stackSelectFlagStr, &validBool ); + if ( validBool ) { + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + controller->setStackSelectAuto( stackSelectFlag ); + resultList = QStringList( "" ); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + } + else { + resultList = _logErrorMessage( ERROR, "Set Stack Select Auto parameter must be true/false " + stackSelectFlagStr); + } + return resultList; +} + +QStringList ScriptFacade::setCompositionMode( const QString& controlId, const QString& imageName) { + QStringList resultList; + + Carta::State::CartaObject* obj = _getObject( controlId ); + if ( obj != nullptr ){ + Carta::Data::Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + QString result = controller->setCompositionMode(imageName, "mode"); + resultList = QStringList( result ); + } + else { + resultList = _logErrorMessage( ERROR, UNKNOWN_ERROR ); + } + } + else { + resultList = _logErrorMessage( ERROR, IMAGE_VIEW_NOT_FOUND + controlId ); + } + if ( resultList.length() == 0 ) { + resultList = QStringList(""); + } + return resultList; +} + QStringList ScriptFacade::setClipBuffer( const QString& histogramId, int bufferAmount ) { QStringList resultList; Carta::State::CartaObject* obj = _getObject( histogramId ); @@ -1069,7 +1296,8 @@ QStringList ScriptFacade::getIntensity( const QString& controlId, int frameLow, if ( obj != nullptr ){ Carta::Data::Controller* controller = dynamic_cast(obj); if ( controller != nullptr ){ - valid = controller->getIntensity( frameLow, frameHigh, percentile, &intensity ); + int index = 0; + valid = controller->getIntensity( frameLow, frameHigh, percentile, &intensity, &index ); if ( valid ) { resultList = QStringList( QString::number( intensity ) ); } diff --git a/carta/cpp/core/ScriptedClient/ScriptFacade.h b/carta/cpp/core/ScriptedClient/ScriptFacade.h index e7fe7a97..63c5ac6c 100644 --- a/carta/cpp/core/ScriptedClient/ScriptFacade.h +++ b/carta/cpp/core/ScriptedClient/ScriptFacade.h @@ -175,6 +175,17 @@ class ScriptFacade: public QObject { */ QStringList invertColorMap( const QString& colormapId, const QString& invertStr ); + /** + * set Nan Default + * Set whether or not to use the default nan color (bottom of the color + * map). + * @param nanDefaultStr - true if the bottom color map value should be the + * nan color; false if the nan color should be user specified. + * @return an error message if whether or not to use the default nan + * color could not be set. + */ + QStringList setNanDefault( const QString& colormapId, const QString& nanDefaultStr ); + /** * Set a color mix. * @param colormapId the unique server-side id of an object managing a color map. @@ -442,6 +453,76 @@ class ScriptFacade: public QObject { */ QStringList closeImage( const QString& controlId, const QString& imageName ); + + /** + * Show Image + * @param controlId the unique server-side id of an object managing a controller. + * @param imageName an identifier for the image + * return error information if error occured + */ + QStringList showImage( const QString& controlId, const QString& imageName ); + + + /** + * Hide image + * @param controlId the unique server-side id of an object managing a controller. + * @param imageName an identifier for the image + * return error information if error occured + */ + QStringList hideImage( const QString& controlId, const QString& imageName ); + + + /** + * Stack Select Auto + * Set whether or not selection of layers in the stack should be based on the + * current layer or whether the user wants to make a manual selection. + * @param controlId the unique server-side id of an object managing a controller. + * @param imageName an identifier for the image + * return error information if error occured + */ + QStringList setStackSelectAuto( const QString& controlId, const QString& stackSelectAutoFlagStr); + + /** + * Composition Mode + * Set whether or not to apply a composition mode to the image + * @param controlId the unique server-side id of an object managing a controller. + * @param imageName an identifier for the image + * return error information if error occured + */ + QStringList setCompositionMode( const QString& controlId, const QString& imageName); + + /** + * Pan Zoom All + * Set whether or not a pan/zoom operation should affect all layers in the stack + * or just the top layer. + * @param controlId the unique server-side id of an object managing a controller. + * @param setPanZoomAllFlagStr pan zoom all flag + * return error information if error occured + */ + QStringList setPanZoomAll(const QString& controlId, const QString& setPanZoomAllFlagStr); + + + /** + * set Alpha Mask + * Set the transparency of the layer. + * @param alphaAmount - the transparency level in [0,255] with 255 being opaque. + * @param imageName an identifier for the image + * return error information if error occured + */ + QStringList setMaskAlpha(const QString& controlId, const QString& imageName, const QString& alphaAmount); + + /** + * set Mask Colors + * Set the color to use for the mask. + * redAmount - the amount of red in [0,255] + * redAmount - the amount of red in [0,255] + * redAmount - the amount of red in [0,255] + * return error information if error occured + */ + QStringList setMaskColor(const QString& controlId, const QString& imageName, const QString& redAmount, + const QString& greenAmount, const QString& blueAmount); + + /** * Set the amount of extra space on each side of the clip bounds. * @param histogramId the unique server-side id of an object managing a histogram. @@ -498,7 +579,7 @@ class ScriptFacade: public QObject { * @param frameHigh an upper bound for the image channels or -1 if there is no upper bound. * @param percentile a number [0,1] for which an intensity is desired. */ - QStringList getIntensity( const QString& controlId, int frameLow, int frameHigh, double percentile ); + QStringList getIntensity( const QString& controlId, int frameLow, int frameHigh, double percentile ); /** * Set the number of bins in the histogram. diff --git a/carta/cpp/core/ScriptedClient/ScriptedCommandInterpreter.cpp b/carta/cpp/core/ScriptedClient/ScriptedCommandInterpreter.cpp index 0da082dc..f94355d3 100644 --- a/carta/cpp/core/ScriptedClient/ScriptedCommandInterpreter.cpp +++ b/carta/cpp/core/ScriptedClient/ScriptedCommandInterpreter.cpp @@ -193,6 +193,12 @@ ScriptedCommandInterpreter::tagMessageReceivedCB( TagMessage tm ) result = m_scriptFacade->setDataTransform( colormapId, transform ); } + else if ( cmd == "setnandefault" ) { + QString colormapId = args["colormapId"].toString(); + QString nanDefaultString = args["nanDefaultString"].toString().toLower(); + result = m_scriptFacade->setNanDefault( colormapId, nanDefaultString); + } + /// Section: Image/Controller Commands /// ---------------------------------- /// These commands come from the Python Image class. They allow @@ -345,6 +351,52 @@ ScriptedCommandInterpreter::tagMessageReceivedCB( TagMessage tm ) result = m_scriptFacade->closeImage( imageView, imageName ); } + else if ( cmd == "showimage" ) { + QString imageView = args["imageView"].toString(); + QString imageName = args["imageName"].toString(); + result = m_scriptFacade->showImage( imageView, imageName); + } + + else if ( cmd == "hideimage" ) { + QString imageView = args["imageView"].toString(); + QString imageName = args["imageName"].toString(); + result = m_scriptFacade->hideImage( imageView, imageName ); + } + + else if ( cmd == "setcompositionmode" ) { + QString imageView = args["imageView"].toString(); + QString imageName = args["imageName"].toString(); + result = m_scriptFacade->setCompositionMode( imageView, imageName ); + } + + else if ( cmd == "setstackselectauto" ) { + QString imageView = args["imageView"].toString(); + QString stackSelectFlag = args["stackSelectFlag "].toString(); + result = m_scriptFacade->setStackSelectAuto( imageView, stackSelectFlag ); + } + + else if ( cmd == "setpanzoomall" ) { + QString imageView = args["imageView"].toString(); + QString setPanZoomAllFlag = args["setPanZoomAllFlag"].toString(); + result = m_scriptFacade->setPanZoomAll( imageView, setPanZoomAllFlag ); + } + + else if ( cmd == "setmaskalpha" ) { + QString imageView = args["imageView"].toString(); + QString imageName = args["imageName"].toString(); + QString alphaAmount = args["alphaAmount"].toString(); + result = m_scriptFacade->setMaskAlpha( imageView, imageName, alphaAmount ); + } + + else if ( cmd == "setmaskcolor" ) { + QString imageView = args["imageView"].toString(); + QString imageName = args["imageName"].toString(); + QString redAmount = args["redAmount"].toString(); + QString greenAmount = args["greenAmount"].toString(); + QString blueAmount = args["blueAmount"].toString(); + result = m_scriptFacade->setMaskColor( imageView, imageName, redAmount, greenAmount, blueAmount ); + } + /// Section: Grid Commands /// ---------------------------------- /// These commands also come from the Python Image class. They allow diff --git a/carta/cpp/core/State/ObjectManager.cpp b/carta/cpp/core/State/ObjectManager.cpp index 9c4b8e3f..7faa2f6c 100644 --- a/carta/cpp/core/State/ObjectManager.cpp +++ b/carta/cpp/core/State/ObjectManager.cpp @@ -22,12 +22,14 @@ namespace State { QList CartaObjectFactory::globalIds = {"ChannelUnits", "Clips", "Colormaps","ContourGenerateModes","ContourSpacingModes","ContourStyles", "CoordinateSystems","DataLoader","ErrorManager", - "GenerateModes","Fonts","IntensityUnits", + "Gamma","GenerateModes","Fonts", "LabelFormats","Layout","LayerCompositionModes","LineStyles", "PlotStyles", "ProfilePlotStyles", "Preferences", "PreferencesSave","ProfileStatistics", - "SpectralUnits", "TransformsImage","TransformsData", - "Themes","ViewManager"}; + "TransformsImage","TransformsData", + "Themes", + "UnitsFrequency","UnitsIntensity","UnitsSpectral","UnitsWavelength", + "ViewManager"}; QString CartaObject::addIdToCommand (const QString & command) const { QString fullCommand = m_path; @@ -183,6 +185,8 @@ const QString ObjectManager::STATE_ARRAY = "states"; const QString ObjectManager::STATE_ID = "id"; const QString ObjectManager::STATE_VALUE = "state"; +std::shared_ptr ObjectManager::m_om = nullptr; + ObjectManager::ObjectManager () : m_root( "CartaObjects"), m_sep( "/"), @@ -348,13 +352,15 @@ ObjectManager * ObjectManager::objectManager () { // Implements a singleton pattern - - static ObjectManager * om = new ObjectManager (); - - return om; + if ( !m_om ){ + m_om.reset( new ObjectManager() ); + } + return m_om.get(); } +ObjectManager::~ObjectManager (){ +} bool ObjectManager::registerClass (const QString & className, CartaObjectFactory * factory) diff --git a/carta/cpp/core/State/ObjectManager.h b/carta/cpp/core/State/ObjectManager.h index 15462d0c..bb4777fd 100644 --- a/carta/cpp/core/State/ObjectManager.h +++ b/carta/cpp/core/State/ObjectManager.h @@ -346,6 +346,7 @@ class ObjectManager { private: + /// stores a pair< QString, CartaObjectFactory > class ClassRegistryEntry { @@ -425,6 +426,7 @@ class ObjectManager { typedef std::map ObjectRegistry; ObjectRegistry m_objects; + static std::shared_ptr m_om; }; diff --git a/carta/cpp/core/State/StateInterface.h b/carta/cpp/core/State/StateInterface.h index 1d18fe3f..6eb0286d 100644 --- a/carta/cpp/core/State/StateInterface.h +++ b/carta/cpp/core/State/StateInterface.h @@ -5,8 +5,6 @@ #include -using namespace std; - namespace Carta { namespace State { diff --git a/carta/cpp/core/Viewer.cpp b/carta/cpp/core/Viewer.cpp index b8ed9f9b..1a557035 100644 --- a/carta/cpp/core/Viewer.cpp +++ b/carta/cpp/core/Viewer.cpp @@ -97,9 +97,10 @@ Viewer::start() if( ! Globals::instance()-> platform()-> initialFileList().isEmpty()) { fname = Globals::instance()-> platform()-> initialFileList() [0]; } - Carta::State::ObjectManager* objManager = Carta::State::ObjectManager::objectManager(); + if ( m_viewManager == nullptr ){ - Carta::Data::ViewManager* vm = objManager->createObject (); + Carta::State::ObjectManager* objectManager = Carta::State::ObjectManager::objectManager(); + Carta::Data::ViewManager* vm = objectManager->createObject (); m_viewManager.reset( vm ); } else { @@ -127,3 +128,6 @@ Viewer::start() void Viewer::setDeveloperView( ){ m_devView = true; } + + + diff --git a/carta/cpp/core/Viewer.h b/carta/cpp/core/Viewer.h index 818b52bb..100582c8 100644 --- a/carta/cpp/core/Viewer.h +++ b/carta/cpp/core/Viewer.h @@ -13,6 +13,7 @@ namespace Carta { namespace Data { class ViewManager; } + } /// diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index 1ed18ecb..722f205f 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -31,6 +31,7 @@ HEADERS += \ Data/Colormap/Colormap.h \ Data/Colormap/Colormaps.h \ Data/Colormap/ColorState.h \ + Data/Colormap/Gamma.h \ Data/Colormap/TransformsData.h \ Data/Colormap/TransformsImage.h \ Data/DataLoader.h \ @@ -45,6 +46,7 @@ HEADERS += \ Data/ILinkable.h \ Data/Settings.h \ Data/Image/Controller.h \ + Data/Image/DataFactory.h \ Data/Image/LayerGroup.h \ Data/Image/Stack.h \ Data/Image/Layer.h \ @@ -87,11 +89,12 @@ HEADERS += \ Data/Preferences/Preferences.h \ Data/Preferences/PreferencesSave.h \ Data/Profile/CurveData.h \ - Data/Profile/IntensityUnits.h \ Data/Profile/Profiler.h \ Data/Profile/ProfilePlotStyles.h \ + Data/Profile/ProfileRenderService.h \ + Data/Profile/ProfileRenderThread.h \ + Data/Profile/ProfileRenderWorker.h \ Data/Profile/ProfileStatistics.h \ - Data/Profile/SpectralUnits.h \ Data/Profile/GenerateModes.h \ Data/Region/Region.h \ Data/Region/RegionFactory.h \ @@ -100,6 +103,10 @@ HEADERS += \ Data/Snapshot/Snapshot.h \ Data/Snapshot/SnapshotsFile.h \ Data/Statistics/Statistics.h \ + Data/Units/UnitsFrequency.h \ + Data/Units/UnitsIntensity.h \ + Data/Units/UnitsSpectral.h \ + Data/Units/UnitsWavelength.h \ Data/Util.h \ Data/ViewManager.h \ Data/ViewPlugins.h \ @@ -157,9 +164,11 @@ SOURCES += \ Data/Colormap/Colormap.cpp \ Data/Colormap/Colormaps.cpp \ Data/Colormap/ColorState.cpp \ + Data/Colormap/Gamma.cpp \ Data/Colormap/TransformsData.cpp \ Data/Colormap/TransformsImage.cpp \ Data/Image/Controller.cpp \ + Data/Image/DataFactory.cpp \ Data/Image/LayerData.cpp \ Data/Image/Layer.cpp \ Data/Image/LayerGroup.cpp \ @@ -210,11 +219,12 @@ SOURCES += \ Data/Preferences/Preferences.cpp \ Data/Preferences/PreferencesSave.cpp \ Data/Profile/CurveData.cpp \ - Data/Profile/IntensityUnits.cpp \ Data/Profile/Profiler.cpp \ Data/Profile/ProfilePlotStyles.cpp \ + Data/Profile/ProfileRenderService.cpp \ + Data/Profile/ProfileRenderThread.cpp \ + Data/Profile/ProfileRenderWorker.cpp \ Data/Profile/ProfileStatistics.cpp \ - Data/Profile/SpectralUnits.cpp \ Data/Profile/GenerateModes.cpp \ Data/Region/Region.cpp \ Data/Region/RegionFactory.cpp \ @@ -222,6 +232,10 @@ SOURCES += \ Data/Snapshot/Snapshot.cpp \ Data/Snapshot/SnapshotsFile.cpp \ Data/Statistics/Statistics.cpp \ + Data/Units/UnitsFrequency.cpp \ + Data/Units/UnitsIntensity.cpp \ + Data/Units/UnitsSpectral.cpp \ + Data/Units/UnitsWavelength.cpp \ Data/Util.cpp \ Data/ViewManager.cpp \ Data/ViewPlugins.cpp \ diff --git a/carta/cpp/plugins/ConversionIntensity/ConverterIntensity.cpp b/carta/cpp/plugins/ConversionIntensity/ConverterIntensity.cpp index 048804f6..d87f7864 100644 --- a/carta/cpp/plugins/ConversionIntensity/ConverterIntensity.cpp +++ b/carta/cpp/plugins/ConversionIntensity/ConverterIntensity.cpp @@ -321,7 +321,8 @@ double ConverterIntensity::convertQuantity( double yValue, double frequencyValue double convertedYValue = yValue; if ( oldUnits != KELVIN && newUnits != KELVIN ) { convertedYValue = convertNonKelvinUnits( yValue, oldUnits, newUnits, beamArea ); - } else if ( oldUnits == KELVIN && newUnits != KELVIN ) { + } + else if ( oldUnits == KELVIN && newUnits != KELVIN ) { if ( beamSolidAngle > 0 ) { //kelvin * solidAngle * 2 * 1.38 x 10^-23 * freq^2 / (10^-32 x (3 x 10^8)^2) double num = yValue * beamSolidAngle * FREQUENCY_FACTOR * pow( frequencyValue, 2); diff --git a/carta/cpp/plugins/ConversionSpectral/ConverterChannel.cpp b/carta/cpp/plugins/ConversionSpectral/ConverterChannel.cpp index 785eee3f..06242498 100644 --- a/carta/cpp/plugins/ConversionSpectral/ConverterChannel.cpp +++ b/carta/cpp/plugins/ConversionSpectral/ConverterChannel.cpp @@ -27,12 +27,20 @@ casa::Vector ConverterChannel::convert( const casa::Vector& oldV QString worldUnit(worldUnitsVector[0].c_str()); if ( worldUnit == newUnits ) { resultValues[i] = result; - } else { + } + else { + qDebug() << "worldUnit="< jyValues; + casa::Vector xValues; try { std::shared_ptr >image ( imagePtr->cloneII() ); casa::PixelValueManipulator pvm(image, ®ionRecord, ""); casa::ImageCollapserData::AggregateType funct = _getCombineMethod( profileInfo ); casa::MFrequency::Types freqType = _determineRefFrame( image ); casa::String frame = casa::String( casa::MFrequency::showType( freqType)); - double restFrequency = profileInfo.getRestFrequency(); - QString restUnit = profileInfo.getRestUnit(); + casa::Quantity restFreq( restFrequency, casa::Unit( restUnit.toStdString().c_str())); + casa::Record result = pvm.getProfile( spectralAxis, funct, unit, specType, &restFreq, frame ); + const casa::String VALUE_KEY( "values"); if ( result.isDefined( VALUE_KEY )){ result.get( VALUE_KEY, jyValues ); } + const casa::String X_KEY( "coords"); + if ( result.isDefined( X_KEY )){ + result.get( X_KEY, xValues ); + } + int dataCount = jyValues.size(); for ( int i = 0; i < dataCount; i++ ){ - profileData.push_back( jyValues[i] ); + std::pair dataPair(xValues[i], jyValues[i]); + profileData.push_back( dataPair ); } - + profileResult.setData( profileData ); } catch( casa::AipsError& error ){ qDebug() << "Could not generate profile: "< @@ -34,7 +35,7 @@ class ProfileCASA : public QObject, public IPlugin private: casa::MFrequency::Types _determineRefFrame( std::shared_ptr > img ) const; - std::vector _generateProfile( casa::ImageInterface < casa::Float > * imagePtr, + Carta::Lib::Hooks::ProfileResult _generateProfile( casa::ImageInterface < casa::Float > * imagePtr, Carta::Lib::RegionInfo regionInfo, Carta::Lib::ProfileInfo profileInfo ) const; casa::ImageCollapserData::AggregateType _getCombineMethod( Carta::Lib::ProfileInfo profileInfo ) const; casa::ImageRegion* _getEllipsoid(const casa::CoordinateSystem& cSys, diff --git a/carta/cpp/plugins/RegionCASA/RegionCASA.cpp b/carta/cpp/plugins/RegionCASA/RegionCASA.cpp index adb55385..93e9caff 100755 --- a/carta/cpp/plugins/RegionCASA/RegionCASA.cpp +++ b/carta/cpp/plugins/RegionCASA/RegionCASA.cpp @@ -11,6 +11,7 @@ #include "imageanalysis/Annotations/AnnEllipse.h" #include +#include RegionCASA::RegionCASA(QObject *parent) : @@ -29,21 +30,30 @@ void RegionCASA::_addCorners( std::shared_ptr& rInfo, bool RegionCASA::handleHook(BaseHook & hookData){ qDebug() << "RegionCASA plugin is handling hook #" << hookData.hookId(); + bool hookHandled = false; if( hookData.is()) { - return true; + hookHandled = true; } else if( hookData.is()) { Carta::Lib::Hooks::LoadRegion & hook = static_cast( hookData); QString fileName = hook.paramsPtr->fileName; if ( fileName.length() > 0 ){ - std::shared_ptr imagePtr = hook.paramsPtr->image; - hook.result = _loadRegion( fileName, imagePtr ); - return true; + //Before going to a lot of trouble, make sure we can open the file and that it has + //the potential to be a CASA region. + bool casaRegion = _isCASARegion( fileName ); + if ( casaRegion ){ + std::shared_ptr imagePtr = hook.paramsPtr->image; + hook.result = _loadRegion( fileName, imagePtr ); + } + else { + //Not a casa region so return an empty vector. + hook.result = std::vector >(); + } + hookHandled = true; } } - qWarning() << "Sorry, RegionCASA doesn't know how to handle this hook"; - return false; + return hookHandled; } std::vector RegionCASA::getInitialHookList(){ @@ -77,9 +87,23 @@ RegionCASA::_getPixelVertices( const casa::AnnotationBase::Direction& corners, } +bool RegionCASA::_isCASARegion( const QString& fileName ) const { + bool casaRegion = false; + QFile inputFile( fileName ); + if ( inputFile.open( QIODevice::ReadOnly ) ){ + QString firstLine = inputFile.readLine(); + if ( firstLine.contains( "#CRTF" ) ){ + casaRegion = true; + } + } + return casaRegion; +} + + std::vector< std::shared_ptr > RegionCASA::_loadRegion( const QString & fname, std::shared_ptr imagePtr ){ std::vector > regionInfos; + casa::String fileName( fname.toStdString().c_str() ); CCImageBase * base = dynamic_cast( imagePtr.get() ); if ( base ){ diff --git a/carta/cpp/plugins/RegionCASA/RegionCASA.h b/carta/cpp/plugins/RegionCASA/RegionCASA.h index 13e605f6..c80e4b49 100644 --- a/carta/cpp/plugins/RegionCASA/RegionCASA.h +++ b/carta/cpp/plugins/RegionCASA/RegionCASA.h @@ -64,6 +64,13 @@ class RegionCASA : public QObject, public IPlugin const casa::CoordinateSystem& csys, const casa::Vector& directions ) const; + /** + * Returns true if the region is a casa region; false otherwise. + * @param fileName - the absolute path to a file containing the region. + * @return - true if the file is a recognized region in CASA format; false otherwise. + */ + bool _isCASARegion( const QString& fileName ) const; + /** * Load one or more regions based on the name of a file specifying regions in CASA format * and an image that will contain the region. diff --git a/carta/cpp/plugins/RegionDs9/ContextDs9.cpp b/carta/cpp/plugins/RegionDs9/ContextDs9.cpp new file mode 100644 index 00000000..d797a2e5 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/ContextDs9.cpp @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ContextDs9::ContextDs9( ){ +} +static inline double wrap_angle( double before, double after ) { + const double UNIT_WRAPAROUND = 2.0 * M_PI; + if ( after < 0 && before > 0 ) + return after + UNIT_WRAPAROUND; + else if ( after > 0 && before < 0 ) + return after - UNIT_WRAPAROUND; + return after; +} + +static inline casa::Quantum > convert_angle( double x, const std::string &xunits, double y, const std::string &yunits, + casa::MDirection::Types original_coordsys, casa::MDirection::Types new_coordsys, const std::string &new_units="rad" ) { + casa::Quantum xq(x,casa::String(xunits)); + casa::Quantum yq(y,casa::String(yunits)); + casa::MDirection md = casa::MDirection::Convert(casa::MDirection(xq,yq,original_coordsys), new_coordsys)(); + casa::Quantum > result = md.getAngle("rad"); + xq.convert("rad"); + yq.convert("rad"); + result.getValue( )(0) = wrap_angle(xq.getValue( ), result.getValue( )(0)); + result.getValue( )(1) = wrap_angle(yq.getValue( ), result.getValue( )(1)); + result.convert(casa::String(new_units)); + return result; +} + + +void ContextDs9::createBoxCmd( const Vector& center, const Vector& size, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { + qDebug() << "Create box cmd"; + double xoffset = size[0] / 2.0; + double yoffset = size[1] / 2.0; + Carta::Lib::RegionInfo* info = new Carta::Lib::RegionInfo(); + info->setRegionType( Carta::Lib::RegionInfo::RegionType::Polygon ); + if ( xoffset > 1.0 || yoffset > 1.0 ) { + // size is big enough to make a rectangle... perhaps we should require bigger size... + // 'width' is the line width... need to thread that through... + int cornerCount = 4; + std::vector > pts(cornerCount); + pts[0].first = center[0] - xoffset; + pts[0].second = center[1] - yoffset; + pts[1].first = center[0] - xoffset; + pts[1].second = center[1] + yoffset; + pts[2].first = center[0] + xoffset; + pts[2].second = center[1] - yoffset; + pts[3].first = center[0] + xoffset; + pts[3].second = center[1] + yoffset; + for ( int i = 0; i < cornerCount; i++ ){ + info->addCorner( pts[i].first, pts[i].second ); + } + } + else { + //Just a point + info->addCorner( center[0], center[1] ); + } + m_regions.push_back( std::shared_ptr( info ) ); +} + + +#define DEFINE_POINT_COMMAND(NAME) \ +void ContextDs9::create ## NAME ## Cmd( const Vector& center, int size, \ + const char* color, int* dash, int width, const char* font, \ + const char* text, unsigned short prop, const char* comment, const std::list& tag ) { \ + PointShape shape = XPT; \ + createPointCmd( center, shape, size, color, dash, width, font, text, prop, comment, tag ); \ +} +DEFINE_POINT_COMMAND(BoxPoint) +DEFINE_POINT_COMMAND(CirclePoint) +DEFINE_POINT_COMMAND(DiamondPoint) +DEFINE_POINT_COMMAND(CrossPoint) +DEFINE_POINT_COMMAND(ExPoint) +DEFINE_POINT_COMMAND(ArrowPoint) +DEFINE_POINT_COMMAND(BoxCirclePoint) + +void ContextDs9::createPointCmd( const Vector& v, PointShape, int, const char*, int*, int, const char*, + const char*, unsigned short, const char*, const std::list& ){ + qDebug() << "creating point"; + if ( v.size() == 2 ){ + Carta::Lib::RegionInfo* info = new Carta::Lib::RegionInfo(); + info->setRegionType( Carta::Lib::RegionInfo::RegionType::Polygon ); + info->addCorner( v[0], v[1]); + m_regions.push_back( std::shared_ptr(info) ); + } +} + + +void ContextDs9::createEllipseCmd( const Vector& center, const Vector& radius, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { + // 'width' is the line width... need to thread that through... + Carta::Lib::RegionInfo* info = new Carta::Lib::RegionInfo(); + info->setRegionType( Carta::Lib::RegionInfo::RegionType::Ellipse ); + int pointCount = 4; + std::vector > pts( pointCount ); + double xoffset = radius[0]; + double yoffset = radius[1]; + pts[0].first = center[0] - xoffset; + pts[0].second = center[1] - yoffset; + pts[1].first = center[0] + xoffset; + pts[1].second = center[1] + yoffset; + pts[2].first = center[0] - xoffset; + pts[2].second = center[1] + yoffset; + pts[3].first = center[0] + xoffset; + pts[3].second = center[1] - yoffset; + for ( int i = 0; i < pointCount; i++ ){ + info->addCorner( pts[i].first, pts[i].second ); + } + m_regions.push_back( std::shared_ptr( info ) ); +} + + +void ContextDs9::createCircleCmd( const Vector& center, double radius, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ) { + qDebug() << "createCircleCmd"; + std::vector radii(2); + radii[0] = radius; + radii[1] = radius; + double angle = 0; + createEllipseCmd( center, radii, angle, color, dash, width, font, text, prop, comment, tag ); +} + +void ContextDs9::createPolygonCmd( const Vector& /*center*/, const Vector& /*bb*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { + qDebug() << "Context create polygon cmd"; +} + +void ContextDs9::createPolygonCmd( const std::list& verts, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { + // 'width' is the line width... need to thread that through... + qDebug() << "createPolygonCmd"; + Carta::Lib::RegionInfo* info = new Carta::Lib::RegionInfo(); + info->setRegionType( Carta::Lib::RegionInfo::RegionType::Polygon ); + for ( std::list::const_iterator it=verts.begin( ); it != verts.end(); ++it ) { + if ( (*it).size( ) < 2 ) return; + info->addCorner( (*it)[0], (*it)[1] ); + } + m_regions.push_back( std::shared_ptr( info ) ); +} + +std::vector > ContextDs9::getRegions() const { + return m_regions; +} + +Vector ContextDs9::mapToRef(const Vector& v, CoordSystem /*sys*/, SkyFrame /*frame*/) { + int count = v.size(); + qDebug() << "mapToRef needs to be implemented"; + Vector result( count ); + for ( int i = 0; i < count; i++ ){ + result[i] = v[i]; + } + return result; +} + +double ContextDs9::mapLenToRef(double d, CoordSystem /*sys*/, SkyDist /*frame*/) { + double lx = d; + qDebug() << "mapLenToRef needs to be implemented"; + + return lx; +} + +Vector ContextDs9::mapLenToRef(const Vector &v, CoordSystem /*sys*/, SkyDist /*format*/) { + Vector result(v); + qDebug() << "mapLenToRef needs to be implemented"; + return result; +} + +double ContextDs9::mapAngleFromRef(double /*angle*/, CoordSystem /*sys*/, SkyFrame /*sky*/) { + qDebug() << "mapAngleFromRef needs to be implemented"; + return 0; +} +double ContextDs9::mapAngleToRef(double /*angle*/, CoordSystem /*sys*/, SkyFrame /*sky*/) { + qDebug() << "mapAngleToRef needs to be implemented"; + return 0; +} + +double degToRad(double d) { + double r = M_PI * d / 180.; + + if (r > 0) + while (r >= 2*M_PI) r -= 2*M_PI; + else + while (r < 0) r += 2*M_PI; + + return r; +} + +double radToDeg(double r) { + double d = 180. * r / M_PI; + + if (d > 0) + while(d >= 360) d -= 360.; + else + while(d < 0) d += 360.; + + return d; +} + +double dmsToDegree(int sign, int degree, int min, double sec) { + // sign is needed because of -00 vs +00 + return double(sign) * (abs(degree) + (min/60.) + (sec/60./60.)); +} + +double parseSEXStr(const char* d) { + char* dms = strdup(d); // its going to get clobbered + char* ptr = dms; + + int sign = 1; + int degree = atoi(strtok(ptr,":")); + int minute = atoi(strtok(NULL,":")); + float sec = atof(strtok(NULL,":")); + + // assumes the minus sign is the first char + if (degree != 0) + sign = degree>0 ? 1 : -1; + else + sign = d[0] == '-' ? -1 : 1; + + free(dms); + + return dmsToDegree(sign,abs(degree),minute,sec); +} + +double parseHMSStr(const char* str) { + char* hms = strdup(str); // its going to get clobbered + char* ptr = hms; + + int sign = 1; + int hour = atoi(strtok(ptr,"h")); + int minute = atoi(strtok(NULL,"m")); + float second = atof(strtok(NULL,"s")); + + // assumes the minus sign is the first char + if (hour != 0) + sign = hour>0 ? 1 : -1; + else + sign = str[0] == '-' ? -1 : 1; + + free(hms); + return dmsToDegree(sign,abs(hour),minute,second)/24.*360.; +} + +double parseDMSStr(const char* str) { + char* dms = strdup(str); // its going to get clobbered + char* ptr = dms; + + int sign = 1; + int degree = atoi(strtok(ptr,"d")); + int minute = atoi(strtok(NULL,"m")); + float sec = atof(strtok(NULL,"s")); + + // assumes the minus sign is the first char + if (degree != 0) + sign = degree>0 ? 1 : -1; + else + sign = str[0] == '-' ? -1 : 1; + + free(dms); + return dmsToDegree(sign,abs(degree),minute,sec); +} + +std::vector coordtovec( double *c ) { + std::vector result(3); + result[0] = c[0]; + result[1] = c[1]; + result[2] = c[2]; + return result; +} + +std::vector doubletovec( double x, double y, double z ) { + std::vector result(3); + result[0] = x; + result[1] = y; + result[2] = z; + return result; +} + +ContextDs9::~ContextDs9(){ + +} + diff --git a/carta/cpp/plugins/RegionDs9/ContextDs9.h b/carta/cpp/plugins/RegionDs9/ContextDs9.h new file mode 100644 index 00000000..63af07b8 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/ContextDs9.h @@ -0,0 +1,280 @@ +/** + * Implements the parser callbacks to produce regions. + */ +#pragma once +#include +#include +#include +#include + +namespace Carta { + namespace Lib { + class RegionInfo; + } +} + +extern double degToRad(double); +extern double radToDeg(double); + +double parseSEXStr(const char* d); +extern double parseHMSStr(const char* str); +extern double parseDMSStr(const char* str); + +std::vector coordtovec( double * ); +std::vector doubletovec( double x=1, double y=1, double z=1 ); + +typedef std::vector Vertex; +typedef std::vector Vector; +typedef std::string Tag; + +enum CoordSystem { IMAGE, PHYSICAL, AMPLIFIER, DETECTOR, WCS, + WCSA, WCSB, WCSC, WCSD, WCSE, WCSF, WCSG, WCSH, WCSI, + WCSJ, WCSK, WCSL, WCSM, WCSN, WCSO, WCSP, WCSQ, WCSR, + WCSS, WCST, WCSU, WCSV, WCSW, WCSX, WCSY, WCSZ, WCS0 +}; + +enum SkyFrame {FK4, FK4_NO_E, FK5, ICRS, GALACTIC, SUPERGALACTIC, + ECLIPTIC, HELIOECLIPTIC, NATIVEWCS}; +enum SkyDist {DEGREE, ARCMIN, ARCSEC}; +enum PointShape {CIRCLE,BOX,DIAMOND,CROSS,XPT,ARROW,BOXCIRCLE}; + +inline casa::MDirection::Types todirection( SkyFrame frame ) { + switch ( frame ) { + case FK4: + return casa::MDirection::B1950; + case FK5: + return casa::MDirection::J2000; + case GALACTIC: + return casa::MDirection::GALACTIC; + case ECLIPTIC: + return casa::MDirection::ECLIPTIC; + default: + return casa::MDirection::GALACTIC; + } +} + +inline const char *tostr(SkyFrame sf) { + return sf == FK4 ? "FK4" : + sf == FK5 ? "FK5" : + sf == ICRS ? "ICRS" : + sf == ECLIPTIC ? "ECLIPTIC" : "NATIVEWCS"; +} + +inline const char * tostr(CoordSystem cs) { + return cs == IMAGE ? "IMAGE" : + cs == PHYSICAL ? "PHYSICAL" : + cs == AMPLIFIER ? "AMPLIFIER" : + cs == DETECTOR ? "DETECTOR" : + cs == WCS ? "WCS" : cs == WCSA ? "WCSA" : + cs == WCSB ? "WCSB" : cs == WCSC ? "WCSC" : + cs == WCSD ? "WCSD" : cs == WCSE ? "WCSE" : + cs == WCSF ? "WCSF" : cs == WCSG ? "WCSG" : + cs == WCSH ? "WCSH" : cs == WCSI ? "WCSI" : + cs == WCSJ ? "WCSJ" : cs == WCSK ? "WCSK" : + cs == WCSL ? "WCSL" : cs == WCSM ? "WCSM" : + cs == WCSN ? "WCSN" : cs == WCSO ? "WCSO" : + cs == WCSP ? "WCSP" : cs == WCSQ ? "WCSQ" : + cs == WCSR ? "WCSR" : cs == WCSS ? "WCSS" : + cs == WCST ? "WCST" : cs == WCSU ? "WCSU" : + cs == WCSV ? "WCSV" : cs == WCSW ? "WCSW" : + cs == WCSX ? "WCSX" : cs == WCSY ? "WCSY" : + cs == WCSZ ? "WCSZ" : "WCS0"; +} + +/*inline const char *tostr( SkyDist f ) { + return f == DEGREES ? "DEGREES" : + f == SEXAGESIMAL ? "SEXAGESIMAL" : + f == ARCMIN ? "ARCMIN" : "ARCSEC"; +}*/ +inline const char *tostr( SkyDist f ) { + return f == DEGREE ? "DEGREE" : + //f == SEXAGESIMAL ? "SEXAGESIMAL" : + f == ARCMIN ? "ARCMIN" : "ARCSEC"; +} + +class ContextDs9 { +public: + + /** + * Constructor. + */ + ContextDs9( ); + + /** + * Returns a list of regions that were read by the parser. + * @return - a list of regions returned by the parser. + */ + std::vector > getRegions() const; + + //----------------------------------------------------------------------------------------- + // Internal methods called by the parser. + + //********** Implemented ******************************************************************/ + void createBoxCmd( const Vector& /*center*/, const Vector& /*size*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ); + void createEllipseCmd( const Vector& /*center*/, const Vector& /*radius*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ); + void createCircleCmd( const Vector& /*center*/, double /*radius*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ); + void createPolygonCmd( const Vector& center, const Vector& bb, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createPolygonCmd( const std::list& list, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createPointCmd( const Vector&, PointShape, int, const char*, int*, int, const char*, + const char*, unsigned short, const char*, const std::list& ); + void createCirclePointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createBoxPointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createDiamondPointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createCrossPointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createExPointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createArrowPointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + void createBoxCirclePointCmd( const Vector& center, int size, const char* color, int* dash, + int width, const char* font, const char* text, unsigned short prop, + const char* comment, const std::list& tag ); + + + //********** NOT Implemented **************************************************************/ + void createContourPolygonCmd( const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createCompassCmd( const Vector& /*center*/, double /*r*/, const char* /*north*/, const char* /*east*/, + int /*na*/, int /*ea*/, CoordSystem /*sys*/, SkyFrame /*sky*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createCompositeCmd( const Vector& /*center*/, double /*angle*/, int /*global*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createVectCmd( const Vector& /*center*/, const Vector& /*p2*/, int /*arrow*/,const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createVectCmd( const Vector& /*center*/, double /*mag*/, double /*ang*/, int /*arrow*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createProjectionCmd( const Vector& /*center*/, const Vector& /*p2*/, double /*w*/, const char* /*mvcb*/, + const char* /*delcb*/, const char* /*color*/, int* /*dash*/, int /*width*/, + const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createRulerCmd( const Vector& /*center*/, const Vector& /*p2*/, CoordSystem /*sys*/, SkyFrame /*sky*/, + CoordSystem /*distsys*/, SkyDist /*distformat*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createCircle3dCmd( const Vector& /*center*/, double /*radius*/, const char* /*mvcb*/, + const char* /*delcb*/, const char* /*color*/, int* /*dash*/, int /*width*/, + const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createAnnulusCmd( const Vector& /*center*/, double /*start*/, double /*stop*/, int /*num*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createAnnulusCmd( const Vector& /*center*/, int /*num*/, double* /*radii*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createCpandaCmd( const Vector& /*center*/, double /*ang1*/, double /*ang2*/, int /*an*/, + double /*rad1*/, double /*rad2*/, int /*rn*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createCpandaCmd( const Vector& /*center*/, int /*an*/, double* /*a*/, int /*rn*/, double* /*r*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createEllipseAnnulusCmd( const Vector& /*center*/, const Vector& /*inner*/, + const Vector& /*outer*/, int /*num*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createEllipseAnnulusCmd( const Vector& /*center*/, int /*num*/, Vector* /*radii*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createEpandaCmd( const Vector& /*center*/, double /*ang1*/, double /*ang2*/, int /*an*/, + const Vector& /*rad1*/, const Vector& /*rad2*/, int /*rn*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createEpandaCmd( const Vector& /*center*/, int /*an*/, double* /*a*/, int /*rn*/, Vector* /*r*/, + double /*angle*/, const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createLineCmd( const Vector& /*center*/, const Vector& /*p2*/, int /*arrow1*/, int /*arrow2*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createBoxAnnulusCmd( const Vector& /*center*/, const Vector& /*inner*/, const Vector& /*outer*/, + int /*num*/, double /*angle*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createBoxAnnulusCmd( const Vector& /*center*/, int /*num*/, Vector* /*size*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createBpandaCmd( const Vector& /*center*/, double /*ang1*/, double /*ang2*/, int /*an*/, + const Vector& /*rad1*/, const Vector& /*rad2*/, int /*rn*/, double /*angle*/, + const char* /*color*/, int* /*dash*/, int /*width*/, const char* /*font*/, + const char* /*text*/, unsigned short /*prop*/, const char* /*comment*/, + const std::list& /*tag*/ ) { } + + void createBpandaCmd( const Vector& /*center*/, int /*an*/, double* /*a*/, int /*rn*/, Vector* /*r*/, + double /*angle*/,const char* /*color*/, int* /*dash*/, int /*width*/, + const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void createSegmentCmd( const std::list&, const char*, int*, int, const char*, + const char*, unsigned short, const char*, const std::list& ){} + + void createTextCmd( const Vector& /*center*/, double /*angle*/, int /*rotate*/, const char* /*color*/, int* /*dash*/, + int /*width*/, const char* /*font*/, const char* /*text*/, unsigned short /*prop*/, + const char* /*comment*/, const std::list& /*tag*/ ) { } + + void markerDeleteLastCmd() { } + void resetCompositeMarker() { } + + //Conversion + double mapAngleFromRef(double /*angle*/, CoordSystem /*sys*/, SkyFrame /*sky*/); + double mapAngleToRef(double /*angle*/, CoordSystem /*sys*/, SkyFrame /*sky*/); + double mapLenToRef(double d, CoordSystem sys, SkyDist format=DEGREE); + Vector mapLenToRef(const Vector &, CoordSystem sys, SkyDist format=DEGREE); + Vector mapToRef(const Vector& v, CoordSystem sys, SkyFrame format=FK5); + + virtual ~ContextDs9(); +private: + std::vector > m_regions; +}; + diff --git a/carta/cpp/plugins/RegionDs9/FlexLexer.h b/carta/cpp/plugins/RegionDs9/FlexLexer.h new file mode 100644 index 00000000..2aaee645 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/FlexLexer.h @@ -0,0 +1,208 @@ +// -*-C++-*- +// FlexLexer.h -- define interfaces for lexical analyzer classes generated +// by flex + +// Copyright (c) 1993 The Regents of the University of California. +// All rights reserved. +// +// This code is derived from software contributed to Berkeley by +// Kent Williams and Tom Epperly. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: + +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. + +// Neither the name of the University nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE. + +// This file defines FlexLexer, an abstract class which specifies the +// external interface provided to flex C++ lexer objects, and yyFlexLexer, +// which defines a particular lexer class. +// +// If you want to create multiple lexer classes, you use the -P flag +// to rename each yyFlexLexer to some other xxFlexLexer. You then +// include in your other sources once per lexer class: +// +// #undef yyFlexLexer +// #define yyFlexLexer xxFlexLexer +// #include +// +// #undef yyFlexLexer +// #define yyFlexLexer zzFlexLexer +// #include +// ... + +#ifndef __FLEX_LEXER_H +// Never included before - need to define base class. +#define __FLEX_LEXER_H + +#include +# ifndef FLEX_STD +# define FLEX_STD std:: +# endif + +extern "C++" { + +struct yy_buffer_state; +typedef int yy_state_type; + +class FlexLexer { +public: + virtual ~FlexLexer() { } + + const char* YYText() const { return yytext; } + size_t YYLeng() const { return yyleng; } + + virtual void + yy_switch_to_buffer( struct yy_buffer_state* new_buffer ) = 0; + virtual struct yy_buffer_state* + yy_create_buffer( FLEX_STD istream* s, int size ) = 0; + virtual void yy_delete_buffer( struct yy_buffer_state* b ) = 0; + virtual void yyrestart( FLEX_STD istream* s ) = 0; + + virtual int yylex() = 0; + + // Call yylex with new input/output sources. + int yylex( FLEX_STD istream* new_in, FLEX_STD ostream* new_out = 0 ) + { + switch_streams( new_in, new_out ); + return yylex(); + } + + // Switch to new input/output streams. A nil stream pointer + // indicates "keep the current one". + virtual void switch_streams( FLEX_STD istream* new_in = 0, + FLEX_STD ostream* new_out = 0 ) = 0; + + int lineno() const { return yylineno; } + + int debug() const { return yy_flex_debug; } + void set_debug( int flag ) { yy_flex_debug = flag; } + +protected: + char* yytext; + size_t yyleng; + int yylineno; // only maintained if you use %option yylineno + int yy_flex_debug; // only has effect with -d or "%option debug" +}; + +} +#endif // FLEXLEXER_H + +#if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce) +// Either this is the first time through (yyFlexLexerOnce not defined), +// or this is a repeated include to define a different flavor of +// yyFlexLexer, as discussed in the flex manual. +#define yyFlexLexerOnce + +extern "C++" { + +class yyFlexLexer : public FlexLexer { +public: + // arg_yyin and arg_yyout default to the cin and cout, but we + // only make that assignment when initializing in yylex(). + yyFlexLexer( FLEX_STD istream* arg_yyin = 0, FLEX_STD ostream* arg_yyout = 0 ); + + virtual ~yyFlexLexer(); + + void yy_switch_to_buffer( struct yy_buffer_state* new_buffer ); + struct yy_buffer_state* yy_create_buffer( FLEX_STD istream* s, int size ); + void yy_delete_buffer( struct yy_buffer_state* b ); + void yyrestart( FLEX_STD istream* s ); + + void yypush_buffer_state( struct yy_buffer_state* new_buffer ); + void yypop_buffer_state(); + + virtual int yylex(); + virtual void switch_streams( FLEX_STD istream* new_in, FLEX_STD ostream* new_out = 0 ); + virtual int yywrap(); + + void begin(int,int); + +protected: + virtual int LexerInput( char* buf, int max_size ); + virtual size_t LexerOutput( char* buf, int size ); + virtual void LexerError( const char* msg ); + + void yyunput( int c, char* buf_ptr ); + int yyinput(); + + void yy_load_buffer_state(); + void yy_init_buffer( struct yy_buffer_state* b, FLEX_STD istream* s ); + void yy_flush_buffer( struct yy_buffer_state* b ); + + int yy_start_stack_ptr; + int yy_start_stack_depth; + int* yy_start_stack; + + void yy_push_state( int new_state ); + void yy_pop_state(); + int yy_top_state(); + + yy_state_type yy_get_previous_state(); + yy_state_type yy_try_NUL_trans( yy_state_type current_state ); + int yy_get_next_buffer(); + + FLEX_STD istream* yyin; // input source for default LexerInput + FLEX_STD ostream* yyout; // output sink for default LexerOutput + + // yy_hold_char holds the character lost when yytext is formed. + char yy_hold_char; + + // Number of characters read into yy_ch_buf. + size_t yy_n_chars; + + // Points to current character in buffer. + char* yy_c_buf_p; + + int yy_init; // whether we need to initialize + int yy_start; // start state number + + // Flag which is used to allow yywrap()'s to do buffer switches + // instead of setting up a fresh yyin. A bit of a hack ... + int yy_did_buffer_switch_on_eof; + + + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + struct yy_buffer_state ** yy_buffer_stack; /**< Stack as an array. */ + void yyensure_buffer_stack(void); + + // The following are not always needed, but may be depending + // on use of certain flex features (like REJECT or yymore()). + + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + yy_state_type* yy_state_buf; + yy_state_type* yy_state_ptr; + + char* yy_full_match; + int* yy_full_state; + int yy_full_lp; + + int yy_lp; + int yy_looking_for_trail_begin; + + int yy_more_flag; + int yy_more_len; + int yy_more_offset; + int yy_prev_more_offset; +}; + +} + +#endif // yyFlexLexer || ! yyFlexLexerOnce + diff --git a/carta/cpp/plugins/RegionDs9/ParserDs9.cpp b/carta/cpp/plugins/RegionDs9/ParserDs9.cpp new file mode 100644 index 00000000..b29370a1 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/ParserDs9.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include +#include + + + +ParserDs9::ParserDs9( ) : trace_scanning(false), trace_parsing(false) { +} + +bool ParserDs9::parse_stream(ContextDs9 &base, std::istream& in, const std::string& sname) { + streamname = sname; + + ds9lex scanner(&in); + scanner.set_debug(trace_scanning); + lexer = &scanner; + + yy::ds9parse parser( *this, &base ); + + parser.set_debug_level(trace_parsing); + return (parser.parse() == 0); +} + +bool ParserDs9::parse_file( ContextDs9 &base, const std::string &filename) { + std::ifstream in(filename.c_str()); + if (!in.good()) return false; + return parse_stream(base, in, filename); +} + +bool ParserDs9::parse_string( ContextDs9 &base, const std::string &input, const std::string& sname) { + std::istringstream iss(input); + return parse_stream(base, iss, sname); +} + +void ParserDs9::error(const class yy::location& l, const std::string& m) { + std::cerr << l << ": " << m << std::endl; +} + +void ParserDs9::error(const std::string& m) { + std::cerr << m << std::endl; +} + + diff --git a/carta/cpp/plugins/RegionDs9/ParserDs9.h b/carta/cpp/plugins/RegionDs9/ParserDs9.h new file mode 100644 index 00000000..a6a38d9c --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/ParserDs9.h @@ -0,0 +1,53 @@ +/** + * Top level class for parsing ds9 files. + */ + +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include "ds9parse.hpp" + +class ds9lex; + +class ParserDs9 { + +public: + /** + * Constructor. + */ + ParserDs9( ); + + /// enable debug output in the flex scanner + bool trace_scanning; + + /// enable debug output in the bison parser + bool trace_parsing; + + /// stream name (file or input stream) used for error messages. + std::string streamname; + + // input stream, stream name for error messages, return true if successfully parsed + bool parse_stream(ContextDs9 &base, std::istream& in, const std::string& sname = "stream input"); + + bool parse_string(ContextDs9 &base, const std::string& input, const std::string& sname = "string stream"); + + bool parse_file(ContextDs9 &base, const std::string& filename); + + // To demonstrate pure handling of parse errors, instead of + // simply dumping them on the standard error output, we will pass + // them to the driver using the following two member functions. + + /** Error handling with associated line number. This can be modified to + * output the error e.g. to a dialog box. */ + void error(const class yy::location& l, const std::string& m); + + /** General error handling. This can be modified to output the error + * e.g. to a dialog box. */ + void error(const std::string& m); + ds9lex* lexer; + +}; +#endif + diff --git a/carta/cpp/plugins/RegionDs9/RegionDs9.cpp b/carta/cpp/plugins/RegionDs9/RegionDs9.cpp new file mode 100755 index 00000000..a87e26fc --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/RegionDs9.cpp @@ -0,0 +1,52 @@ +#include "RegionDs9.h" +#include "ContextDs9.h" +#include "ParserDs9.h" +#include "plugins/CasaImageLoader/CCImage.h" +#include "CartaLib/Hooks/Initialize.h" +#include "CartaLib/Hooks/LoadRegion.h" +#include "CartaLib/RegionInfo.h" +#include "CartaLib/IImage.h" + +#include + + +RegionDs9::RegionDs9(QObject *parent) : + QObject(parent){ +} + + +bool RegionDs9::handleHook(BaseHook & hookData){ + qDebug() << "RegionDs9 plugin is handling hook #" << hookData.hookId(); + bool hookHandled = false; + if ( hookData.is()) { + hookHandled = true; + } + else if ( hookData.is()) { + Carta::Lib::Hooks::LoadRegion & hook + = static_cast( hookData); + QString fileName = hook.paramsPtr->fileName; + if ( fileName.length() > 0 ){ + ContextDs9 context; + ParserDs9 parser; + bool result = parser.parse_file( context, fileName.toStdString()); + std::shared_ptr imagePtr = hook.paramsPtr->image; + hook.result = context.getRegions(); + qDebug() << "Ds9 read region file result="< RegionDs9::getInitialHookList(){ + return { + Carta::Lib::Hooks::Initialize::staticId, + Carta::Lib::Hooks::LoadRegion::staticId + }; +} + + +RegionDs9::~RegionDs9(){ + +} diff --git a/carta/cpp/plugins/RegionDs9/RegionDs9.h b/carta/cpp/plugins/RegionDs9/RegionDs9.h new file mode 100644 index 00000000..e7834c7f --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/RegionDs9.h @@ -0,0 +1,30 @@ +/** + * This plugin can read ds9 region formats. + */ +#pragma once + +#include "CartaLib/IPlugin.h" +#include + +namespace Carta { + namespace Lib { + class RegionInfo; + } +} + +class RegionDs9 : public QObject, public IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.cartaviewer.IPlugin") + Q_INTERFACES( IPlugin) + +public: + + /** + * Constructor. + */ + RegionDs9(QObject *parent = 0); + virtual bool handleHook(BaseHook & hookData) override; + virtual std::vector getInitialHookList() override; + virtual ~RegionDs9(); +}; diff --git a/carta/cpp/plugins/RegionDs9/RegionDs9.pro b/carta/cpp/plugins/RegionDs9/RegionDs9.pro new file mode 100644 index 00000000..97fb8dd3 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/RegionDs9.pro @@ -0,0 +1,96 @@ +! include(../../common.pri) { + error( "Could not find the common.pri file!" ) +} + +QT += core gui +TARGET = plugin +TEMPLATE = lib +CONFIG += plugin + +FLEXSOURCES = ds9lex.L +BISONSOURCES = ds9parse.y + +SOURCES += \ + RegionDs9.cpp \ + ParserDs9.cpp \ + ContextDs9.cpp + +HEADERS += \ + RegionDs9.h \ + FlexLexer.h \ + ds9lex.h \ + ParserDs9.h \ + ContextDs9.h + + + + +casacoreLIBS += -L$${CASACOREDIR}/lib +casacoreLIBS += -lcasa_lattices -lcasa_tables -lcasa_scimath -lcasa_scimath_f -lcasa_mirlib +casacoreLIBS += -lcasa_casa -llapack -lblas -ldl +casacoreLIBS += -lcasa_images -lcasa_coordinates -lcasa_fits -lcasa_measures + +LIBS += $${casacoreLIBS} +LIBS += -L$${WCSLIBDIR}/lib -lwcs +LIBS += -L$${CFITSIODIR}/lib -lcfitsio +LIBS += -L$$OUT_PWD/../../core/ -lcore +LIBS += -L$$OUT_PWD/../../CartaLib/ -lCartaLib +LIBS += -lfl -ly + +INCLUDEPATH += $${CASACOREDIR}/include +INCLUDEPATH += $${CASACOREDIR}/include/casacore +INCLUDEPATH += $${WCSLIBDIR}/include +INCLUDEPATH += $${CFITSIODIR}/include +#INCLUDEPATH += $$PWD/../../core +DEPENDPATH += $$PWD/../../core + +OTHER_FILES += \ + plugin.json \ + $$FLEXSOURCES \ + $$BISONSOURCES + +# copy json to build directory +MYFILES = plugin.json +MYFILES += $$FLEXSOURCES +MYFILES += $$BISONSOURCES + +! include($$top_srcdir/cpp/copy_files.pri) { + error( "Could not include $$top_srcdir/cpp/copy_files.pri file!" ) +} + +flexsource.input = FLEXSOURCES +flexsource.output = ${QMAKE_FILE_BASE}.cpp +flexsource.commands = flex -o ${QMAKE_FILE_BASE}.cpp ${QMAKE_FILE_IN} +flexsource.variable_out = SOURCES +flexsource.name = Flex Sources ${QMAKE_FILE_IN} +flexsource.CONFIG = += target_predeps +QMAKE_EXTRA_COMPILERS += flexsource + +bisonsource.input = BISONSOURCES +bisonsource.output = ${QMAKE_FILE_BASE}.cpp +bisonsource.commands = bison -d --defines=${QMAKE_FILE_BASE}.hpp -o ${QMAKE_FILE_BASE}.cpp ${QMAKE_FILE_IN} +bisonsource.variable_out = SOURCES +bisonsource.name = Bison Sources ${QMAKE_FILE_IN} +bisonsource.CONFIG += target_predeps +QMAKE_EXTRA_COMPILERS += bisonsource + +bisonheader.input = BISONSOURCES +bisonheader.output = ${QMAKE_FILE_BASE}.hpp +bisonheader.commands = @true +bisonheader.variable_out = HEADERS +bisonheader.name = Bison Headers ${QMAKE_FILE_IN} +bisonheader.CONFIG += target_predeps no_link +QMAKE_EXTRA_COMPILERS += bisonheader + + + + +unix:macx { + PRE_TARGETDEPS += $$OUT_PWD/../../core/libcore.dylib + QMAKE_LFLAGS += -undefined dynamic_lookup +} +else{ + PRE_TARGETDEPS += $$OUT_PWD/../../core/libcore.so +} + + diff --git a/carta/cpp/plugins/RegionDs9/ds9lex.L b/carta/cpp/plugins/RegionDs9/ds9lex.L new file mode 100644 index 00000000..018cf9d7 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/ds9lex.L @@ -0,0 +1,343 @@ +/* Copyright (C) 1999-2016 + * Smithsonian Astrophysical Observatory, Cambridge, MA, USA + * For conditions of distribution and use, see copyright notice in "copyright" + */ + +%option caseless +%option debug +%option never-interactive +%option c++ +%option prefix="ds9lex_" +%option batch + +%{ + + #include "ds9lex.h" + + #include + #include + #include + + #include "ds9parse.hpp" + + typedef yy::ds9parse::token token; + typedef yy::ds9parse::token_type token_type; + + ds9lex *mklexx; +%} + +%x DISCARD + +D [0-9] +E [Ee][+-]?{D}+ +H [0-9a-fA-F] + +/* rules */ + +%% + +[\n] { // special case-- #\n + BEGIN INITIAL; + yyless(0); // put back the terminator + strcpy(yylval->str,""); // feed a blank string + return token::STRING; + } + +[^\n]* { // Discard reset of line + BEGIN INITIAL; + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::STRING; + } + +amplifier {return token::AMPLIFIER_;} +ann[u][l][u][s] {return token::ANNULUS_;} +arcmin {return token::ARCMIN_;} +arcsec {return token::ARCSEC_;} +arrow {return token::ARROW_;} +b1950 {return token::B1950_;} +background {return token::BACKGROUND_;} +begin {return token::BEGIN_;} +box {return token::BOX_;} +boxcircle {return token::BOXCIRCLE_;} +bpanda {return token::BPANDA_;} +callback {return token::CALLBACK_;} +cir[c][l][e] {return token::CIRCLE_;} +circle3d {return token::CIRCLE3D_;} +color {return token::COLOR_;} +compass {return token::COMPASS_;} +composite {return token::COMPOSITE_;} +cpanda {return token::CPANDA_;} +cross {return token::CROSS_;} +dash {return token::DASH_;} +dashlist {return token::DASHLIST_;} +debug {return token::DEBUG_;} +degrees {return token::DEGREES_;} +delete {return token::DELETE_;} +detector {return token::DETECTOR_;} +diamond {return token::DIAMOND_;} +edit {return token::EDIT_;} +ell[i][p][s][e] {return token::ELLIPSE_;} +ecliptic {return token::ECLIPTIC_;} +epanda {return token::EPANDA_;} +end {return token::END_;} +false {return token::FALSE_;} +fie[l][d] {return token::FIELD_;} +fixed {return token::FIXED_;} +fk4 {return token::FK4_;} +fk4-no-e {return token::FK4_NO_E_;} +fk5 {return token::FK5_;} +font {return token::FONT_;} +galactic {return token::GALACTIC_;} +global {return token::GLOBAL_;} +helioecliptic {return token::HELIOECLIPTIC_;} +highlite {return token::HIGHLITE_;} +icrs {return token::ICRS_;} +ignore {return token::IGNORE_;} +include {return token::INCLUDE_;} +image {return token::IMAGE_;} +key {return token::KEY_;} +j2000 {return token::J2000_;} +lin[e] {return token::LINE_;} +linear {return token::LINEAR_;} +move {return token::MOVE_;} +n {return token::N_;} +no {return token::NO_;} +off {return token::OFF_;} +on {return token::ON_;} +panda {return token::CPANDA_;} +physical {return token::PHYSICAL_;} +pie {return token::PIE_;} +pixels {return token::PIXELS_;} +poi[n][t] {return token::POINT_;} +pol[y][g][o][n] {return token::POLYGON_;} +projection {return token::PROJECTION_;} +property {return token::PROPERTY_;} +rotate {return token::ROTATE_;} +rotbox {return token::ROTBOX_;} +ruler {return token::RULER_;} +select {return token::SELECT_;} +segment {return token::SEGMENT_;} +source {return token::SOURCE_;} +supergalactic {return token::SUPERGALACTIC_;} +tag {return token::TAG_;} +text {return token::TEXT_;} +textangle {return token::TEXTANGLE_;} +textrotate {return token::TEXTROTATE_;} +tile {return token::TILE_;} +true {return token::TRUE_;} +vector {return token::VECTOR_;} +version {return token::VERSION_;} +update {return token::UPDATE_;} +unhighlite {return token::UNHIGHLITE_;} +unselect {return token::UNSELECT_;} +wcs {return token::WCS_;} +wcsa {return token::WCSA_;} +wcsb {return token::WCSB_;} +wcsc {return token::WCSC_;} +wcsd {return token::WCSD_;} +wcse {return token::WCSE_;} +wcsf {return token::WCSF_;} +wcsg {return token::WCSG_;} +wcsh {return token::WCSH_;} +wcsi {return token::WCSI_;} +wcsj {return token::WCSJ_;} +wcsk {return token::WCSK_;} +wcsl {return token::WCSL_;} +wcsm {return token::WCSM_;} +wcsn {return token::WCSN_;} +wcso {return token::WCSO_;} +wcsp {return token::WCSP_;} +wcsq {return token::WCSQ_;} +wcsr {return token::WCSR_;} +wcss {return token::WCSS_;} +wcst {return token::WCST_;} +wcsu {return token::WCSU_;} +wcsv {return token::WCSV_;} +wcsw {return token::WCSW_;} +wcsx {return token::WCSX_;} +wcsy {return token::WCSY_;} +wcsz {return token::WCSZ_;} +wcs0 {return token::WCS0_;} +width {return token::WIDTH_;} +x {return token::X_;} +y {return token::Y_;} +yes {return token::YES_;} + + +[+-]?{D}+ { // Integer + yylval->integer = atoi(yytext); + return token::INT; + } + +[+-]?{D}+"."?({E})? | +[+-]?{D}*"."{D}+({E})? { // Real Number + yylval->real = atof(yytext); + return token::REAL; + } + +[+-]?{D}+"."?d | +[+-]?{D}*"."{D}+d { // degrees + yytext[yyleng-1] = '\0'; + yylval->real = atof(yytext); + return token::ANGDEGREE; + } + +[+-]?{D}+"."?r | +[+-]?{D}*"."{D}+r { // radians + yytext[yyleng-1] = '\0'; + yylval->real = atof(yytext); + return token::ANGRADIAN; + } + +[+-]?{D}+"."?p | +[+-]?{D}*"."{D}+p { // physical coords + yytext[yyleng-1] = '\0'; + yylval->real = atof(yytext); + return token::PHYCOORD; + } + +[+-]?{D}+"."?i | +[+-]?{D}*"."{D}+i { // image coords + yytext[yyleng-1] = '\0'; + yylval->real = atof(yytext); + return token::IMGCOORD; + } + +{D}+"."?' | +{D}*"."{D}+' | +[+-]?{D}+"."?({E})?' | +[+-]?{D}*"."{D}+({E})?' { // minutes of arc + yytext[yyleng-1] = '\0'; + yylval->real = atof(yytext); + return token::ARCMINUTE; + } + +{D}+"."?\" | +{D}*"."{D}+\" | +[+-]?{D}+"."?({E})?\" | +[+-]?{D}*"."{D}+({E})?\" { // seconds of arc + yytext[yyleng-1] = '\0'; + yylval->real = atof(yytext); + return token::ARCSECOND; + } + +[+-]?{D}+:{D}+:{D}+"."? | +[+-]?{D}+:{D}+:{D}*"."{D}+ { // Sexagesimal + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::SEXSTR; + } + +[+-]?{D}+h{D}+m{D}+"."?s | +[+-]?{D}+h{D}+m{D}*"."{D}+s { // HMS + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::HMSSTR; + } + +[+-]?{D}+d{D}+m{D}+"."?s | +[+-]?{D}+d{D}+m{D}*"."{D}+s { // DMS + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::DMSSTR; + } + +#({H}){3} { // 8 bit Hex Color + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::STRING; + } + +#({H}){6} { // 16 bit Hex Color + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::STRING; + } + +#({H}){12} { // 32 bit Hex Color + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::STRING; + } + +\"[^\"\n]*\" | +\'[^\'\n]*\' { // Quoted String + int ll = (yyleng-2)<(MKBUFSIZE-1) ? (yyleng-2):(MKBUFSIZE-1); + strncpy(yylval->str,yytext+1,ll); // skip the " " + yylval->str[ll] = '\0'; // Remove the '"' + return token::STRING; + } + +\{[^\}\n]*\} { // Quoted String + int ll = (yyleng-2)<(MKBUFSIZE-1) ? (yyleng-2):(MKBUFSIZE-1); + strncpy(yylval->str,yytext+1,ll); // skip the '{' + yylval->str[ll] = '\0'; // Remove the '}' + return token::STRING; + } + +[0-9A-Za-z]+ { // General String + int ll = yyleng <(MKBUFSIZE-1) ? yyleng:(MKBUFSIZE-1); + strncpy(yylval->str,yytext,ll); + yylval->str[ll] = '\0'; + return token::STRING; + } + +[ \t]+ { // White Spaces + } + +\r\n { // windows line feed + return token::EOL_; + } + +\\n { // fake line feed + return token::EOL_; + } + +\n { // linefeed + return token::EOL_; + } + +<> { // eof + return token::EOF_; + } + +. { // Else, return the char + //return yytext[0]; + return static_cast(*yytext); + } + +%% + + +void ds9lex::discard( int doit ){ + begin(DISCARD, doit); +} + +void ds9lex::begin( int which, int doit ) +{ + BEGIN which; + if (doit) + yyless(0); +} + +#ifdef yylex +#undef yylex +#endif +int ds9lex_FlexLexer::yylex(){ +std::cerr << "In exampple yylex"< +#undef yyFlexLexer +#endif + +#include + + +// Scanner is a derived class to add some extra function to the scanner +// class. Flex itself creates a class named yyFlexLexer, which is renamed using +// macros to ds9lex_FlexLexer. However we change the context of the generated +// yylex() function to be contained within the ds9lex class. This is required +// because the yylex() defined in FlexLexer.has no parameters. */ + +class ds9lex : public ds9lex_FlexLexer { +public: + // Create a new scanner object. The streams arg_yyin and arg_yyout default + // to cin and cout, but that assignment is only made when initializing in + // yylex(). + ds9lex( std::istream* yyin=0, std::ostream* yyout=0) : ds9lex_FlexLexer( yyin, yyout) { + loc = new yy::ds9parse::location_type(); + } + ~ds9lex( ) { } + + // This is the main lexing function. It is generated by flex according to + // the macro declaration YY_DECL above. The generated bison parser then + // calls this virtual function to fetch new tokens. + + virtual yy::ds9parse::token_type lex( yy::ds9parse::semantic_type* yylval, + yy::ds9parse::location_type* location ); + + void discard( int ); + void begin( int which, int doit ); + +private: + yy::ds9parse::semantic_type* yylval = nullptr; + yy::ds9parse::location_type* loc = nullptr; +}; + + + +class CallBack { +public: + enum { SELECTCB, UNSELECTCB, HIGHLITECB, UNHIGHLITECB, + MOVEBEGINCB, MOVECB, MOVEENDCB, EDITBEGINCB, + EDITCB, EDITENDCB, ROTATEBEGINCB, ROTATECB, + ROTATEENDCB, DELETECB, TEXTCB, COLORCB, + LINEWIDTHCB, PROPERTYCB, FONTCB, KEYCB, + UPDATECB + }; +}; + +class Marker { +public: + // Select-- user may select the marker + // Highlite-- user may highlite the marker + // Edit-- user may edit the marker + // Move-- user may move the marker + // Rotate-- user may rotate the marker + // Delete-- user may delete the marker + // Fixed-- marker is fixed in size (not scaled based on zoom) + // Include-- marker is 'included' or 'excluded' ie '+' or '-' + // Source-- marker is a 'source' or 'background' marker + // Dash-- render with dashed line + enum Property { NONE=0, SELECT=1, HIGHLITE=2, EDIT=4, MOVE=8, ROTATE=16, + DELETE=32, FIXED=64, INCLUDE=128, SOURCE=256, DASH=512 + }; +}; +#endif + + diff --git a/carta/cpp/plugins/RegionDs9/ds9parse.y b/carta/cpp/plugins/RegionDs9/ds9parse.y new file mode 100644 index 00000000..ddeca1a8 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/ds9parse.y @@ -0,0 +1,1538 @@ +// Copyright (C) 1999-2016 +// Smithsonian Astrophysical Observatory, Cambridge, MA, USA +// For conditions of distribution and use, see copyright notice in "copyright" + +//%pure-parser + +%require "3.0" +%skeleton "lalr1.cc" +%debug +%defines +%define parser_class_name {ds9parse} + +%code requires { + class ParserDs9; + class ContextDs9; +} + +%parse-param {class ParserDs9& driver} +%parse-param {class ContextDs9* fr } +//%lex-param {mkFlexLexer* ll} +//%parse-param {mkFlexLexer* ll} + +%code{ + +#ifdef yylex +#undef yylex +#endif + +#include +#include +#include +#include + +#include "ParserDs9.h" +#include "ContextDs9.h" +#include "ds9parse.hpp" +#include "ds9lex.h" + +#define YYDEBUG 1 +#define POINTSIZE 11 + +#define FITSPTR (fr) +#define DISCARD_(x) {yyclearin; driver.lexer->discard(x);} + +const int MAXANNULI = 512; +const int MAXANGLES = 720; + + +extern int mklex(void*, FlexLexer*); +extern void mkerror(ContextDs9*, FlexLexer*, const char*); +extern void mkDiscard(int); + +static CoordSystem globalSystem; +static CoordSystem globalWCS; +static SkyFrame globalSky; +static CoordSystem localSystem; +static SkyFrame localSky; + +static int globalTile; + +static unsigned short globalProps; +static unsigned short localProps; + +static int globalDash[2]; +static int localDash[2]; + +static int globalWidth; +static int localWidth; + +static char globalColor[16]; +static char localColor[16]; + +static char globalFont[32]; +static char localFont[32]; + +static char globalText[80]; +static char localText[80]; + +static char localComment[80]; + +static int globalLine1; +static int localLine1; +static int globalLine2; +static int localLine2; + +static int globalVector; +static int localVector; + +static int globalComposite; +static int localComposite; + +static int globalPoint; +static int localPoint; +static int globalPointSize; +static int localPointSize; + +static double globalTextAngle; +static double localTextAngle; +static int globalTextRotate; +static int localTextRotate; + +static CoordSystem globalRulerCoordSystem; +static CoordSystem localRulerCoordSystem; +static SkyFrame globalRulerSkyFrame; +static SkyFrame localRulerSkyFrame; +static CoordSystem globalRulerDistSystem; +static CoordSystem localRulerDistSystem; +static SkyDist globalRulerDistFormat; +static SkyDist localRulerDistFormat; + +static CoordSystem globalCompassCoordSystem; +static SkyFrame globalCompassSkyFrame; +static char globalCompassNorth[80]; +static char globalCompassEast[80]; +static int globalCompassNArrow; +static int globalCompassEArrow; +static CoordSystem localCompassCoordSystem; +static SkyFrame localCompassSkyFrame; +static char localCompassNorth[80]; +static char localCompassEast[80]; +static int localCompassNArrow; +static int localCompassEArrow; + +static int localCpanda; +static int localEpanda; +static int localBpanda; + +static std::list polylist; +static std::list taglist; +static std::list cblist; + +static double aAnnuli[MAXANNULI]; +static Vector aVector[MAXANNULI]; +static int aNum; +static int aNumsao; +static int aStatus; +static int cStatus; +static Vector aCenter; +static double aAngles[MAXANGLES]; +static int aAngNum; +static double aAngle; +static unsigned short aProps; +static char aColor[16]; +static int aWidth; +static int aDash[2]; +static char aFont[32]; +static char aText[80]; +static char aComment[80]; + +static void setProps(unsigned short* props, unsigned short prop, int value); +static CoordSystem checkWCSSystem(); +static SkyFrame checkWCSSky(); + +#include +#undef yylex +#define yylex driver.lexer->lex + +} + +%locations + +%error-verbose + +%union { +#define MKBUFSIZE 2048 + double real; + int integer; + char str[MKBUFSIZE]; + double vector[3]; +} + +%type numeric +%type yesno + +%type angle +%type optangle +%type value +%type vvalue +%type sexagesimal +%type hms +%type dms +%type coord +%type coordSystem +%type wcsSystem +%type skyFrame +%type skyDist +%type property +%type callBack +%type pointShape +%type numberof + +%token INT +%token REAL +%token STRING +%token COLOR + +%token ANGDEGREE +%token ANGRADIAN +%token ARCMINUTE +%token ARCSECOND +%token PHYCOORD +%token IMGCOORD + +%token SEXSTR +%token HMSSTR +%token DMSSTR + +%token EOF_ +%token EOL_ + +%token AMPLIFIER_ +%token ANNULUS_ +%token ARCMIN_ +%token ARCSEC_ +%token ARROW_ +%token B1950_ +%token BACKGROUND_ +%token BEGIN_ +%token BOX_ +%token BOXCIRCLE_ +%token BPANDA_ +%token CALLBACK_ +%token CIRCLE_ +%token CIRCLE3D_ +%token COLOR_ +%token COMPASS_ +%token COMPOSITE_ +%token CPANDA_ +%token CROSS_ +%token DASH_ +%token DASHLIST_ +%token DEBUG_ +%token DEGREES_ +%token DELETE_ +%token DETECTOR_ +%token DIAMOND_ +%token ECLIPTIC_ +%token EDIT_ +%token ELLIPSE_ +%token END_ +%token EPANDA_ +%token FALSE_ +%token FIELD_ +%token FIXED_ +%token FK4_ +%token FK4_NO_E_ +%token FK5_ +%token FONT_ +%token GALACTIC_ +%token GLOBAL_ +%token HELIOECLIPTIC_ +%token HIGHLITE_ +%token ICRS_ +%token IGNORE_ +%token IMAGE_ +%token INCLUDE_ +%token J2000_ +%token KEY_ +%token LINE_ +%token LINEAR_ +%token MOVE_ +%token N_ +%token NO_ +%token OFF_ +%token ON_ +%token PHYSICAL_ +%token PIE_ +%token PIXELS_ +%token POINT_ +%token POLYGON_ +%token PROJECTION_ +%token PROPERTY_ +%token ROTATE_ +%token ROTBOX_ +%token RULER_ +%token SEGMENT_ +%token SELECT_ +%token SOURCE_ +%token SUPERGALACTIC_ +%token TAG_ +%token TEXT_ +%token TEXTANGLE_ +%token TEXTROTATE_ +%token TILE_ +%token TRUE_ +%token VECTOR_ +%token VERSION_ +%token UNHIGHLITE_ +%token UNSELECT_ +%token UPDATE_ +%token WCS_ +%token WCSA_ +%token WCSB_ +%token WCSC_ +%token WCSD_ +%token WCSE_ +%token WCSF_ +%token WCSG_ +%token WCSH_ +%token WCSI_ +%token WCSJ_ +%token WCSK_ +%token WCSL_ +%token WCSM_ +%token WCSN_ +%token WCSO_ +%token WCSP_ +%token WCSQ_ +%token WCSR_ +%token WCSS_ +%token WCST_ +%token WCSU_ +%token WCSV_ +%token WCSW_ +%token WCSX_ +%token WCSY_ +%token WCSZ_ +%token WCS0_ +%token WIDTH_ +%token X_ +%token Y_ +%token YES_ + + + +%% + +start : initGlobal commands postLocal + ; + +commands: commands command terminator + | command terminator + ; + +command : /* empty */ + | DEBUG_ debug + | VERSION_ {std::cerr << "DS9 Regions File 3.2" << std::endl;} + + | GLOBAL_ global comment + | TILE_ INT {globalTile = $2;} + + | coordSystem {globalSystem=( CoordSystem)$1;} comment + | skyFrame {globalSystem=globalWCS; globalSky=( SkyFrame)$1;} comment + | LINEAR_ {globalSystem=globalWCS; globalSky= FK5;} comment + + | initLocal shape + | initLocal include shape + | initLocal '#' hash + ; + +hash : nonshape + | include nonshape + | TILE_ INT {globalTile = $2;} + | {DISCARD_(1);} STRING + ; + +comment : /* empty */ + | '#' {DISCARD_(1);} STRING + ; + +shapeComment : /* empty */ postLocal + | '#' {DISCARD_(1);} STRING postLocal {strncpy(localComment,$3,80);} + | '#' local postLocal + | '#' local {DISCARD_(1);} STRING postLocal {strncpy(localComment,$4,80);} + ; + +nonshapeComment : /* empty */ postLocal + | {DISCARD_(1);} STRING postLocal {strncpy(localComment,$2,80);} + | local postLocal + | local {DISCARD_(1);} STRING postLocal {strncpy(localComment,$3,80);} + ; + +terminator: EOL_ + | EOF_ {YYACCEPT;} + ; + +numeric : REAL {$$=$1;} + | INT {$$=$1;} + ; + +debug : ON_ {yydebug_=1;} + | OFF_ {yydebug_=0;} + ; + +yesno : INT {$$=($1 ? 1 : 0);} + + | YES_ {$$=1;} + | Y_ {$$=1;} + | ON_ {$$=1;} + | TRUE_ {$$=1;} + + | NO_ {$$=0;} + | N_ {$$=0;} + | OFF_ {$$=0;} + | FALSE_ {$$=0;} + ; + +sp : /* empty */ + | ',' + ; + +bp : /* empty */ + | '(' + ; + +ep : /* emtpy */ + | ')' + ; + +conjuction : /* empty */ {cStatus = 0;} + | '|' {cStatus = 1;} + | '|' '|' {cStatus = 1;} + ; + +optangle: /* empty */ {$$ = fr->mapAngleToRef(0,localSystem,localSky);} + | angle {$$ = $1;} + ; + +angle : numeric {$$ = fr->mapAngleToRef(degToRad($1),localSystem,localSky);} + | ANGDEGREE {$$ = fr->mapAngleToRef(degToRad($1),localSystem,localSky);} + | ANGRADIAN {$$ = fr->mapAngleToRef($1,localSystem,localSky);} + ; + +value : numeric {$$ = FITSPTR->mapLenToRef($1, localSystem, DEGREE);} + | PHYCOORD {$$ = FITSPTR->mapLenToRef($1, PHYSICAL);} + | IMGCOORD {$$ = FITSPTR->mapLenToRef($1, IMAGE);} + | ANGDEGREE {$$ = FITSPTR->mapLenToRef($1, checkWCSSystem(), DEGREE);} + | ARCMINUTE {$$ = FITSPTR->mapLenToRef($1, checkWCSSystem(), ARCMIN);} + | ARCSECOND {$$ = FITSPTR->mapLenToRef($1, checkWCSSystem(), ARCSEC);} + ; + +vvalue : numeric sp numeric + { + Vector r = FITSPTR->mapLenToRef(doubletovec($1,$3), localSystem, DEGREE); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | PHYCOORD sp PHYCOORD + { + Vector r = FITSPTR->mapLenToRef(doubletovec($1,$3), PHYSICAL); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | IMGCOORD sp IMGCOORD + { + Vector r = FITSPTR->mapLenToRef(doubletovec($1,$3), IMAGE); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | ANGDEGREE sp ANGDEGREE + { + Vector r=FITSPTR->mapLenToRef(doubletovec($1,$3),checkWCSSystem(), DEGREE); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | ARCMINUTE sp ARCMINUTE + { + Vector r=FITSPTR->mapLenToRef(doubletovec($1,$3),checkWCSSystem(), ARCMIN); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | ARCSECOND sp ARCSECOND + { + Vector r=FITSPTR->mapLenToRef(doubletovec($1,$3),checkWCSSystem(), ARCSEC); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + ; + +numberof: N_ '=' INT {$$ = $3;} + ; + +sexagesimal: SEXSTR {$$ = parseSEXStr($1);} + ; + +hms : HMSSTR {$$ = parseHMSStr($1);} + ; + +dms : DMSSTR {$$ = parseDMSStr($1);} + ; + +coord : sexagesimal sp sexagesimal + { + Vector r; + CoordSystem sys = checkWCSSystem(); + SkyFrame sky = checkWCSSky(); + if (sky == GALACTIC || sky == ECLIPTIC) + r = FITSPTR->mapToRef(doubletovec($1,$3), sys, sky); + else + r = FITSPTR->mapToRef(doubletovec($1*360./24.,$3), sys, sky); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | hms sp dms + { + Vector r = FITSPTR->mapToRef(doubletovec($1,$3), + checkWCSSystem(), checkWCSSky()); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | dms sp dms + { + Vector r = FITSPTR->mapToRef(doubletovec($1,$3), + checkWCSSystem(), checkWCSSky()); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | numeric sp numeric + { + Vector r = FITSPTR->mapToRef(doubletovec($1,$3), localSystem, localSky); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | ANGDEGREE sp ANGDEGREE + { + Vector r = FITSPTR->mapToRef(doubletovec($1,$3), + checkWCSSystem(), checkWCSSky()); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | IMGCOORD sp IMGCOORD + { + Vector r = FITSPTR->mapToRef(doubletovec($1,$3), IMAGE); + $$[0] = r[0]; + $$[1] = r[1]; + $$[2] = r[2]; + } + | PHYCOORD sp PHYCOORD + { + Vector r = FITSPTR->mapToRef(doubletovec($1,$3), PHYSICAL); + $$[0] = r[0]; + $$[1] = r[1]; + } + ; + +coordSystem :IMAGE_ {$$ = IMAGE;} + | PHYSICAL_ {$$ = PHYSICAL;} + | DETECTOR_ {$$ = DETECTOR;} + | AMPLIFIER_ {$$ = AMPLIFIER;} + | wcsSystem {$$ = $1; globalWCS = ( CoordSystem)$1;} + ; + +wcsSystem : WCS_ {$$ = WCS;} + | WCSA_ {$$ = WCSA;} + | WCSB_ {$$ = WCSB;} + | WCSC_ {$$ = WCSC;} + | WCSD_ {$$ = WCSD;} + | WCSE_ {$$ = WCSE;} + | WCSF_ {$$ = WCSF;} + | WCSG_ {$$ = WCSG;} + | WCSH_ {$$ = WCSH;} + | WCSI_ {$$ = WCSI;} + | WCSJ_ {$$ = WCSJ;} + | WCSK_ {$$ = WCSK;} + | WCSL_ {$$ = WCSL;} + | WCSM_ {$$ = WCSM;} + | WCSN_ {$$ = WCSN;} + | WCSO_ {$$ = WCSO;} + | WCSP_ {$$ = WCSP;} + | WCSQ_ {$$ = WCSQ;} + | WCSR_ {$$ = WCSR;} + | WCSS_ {$$ = WCSS;} + | WCST_ {$$ = WCST;} + | WCSU_ {$$ = WCSU;} + | WCSV_ {$$ = WCSV;} + | WCSW_ {$$ = WCSW;} + | WCSX_ {$$ = WCSX;} + | WCSY_ {$$ = WCSY;} + | WCSZ_ {$$ = WCSZ;} + | WCS0_ {$$ = WCS0;} + ; + +skyFrame : FK4_ {$$ = FK4;} + | B1950_ {$$ = FK4;} + | FK4_NO_E_ {$$ = FK4_NO_E;} + | FK5_ {$$ = FK5;} + | J2000_ {$$ = FK5;} + | ICRS_ {$$ = ICRS;} + | GALACTIC_ {$$ = GALACTIC;} + | SUPERGALACTIC_ {$$ = SUPERGALACTIC;} + | ECLIPTIC_ {$$ = ECLIPTIC;} + | HELIOECLIPTIC_ {$$ = HELIOECLIPTIC;} + ; + +skyDist : DEGREES_ {$$= DEGREE;} + | ARCMIN_ {$$= ARCMIN;} + | ARCSEC_ {$$= ARCSEC;} + ; + +property : SELECT_ {$$ = Marker::SELECT;} + | HIGHLITE_ {$$ = Marker::HIGHLITE;} + | DASH_ {$$ = Marker::DASH;} + | FIXED_ {$$ = Marker::FIXED;} + | EDIT_ {$$ = Marker::EDIT;} + | MOVE_ {$$ = Marker::MOVE;} + | ROTATE_ {$$ = Marker::ROTATE;} + | DELETE_ {$$ = Marker::DELETE;} + | INCLUDE_ {$$ = Marker::INCLUDE;} + | SOURCE_ {$$ = Marker::SOURCE;} + ; + +callBack : SELECT_ {$$ = CallBack::SELECTCB;} + | UNSELECT_ {$$ = CallBack::UNSELECTCB;} + | HIGHLITE_ {$$ = CallBack::HIGHLITECB;} + | UNHIGHLITE_ {$$ = CallBack::UNHIGHLITECB;} + | BEGIN_ MOVE_ {$$ = CallBack::MOVEBEGINCB;} + | MOVE_ {$$ = CallBack::MOVECB;} + | END_ MOVE_ {$$ = CallBack::MOVEENDCB;} + | BEGIN_ EDIT_ {$$ = CallBack::EDITBEGINCB;} + | EDIT_ {$$ = CallBack::EDITCB;} + | END_ EDIT_ {$$ = CallBack::EDITENDCB;} + | BEGIN_ ROTATE_ {$$ = CallBack::ROTATEBEGINCB;} + | ROTATE_ {$$ = CallBack::ROTATECB;} + | END_ ROTATE_ {$$ = CallBack::ROTATEENDCB;} + | DELETE_ {$$ = CallBack::DELETECB;} + | TEXT_ {$$ = CallBack::TEXTCB;} + | COLOR_ {$$ = CallBack::COLORCB;} + | WIDTH_ {$$ = CallBack::LINEWIDTHCB;} + | PROPERTY_ {$$ = CallBack::PROPERTYCB;} + | FONT_ {$$ = CallBack::FONTCB;} + | KEY_ {$$ = CallBack::KEYCB;} + | UPDATE_ {$$ = CallBack::UPDATECB;} + ; + +global : global sp globalProperty + | globalProperty + ; + +globalProperty : property '=' yesno + { + setProps(&globalProps,$1,$3); + setProps(&localProps,$1,$3); + } + | COLOR_ '=' STRING + { + strncpy(globalColor,$3,16); + strncpy(localColor,$3,16); + } + | DASHLIST_ '=' INT INT + { + globalDash[0] = localDash[0] =$3; + globalDash[1] = localDash[1] =$4; + } + | WIDTH_ '=' INT {globalWidth = localWidth = $3;} + | FONT_ '=' STRING + { + strncpy(globalFont,$3,32); + strncpy(localFont,$3,32); + } + | TEXT_ '=' STRING + { + strncpy(globalText,$3,80); + strncpy(localText,$3,80); + } + | DASH_ + { + setProps(&globalProps,Marker::DASH,1); + setProps(&localProps,Marker::DASH,1); + } + | SOURCE_ + { + setProps(&globalProps,Marker::SOURCE,1); + setProps(&localProps,Marker::SOURCE,1); + } + | BACKGROUND_ + { + setProps(&globalProps,Marker::SOURCE,0); + setProps(&localProps,Marker::SOURCE,0); + } + | POINT_ '=' pointShape {globalPoint = localPoint = $3;} + | POINT_ '=' pointShape INT + { + globalPoint = localPoint = $3; + globalPointSize = localPointSize = $4; + } + | LINE_ '=' INT INT + { + globalLine1 = localLine1 = $3; + globalLine2 = localLine2 = $4; + } + | VECTOR_ '=' INT {globalVector = localVector = $3;} + | COMPOSITE_ '=' INT + { + globalComposite = localComposite = $3; + } + | RULER_ '=' globalRuler {} + | COMPASS_ '=' globalCompass STRING STRING INT INT + { + strncpy(globalCompassNorth,$4,80); + strncpy(globalCompassEast,$5,80); + strncpy(localCompassNorth,$4,80); + strncpy(localCompassEast,$5,80); + globalCompassNArrow = localCompassNArrow = $6; + globalCompassEArrow = localCompassEArrow = $7; + } + | TEXTANGLE_ '=' angle {globalTextAngle = localTextAngle = $3;} + | TEXTROTATE_ '=' INT {globalTextRotate = localTextRotate = $3;} + | WCS_ '=' wcsSystem {globalWCS = ( CoordSystem)$3;} + ; + +globalRuler : coordSystem skyFrame coordSystem skyDist + { + globalRulerCoordSystem = localRulerCoordSystem = ( CoordSystem)$1; + globalRulerSkyFrame = localRulerSkyFrame = ( SkyFrame)$2; + globalRulerDistSystem = localRulerDistSystem = ( CoordSystem)$3; + globalRulerDistFormat = localRulerDistFormat = ( SkyDist)$4; + } + | coordSystem coordSystem + { + globalRulerCoordSystem = localRulerCoordSystem = ( CoordSystem)$1; + globalRulerSkyFrame = localRulerSkyFrame = FK5; + globalRulerDistSystem = localRulerDistSystem = ( CoordSystem)$2; + globalRulerDistFormat = localRulerDistFormat = DEGREE; + } + | coordSystem skyDist + { + globalRulerCoordSystem = localRulerCoordSystem = ( CoordSystem)$1; + globalRulerSkyFrame = localRulerSkyFrame = FK5; + globalRulerDistSystem = localRulerDistSystem = WCS; + globalRulerDistFormat = localRulerDistFormat = ( SkyDist)$2; + } + | skyFrame coordSystem + { + globalRulerCoordSystem = localRulerCoordSystem = WCS; + globalRulerSkyFrame = localRulerSkyFrame = ( SkyFrame)$1; + globalRulerDistSystem = localRulerDistSystem = ( CoordSystem)$2; + globalRulerDistFormat = localRulerDistFormat = DEGREE; + } + | skyFrame skyDist + { + globalRulerCoordSystem = localRulerCoordSystem = WCS; + globalRulerSkyFrame = localRulerSkyFrame = ( SkyFrame)$1; + globalRulerDistSystem = localRulerDistSystem = WCS; + globalRulerDistFormat = localRulerDistFormat = ( SkyDist)$2; + } + | LINEAR_ coordSystem + { + globalRulerCoordSystem = localRulerCoordSystem = WCS; + globalRulerSkyFrame = localRulerSkyFrame = FK5; + globalRulerDistSystem = localRulerDistSystem = ( CoordSystem)$2; + globalRulerDistFormat = localRulerDistFormat = DEGREE; + } + | LINEAR_ skyDist + { + globalRulerCoordSystem = localRulerCoordSystem = WCS; + globalRulerSkyFrame = localRulerSkyFrame = FK5; + globalRulerDistSystem = localRulerDistSystem = WCS; + globalRulerDistFormat = localRulerDistFormat = ( SkyDist)$2; + } + | skyDist + { + globalRulerCoordSystem = localRulerCoordSystem = IMAGE; + globalRulerSkyFrame = localRulerSkyFrame = FK5; + globalRulerDistSystem = localRulerDistSystem = WCS; + globalRulerDistFormat = localRulerDistFormat = ( SkyDist)$1; + } + | PIXELS_ + { + globalRulerCoordSystem = localRulerCoordSystem = IMAGE; + globalRulerSkyFrame = localRulerSkyFrame = FK5; + globalRulerDistSystem = localRulerDistSystem = IMAGE; + globalRulerDistFormat = localRulerDistFormat = DEGREE; + } + ; + +globalCompass : coordSystem skyFrame + { + globalCompassCoordSystem = localCompassCoordSystem = ( CoordSystem)$1; + globalCompassSkyFrame = localCompassSkyFrame = ( SkyFrame)$2; + } + | coordSystem + { + globalCompassCoordSystem = localCompassCoordSystem = ( CoordSystem)$1; + globalCompassSkyFrame = localCompassSkyFrame = FK5; + } + | skyFrame + { + globalCompassCoordSystem = localCompassCoordSystem = WCS; + globalCompassSkyFrame = localCompassSkyFrame = ( SkyFrame)$1; + } + | LINEAR_ + { + globalCompassCoordSystem = localCompassCoordSystem = WCS; + globalCompassSkyFrame = localCompassSkyFrame = FK5; + } + ; + +initGlobal:{ + // global properties + globalSystem = PHYSICAL; + globalWCS = WCS; + globalSky = NATIVEWCS; + globalTile = 1; + globalProps = + Marker::SELECT | Marker::EDIT | Marker::MOVE | + Marker::ROTATE | Marker::DELETE | Marker::HIGHLITE | + Marker::INCLUDE | Marker::SOURCE; + strcpy(globalColor,"green"); + globalDash[0] = 8; + globalDash[1] = 3; + globalWidth = 1; + strcpy(globalFont,"helvetica 10 normal roman"); + strcpy(globalText,""); + + // unique properties + globalLine1 = 0; + globalLine2 = 0; + globalVector = 1; + globalComposite = 1; + globalRulerCoordSystem = PHYSICAL; + globalRulerSkyFrame = FK5; + globalRulerDistSystem = PHYSICAL; + globalRulerDistFormat = DEGREE; + globalCompassCoordSystem = PHYSICAL; + globalCompassSkyFrame = FK5; + strcpy(globalCompassNorth,"N"); + strcpy(globalCompassEast,"E"); + globalCompassNArrow = 1; + globalCompassEArrow = 1; + globalPoint = BOXCIRCLE; + globalPointSize = POINTSIZE; + globalTextAngle=0; + globalTextRotate=1; + + aStatus = 0; + cStatus = 0; + } + ; + +local : local sp localProperty + | localProperty + ; + +localProperty : property '=' yesno {setProps(&localProps,$1,$3);} + | COLOR_ '=' STRING {strncpy(localColor,$3,16);} + | DASHLIST_ '=' INT INT + { + localDash[0] =$3; + localDash[1] =$4; + } + | WIDTH_ '=' INT {localWidth = $3;} + | FONT_ '=' STRING {strncpy(localFont,$3,32);} + | TEXT_ '=' STRING {strncpy(localText,$3,80);} + | TAG_ '=' STRING {taglist.push_back($3);} + | CALLBACK_ '=' callBack STRING STRING {/*cblist.append( + new CallBack(fr->getInterp(),(CallBack::Type)$3,$4,$5));*/} + | DASH_ {setProps(&localProps,Marker::DASH,1);} + | SOURCE_ {setProps(&localProps,Marker::SOURCE,1);} + | BACKGROUND_ {setProps(&localProps,Marker::SOURCE,0);} + + | POINT_ '=' pointShape {localPoint = $3;} + | POINT_ '=' pointShape INT {localPoint = $3; localPointSize = $4;} + | LINE_ '=' INT INT {localLine1=$3; localLine2=$4;} + | VECTOR_ '=' INT {localVector=$3;} + | COMPOSITE_ '=' INT {localComposite=$3;} + | RULER_ '=' localRuler + | COMPASS_ '=' localCompass STRING STRING INT INT + { + strncpy(localCompassNorth,$4,80); + strncpy(localCompassEast,$5,80); + localCompassNArrow = $6; + localCompassEArrow = $7; + } + | TEXTANGLE_ '=' angle {localTextAngle=$3;} + | TEXTROTATE_ '=' INT {localTextRotate=$3;} + | CPANDA_ '=' localCpanda + | EPANDA_ '=' localEpanda + | BPANDA_ '=' localBpanda + ; + +localRuler : coordSystem skyFrame coordSystem skyDist + { + localRulerCoordSystem = ( CoordSystem)$1; + localRulerSkyFrame = ( SkyFrame)$2; + localRulerDistSystem = ( CoordSystem)$3; + localRulerDistFormat = ( SkyDist)$4; + } + | coordSystem coordSystem + { + localRulerCoordSystem = ( CoordSystem)$1; + localRulerSkyFrame = FK5; + localRulerDistSystem = ( CoordSystem)$2; + localRulerDistFormat = DEGREE; + } + | coordSystem skyDist + { + localRulerCoordSystem = ( CoordSystem)$1; + localRulerSkyFrame = FK5; + localRulerDistSystem = WCS; + localRulerDistFormat = ( SkyDist)$2; + } + | skyFrame coordSystem + { + localRulerCoordSystem = WCS; + localRulerSkyFrame = ( SkyFrame)$1; + localRulerDistSystem = ( CoordSystem)$2; + localRulerDistFormat = DEGREE; + } + | skyFrame skyDist + { + localRulerCoordSystem = WCS; + localRulerSkyFrame = ( SkyFrame)$1; + localRulerDistSystem = WCS; + localRulerDistFormat = ( SkyDist)$2; + } + | LINEAR_ coordSystem + { + localRulerCoordSystem = WCS; + localRulerSkyFrame = FK5; + localRulerDistSystem = ( CoordSystem)$2; + localRulerDistFormat = DEGREE; + } + | LINEAR_ skyDist + { + localRulerCoordSystem = WCS; + localRulerSkyFrame = FK5; + localRulerDistSystem = WCS; + localRulerDistFormat = ( SkyDist)$2; + } + | skyDist + { + localRulerCoordSystem = IMAGE; + localRulerSkyFrame = FK5; + localRulerDistSystem = WCS; + localRulerDistFormat = ( SkyDist)$1; + } + | PIXELS_ + { + localRulerCoordSystem = IMAGE; + localRulerSkyFrame = FK5; + localRulerDistSystem = IMAGE; + localRulerDistFormat = DEGREE; + } + ; + +localCompass : coordSystem skyFrame + { + localCompassCoordSystem = ( CoordSystem)$1; + localCompassSkyFrame = ( SkyFrame)$2; + } + | coordSystem + { + localCompassCoordSystem = ( CoordSystem)$1; + localCompassSkyFrame = FK5; + } + | skyFrame + { + localCompassCoordSystem = WCS; + localCompassSkyFrame = ( SkyFrame)$1; + } + | LINEAR_ + { + localCompassCoordSystem = WCS; + localCompassSkyFrame = FK5; + } + ; + +localCpanda: {aNum=0; aAngNum=0;} '(' aAngs ')' '(' aRads ')' {localCpanda = 2;} + | IGNORE_ {localCpanda=0;} + ; + +localEpanda: {aNum=0; aAngNum=0, aAngle=0;} + '(' aAngs ')' '(' vRads ')' '(' angle ')' {aAngle=$9;localEpanda=2;} + | IGNORE_ {localEpanda=0;} + ; + +localBpanda: {aNum=0; aAngNum=0, aAngle=0;} + '(' aAngs ')' '(' vRads ')' '(' angle ')' {aAngle=$9;localBpanda=2;} + | IGNORE_ {localBpanda=0;} + ; + +initLocal : { + // reset maperr flag + //maperr = 0; + + // needed for annulus, ellipse annulus, and box annulus + aNum = 2; + + // composite (previous conjuction found?) + if (!cStatus) fr->resetCompositeMarker(); + + // global properties + localSystem = globalSystem; + localSky = globalSky; + localProps = globalProps; + strcpy(localColor,globalColor); + localDash[0] = globalDash[0]; + localDash[1] = globalDash[1]; + localWidth = globalWidth; + strcpy(localFont,globalFont); + strcpy(localText,globalText); + strcpy(localComment,""); + taglist.clear(); + cblist.clear(); + + // unique properties + localLine1 = globalLine1; + localLine2 = globalLine2; + localVector = globalVector; + localComposite = globalComposite; + localPoint = globalPoint; + localPointSize = globalPointSize; + localRulerCoordSystem = globalRulerCoordSystem; + localRulerSkyFrame = globalRulerSkyFrame; + localRulerDistSystem = globalRulerDistSystem; + localRulerDistFormat = globalRulerDistFormat; + localCompassCoordSystem = globalCompassCoordSystem; + localCompassSkyFrame = globalCompassSkyFrame; + strcpy(localCompassNorth,globalCompassNorth); + strcpy(localCompassEast,globalCompassEast); + localCompassNArrow = globalCompassNArrow; + localCompassEArrow = globalCompassEArrow; + localTextAngle = globalTextAngle; + localTextRotate = globalTextRotate; + localCpanda = 1; + localEpanda = 1; + localBpanda = 1; + } + ; + +pointShape : CIRCLE_ {$$ = CIRCLE;} + | BOX_ {$$ = BOX;} + | DIAMOND_ {$$ = DIAMOND;} + | CROSS_ {$$ = CROSS;} + | X_ {$$ = XPT;} + | ARROW_ {$$ = ARROW;} + | BOXCIRCLE_ {$$ = BOXCIRCLE;} + ; + +include : '+' {setProps(&localProps, Marker::INCLUDE, 1);} + | '-' {setProps(&localProps, Marker::INCLUDE, 0);} + ; + +nonshape : TEXT_ bp coord ep conjuction nonshapeComment + {fr->createTextCmd(coordtovec($3), + localTextAngle,localTextRotate, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist /*,cblist*/);} + | COMPOSITE_ bp coord sp optangle ep conjuction nonshapeComment + {fr->createCompositeCmd(coordtovec($3), + $5, localComposite, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist /*,cblist*/);} + | VECTOR_ bp coord sp value sp angle ep conjuction nonshapeComment + {fr->createVectCmd(coordtovec($3), + $5,$7, + localVector, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist /*,cblist*/);} + | PROJECTION_ bp coord sp coord sp value ep conjuction nonshapeComment + { + // hard coded into projection.tcl + fr->createProjectionCmd(coordtovec($3), + coordtovec($5), + $7, + "ProjectionPlotCB", "ProjectionPlotDeleteCB", + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist /*,cblist*/); + } + | RULER_ bp coord sp coord ep conjuction nonshapeComment + { + fr->createRulerCmd(coordtovec($3), + coordtovec($5), + localRulerCoordSystem, localRulerSkyFrame, + localRulerDistSystem, localRulerDistFormat, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | COMPASS_ bp coord sp value ep conjuction nonshapeComment + { + fr->createCompassCmd(coordtovec($3), + $5, + localCompassNorth, localCompassEast, + localCompassNArrow, localCompassEArrow, + localCompassCoordSystem, localCompassSkyFrame, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + + | CIRCLE3D_ bp coord sp value ep conjuction nonshapeComment + { + // backward compatibility + fr->createCircleCmd(coordtovec($3), + $5, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + ; + +shape : CIRCLE_ bp coord sp value ep conjuction shapeComment + { + fr->createCircleCmd(coordtovec($3), + $5, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | CIRCLE3D_ bp coord sp value ep conjuction shapeComment + { + // backwards compatibility + fr->createCircleCmd(coordtovec($3), + $5, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ELLIPSE_ bp coord sp vvalue sp optangle ep conjuction shapeComment + { + // for ellipse annulus + aStatus = 1; + aCenter = coordtovec($3); + aAngles[0] = $7; + aVector[0] = coordtovec($5); + aNumsao = 1; + strncpy(aColor,localColor,16); + aDash[0] = localDash[0]; + aDash[1] = localDash[1]; + aWidth = localWidth; + strncpy(aFont,localFont,32); + strncpy(aText,localText,80); + strncpy(aComment,localComment,80); + aProps = localProps; + + fr->createEllipseCmd(coordtovec($3), + coordtovec($5), + $7, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | BOX_ bp coord sp vvalue sp optangle ep conjuction shapeComment + { + // for box annulus + aStatus = 3; + aCenter = coordtovec($3); + aAngles[0] = $7; + aVector[0] = coordtovec($5); + aNumsao = 1; + strncpy(aColor,localColor,16); + aDash[0] = localDash[0]; + aDash[1] = localDash[1]; + aWidth = localWidth; + strncpy(aFont,localFont,32); + strncpy(aText,localText,80); + strncpy(aComment,localComment,80); + aProps = localProps; + + fr->createBoxCmd(coordtovec($3), + coordtovec($5), + $7, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ROTBOX_ bp coord sp vvalue sp optangle ep conjuction shapeComment + { + // backwards compatibility + fr->createBoxCmd(coordtovec($3), + coordtovec($5), + $7, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | POLYGON_ {polylist.clear();} bp polyNodes ep conjuction + shapeComment + { + fr->createPolygonCmd(polylist, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | SEGMENT_ {polylist.clear();} bp polyNodes ep conjuction + shapeComment + { + fr->createSegmentCmd(polylist, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + + | LINE_ bp coord sp coord ep conjuction shapeComment + { + fr->createLineCmd(coordtovec($3), + coordtovec($5), + localLine1,localLine2, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | VECTOR_ bp coord sp value sp angle ep conjuction shapeComment + { + fr->createVectCmd(coordtovec($3), + $5,$7, + localVector, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | TEXT_ bp coord ep conjuction shapeComment + { + fr->createTextCmd(coordtovec($3), + localTextAngle,localTextRotate, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | TEXT_ bp coord sp STRING ep {strncpy(localText,$5,80);} conjuction + shapeComment + { + fr->createTextCmd(coordtovec($3), + localTextAngle,localTextRotate, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | POINT_ bp coord ep conjuction shapeComment + { + fr->createPointCmd(coordtovec($3), + (PointShape)localPoint, localPointSize, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + + | RULER_ bp coord sp coord ep conjuction shapeComment + { + fr->createRulerCmd(coordtovec($3), + coordtovec($5), + localRulerCoordSystem, localRulerSkyFrame, + localRulerDistSystem, localRulerDistFormat, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | COMPASS_ bp coord sp value ep conjuction shapeComment + { + fr->createCompassCmd(coordtovec($3), + $5, + localCompassNorth, localCompassEast, + localCompassNArrow, localCompassEArrow, + localCompassCoordSystem, localCompassSkyFrame, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | PROJECTION_ bp coord sp coord sp value ep conjuction shapeComment + { + fr->createProjectionCmd(coordtovec($3), + coordtovec($5), + $7, + "ProjectionPlotCB", "ProjectionPlotDeleteCB", + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ANNULUS_ bp coord sp value sp value ep conjuction shapeComment + { + fr->createAnnulusCmd(coordtovec($3), + $5,$7,1, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ANNULUS_ bp coord sp value sp value sp aRads ep conjuction + shapeComment + { + aAnnuli[0] = $5; + aAnnuli[1] = $7; + fr->createAnnulusCmd(coordtovec($3), + aNum,aAnnuli, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ANNULUS_ bp coord sp value sp value sp numberof ep conjuction + shapeComment + { + fr->createAnnulusCmd(coordtovec($3), + $5,$7,$9, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ELLIPSE_ bp coord sp vvalue sp vvalue sp optangle ep conjuction + shapeComment + { + // prefered syntax + fr->createEllipseAnnulusCmd(coordtovec($3), + coordtovec($5),coordtovec($7),1, + $9, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ELLIPSE_ bp coord sp vvalue sp vvalue sp + numberof sp optangle ep conjuction shapeComment + { + // prefered syntax + fr->createEllipseAnnulusCmd(coordtovec($3), + coordtovec($5),coordtovec($7),$9, + $11, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ELLIPSE_ bp coord sp vvalue sp vvalue sp + vRads sp optangle ep conjuction shapeComment + { + // prefered syntax + aVector[0] = coordtovec($5); + aVector[1] = coordtovec($7); + fr->createEllipseAnnulusCmd(coordtovec($3), + aNum,aVector, + $11, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | ELLIPSE_ bp coord sp vvalue sp optangle ep '&' '!' + ELLIPSE_ bp coord sp vvalue sp optangle ep + { + // backwards compatibility + // old saoimage syntax + aStatus = 2; + aVector[aNumsao++] = coordtovec($5); + } + | BOX_ bp coord sp vvalue sp vvalue sp optangle ep conjuction + shapeComment + { + // prefered syntax + fr->createBoxAnnulusCmd(coordtovec($3), + coordtovec($5),coordtovec($7),1, + $9, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + + + + + | BOX_ bp coord sp vvalue sp vvalue sp + numberof sp optangle ep conjuction shapeComment + { + // prefered syntax + fr->createBoxAnnulusCmd(coordtovec($3), + aNum, aVector, + $11, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + | BOX_ bp coord sp vvalue sp optangle ep '&' '!' + BOX_ bp coord sp vvalue sp optangle ep + { + // backwards compatibility + // old saoimage syntax + aStatus = 4; + aVector[aNumsao++] = coordtovec($5); + } + + | CPANDA_ bp coord sp angle sp angle sp INT sp + value sp value sp INT ep conjuction shapeComment + { + switch (localCpanda) { + case 0: /* ignore it */ + break; + case 1: /* normal cpanda */ + fr->createCpandaCmd(coordtovec($3), + $5,$7,$9, + $11,$13,$15, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + break; + case 2: /* one of our special pandas */ + fr->createCpandaCmd(coordtovec($3), + aAngNum,aAngles, + aNum,aAnnuli, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + break; + } + } + | EPANDA_ bp coord sp angle sp angle sp INT sp + vvalue sp vvalue sp INT sp optangle ep conjuction shapeComment + { + switch (localEpanda) { + case 0: /* ignore it */ + break; + case 1: /* normal epanda */ + fr->createEpandaCmd(coordtovec($3), + $5,$7,$9, + coordtovec($11),coordtovec($13),$15, + $17, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + break; + case 2: /* one of our special pandas */ + fr->createEpandaCmd(coordtovec($3), + aAngNum,aAngles, + aNum,aVector, + aAngle, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + break; + } + } + | BPANDA_ bp coord sp angle sp angle sp INT sp + vvalue sp vvalue sp INT sp optangle ep conjuction shapeComment + { + switch (localBpanda) { + case 0: /* ignore it */ + break; + case 1: /* normal bpanda */ + fr->createBpandaCmd(coordtovec($3), + $5,$7,$9, + coordtovec($11),coordtovec($13),$15, + $17, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + break; + case 2: /* one of our special pandas */ + fr->createBpandaCmd(coordtovec($3), + aAngNum,aAngles, + aNum,aVector, + aAngle, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + break; + } + } + + | PIE_ bp coord sp angle sp angle ep conjuction shapeComment + | PIE_ bp coord sp angle sp angle sp aAngs ep conjuction shapeComment + | PIE_ bp coord sp angle sp angle sp numberof ep conjuction + shapeComment + | FIELD_ bp ep conjuction shapeComment + + | COMPOSITE_ bp coord sp optangle ep conjuction shapeComment + { + fr->createCompositeCmd(coordtovec($3), + $5, localComposite, + localColor,localDash,localWidth,localFont, + localText,localProps,localComment,taglist/*,cblist*/); + } + ; + +polyNodes : polyNodes sp polyNode + | polyNode + ; + +polyNode : coord {polylist.push_back(coordtovec($1));} + ; + +aRads : aRads sp aRad + | aRad + ; + +aRad : value + { + if (aNum < MAXANNULI) + aAnnuli[aNum++] = $1; + } + ; + +aAngs : aAngs sp aAng + | aAng + ; + +aAng : angle + { + if (aAngNum < MAXANGLES) + aAngles[aAngNum++] = $1; + } + ; + +vRads : vRads sp vRad + | vRad + ; + +vRad : value sp value {aVector[aNum++] = doubletovec($1,$3);} + ; + +postLocal : /* empty */ + { + // old style annulus + switch (aStatus) { + case 0: // do nothing + break; + case 1: // we found just an ellipse, do nothing + break; + case 2: // ok we have an ellipse annulus + fr->markerDeleteLastCmd(); // delete the previous ellipse + fr->createEllipseAnnulusCmd(aCenter, + aNumsao,aVector, + aAngles[0], + aColor,aDash,aWidth,aFont,aText,aProps,aComment,taglist/*,cblist*/); + break; + case 3: // we found just a box, do nothing + break; + case 4: // ok, we have a box annulus + fr->markerDeleteLastCmd(); // delete the previous box + fr->createBoxAnnulusCmd(aCenter, + aNumsao,aVector, + aAngles[0], + aColor,aDash,aWidth,aFont,aText,aProps,aComment,taglist/*,cblist*/); + break; + } + aStatus = 0; + } + ; +%% + +static void setProps(unsigned short* props, unsigned short prop, int value) +{ + if (value) + *props |= prop; + else + *props &= ~prop; +} + +static CoordSystem checkWCSSystem() +{ + switch (localSystem) { + case IMAGE: + case PHYSICAL: + return WCS; + default: + return localSystem; + } +} + +static SkyFrame checkWCSSky() +{ + switch (localSystem) { + case IMAGE: + case PHYSICAL: + return FK5; + default: + return localSky; + } +} + +//pass error along to the driver (ParserDs9) +void yy::ds9parse::error( const yy::ds9parse::location_type &l, const std::string& m ){ + driver.error(l,m); +} + + diff --git a/carta/cpp/plugins/RegionDs9/plugin.json b/carta/cpp/plugins/RegionDs9/plugin.json new file mode 100644 index 00000000..e79a1251 --- /dev/null +++ b/carta/cpp/plugins/RegionDs9/plugin.json @@ -0,0 +1,12 @@ +{ + "api" : "1", + "name" : "RegionDs9", + "version" : "1", + "type" : "C++", + "description": [ + "Adds ability to load ds9 region files as ", + "instances of the CARTA RegionInfo class." + ], + "about" : "Parses region files based on the ds9 region format.", + "depends" : [ "casaCore-2.10.2016", "CasaImageLoader"] +} diff --git a/carta/cpp/plugins/plugins.pro b/carta/cpp/plugins/plugins.pro index c034efa5..1799383e 100644 --- a/carta/cpp/plugins/plugins.pro +++ b/carta/cpp/plugins/plugins.pro @@ -12,6 +12,7 @@ SUBDIRS += ConversionIntensity SUBDIRS += ImageAnalysis-2.10.2016 SUBDIRS += ImageStatistics SUBDIRS += RegionCASA +SUBDIRS += RegionDs9 SUBDIRS += ProfileCASA SUBDIRS += qimage diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js b/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js index 679129f9..adff8c94 100644 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js @@ -499,16 +499,30 @@ qx.Class.define("skel.boundWidgets.Animator", { this.m_lowBoundsSpinner.setToolTipText( "Set a lower bound for valid values."); this.m_slider = new qx.ui.form.Slider(); this.m_slider.addListener(skel.widgets.Path.CHANGE_VALUE, function( ev ) { - if (this.m_inUpdateState) { - return; - } - - if ( this.m_frame != this.m_slider.getValue()){ - this._sendFrame(this.m_slider.getValue()); + var sliderValue = this.m_slider.getValue(); + if ( this.m_frame != sliderValue){ + this._sendFrame( sliderValue ); } }, this); + + //Added because of issue #154. If you click somewhere in the slider + //it should move to that position. + this.m_slider.addListener( "click", function(ev){ + var box = this.m_slider.getContentLocation(); + var left = ev.getDocumentLeft() - box.left; + var width = this.m_slider.getBounds().width; + if ( width > 0 ){ + var min = this.m_slider.getMinimum(); + var max = this.m_slider.getMaximum(); + var newValue = min + left / width * ( max - min ); + if ( Math.abs( this.m_slider.getValue() - newValue ) > 1 ){ + this.m_slider.setValue( Math.round(newValue) ); + } + } + }, this ); this.m_highBoundsSpinner = new qx.ui.form.Spinner(0, 100, 100); + this.m_highBoundsSpinner.addListener("changeValue", this._highChanged, this); skel.widgets.TestID.addTestId( this.m_highBoundsSpinner, this.m_title+"UpperBoundSpin"); this.m_highBoundsSpinner.setToolTipText( "Set an upper bound for valid values"); var sliderComposite = new qx.ui.container.Composite(); @@ -945,7 +959,6 @@ qx.Class.define("skel.boundWidgets.Animator", { m_sharedVar : null, m_sharedVarSelection : null, m_identifier : null, - m_inUpdateState : false, m_available : true, m_frame : null, diff --git a/carta/html5/common/skel/source/class/skel/simulation/Util.py b/carta/html5/common/skel/source/class/skel/simulation/Util.py index 5b1838b7..b6aa80ad 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/Util.py +++ b/carta/html5/common/skel/source/class/skel/simulation/Util.py @@ -87,6 +87,13 @@ def get_window_count(unittest, driver): windowList = driver.find_elements_by_xpath("//div[@qxclass='qx.ui.window.Desktop']") windowCount = len( windowList ) return windowCount + +# Return the value of the text field with the given id. +def _getTextValue(self, driver, id): + textField = driver.find_element_by_id( id ) + driver.execute_script( "arguments[0].scrollIntoView(true);", textField) + textValue = textField.get_attribute("value") + return textValue # Determine whether the check box is checked @@ -325,5 +332,5 @@ def verifyAnimatorUpperBound(unittest, driver, expectedCount, animatorName): fullId = animatorName + "AnimatorUpperBound" animatorLabel = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, fullId ) ) ) upperBound = animatorLabel.text - print "Animator upper bound=", upperBound + print "Animator upper bound=", upperBound, " expected bound=", expectedCount unittest.assertEqual( upperBound, str(expectedCount), "Animator upper bound was not correct") diff --git a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py index 331a94b3..1262bb6d 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py @@ -140,6 +140,7 @@ def test_animatorJump(self): Util.load_image( self, driver, "aH.fits") Util.load_image( self, driver, "aJ.fits") Util.load_image( self, driver, "Default") + time.sleep( timeout ) # Record last channel value of the test image self._getLastValue( driver, "Channel" ) @@ -162,6 +163,7 @@ def test_animatorJump(self): # Click the channel tape deck increment button self._getNextValue( driver, "Channel" ) + time.sleep( timeout ) # Check that the channel is at the last channel value currChannelValue = self._getCurrentValue( driver, "Channel" ) @@ -196,6 +198,7 @@ def test_animatorJump(self): # Click the image increment button self._getNextValue( driver, "Image" ) + time.sleep( timeout ) # Check that the Animator is at the last image value currImageValue = self._getCurrentValue( driver, "Image" ) @@ -204,6 +207,7 @@ def test_animatorJump(self): # Click the image increment button again self._getNextValue( driver, "Image" ) + time.sleep( timeout ) currImageValue = self._getCurrentValue( driver, "Image" ) print "Current image", currImageValue self.assertEqual( int(firstImageValue), int(currImageValue), "Image Animator did not jump to first image") @@ -532,6 +536,7 @@ def test_animatorDecreaseFrame(self): Util.load_image( self, driver, "aH.fits") Util.load_image( self, driver, "aJ.fits") Util.load_image( self, driver, "Default") + time.sleep( timeout ) # Go to the last channel value and record the frame value self._getLastValue( driver, "Channel" ) diff --git a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py index 3c8ba9b5..d982e320 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py @@ -24,6 +24,8 @@ def setUp(self): # Test that the Channel Animator can stop the animation def test_channelAnimatorStopAnimation(self): driver = self.driver + timeout = selectBrowser._getSleep() + # Open a test image so we have something to animate Util.load_image( self, driver, "aH.fits") @@ -36,6 +38,7 @@ def test_channelAnimatorStopAnimation(self): # Click on the Stop button. Scroll into view if not visible self._stopAnimation( driver, "Channel") + time.sleep( timeout ) channelValue = self._getCurrentValue( driver, "Channel" ) print "Channel value ", channelValue # Wait for another 2 seconds. Ensure the channel value did not change @@ -50,6 +53,7 @@ def test_channelAnimatorStopAnimation(self): # Click on the Stop button. Scroll into view if not visible self._stopAnimation( driver, "Image") + time.sleep( timeout ) imageValue = self._getCurrentValue( driver, "Image" ) print "Image value ",imageValue @@ -61,6 +65,7 @@ def test_channelAnimatorStopAnimation(self): # Test that the Channel Animator can go to the first frame value of the test image def test_channelAnimatorFirstValue(self): driver = self.driver + timeout = selectBrowser._getSleep() # Open a test image so we have something to animate Util.load_image( self, driver, "aH.fits") @@ -77,6 +82,7 @@ def test_channelAnimatorFirstValue(self): self._animateForward( driver, "Channel" ) time.sleep(2) self._stopAnimation( driver, "Channel") + time.sleep(timeout) self._getFirstValue( driver, "Channel" ) currChannelValue = self._getCurrentValue( driver, "Channel" ) self.assertEqual( int(currChannelValue), int(firstChannelValue), "Channel Animator did not return to first channel value") @@ -89,6 +95,7 @@ def test_channelAnimatorFirstValue(self): self._animateForward( driver, "Image" ) time.sleep(2) self._stopAnimation( driver, "Image") + time.sleep( timeout ) # Click the first valid value button # Check that the image value is the same as the first image value @@ -100,9 +107,11 @@ def test_channelAnimatorFirstValue(self): # Test that the Channel Animator can go to the last frame value of the test image def test_channelAnimatorLastValue(self): driver = self.driver + timeout = selectBrowser._getSleep() # Open a test image so we have something to animate Util.load_image( self, driver, "Default") + time.sleep(timeout) # Record the last channel value of the test image self._getLastValue( driver, "Channel" ) @@ -113,6 +122,7 @@ def test_channelAnimatorLastValue(self): self._animateForward( driver, "Channel" ) time.sleep(2) self._stopAnimation( driver, "Channel") + time.sleep( timeout ) # Click the last valid value button # Check that the channel value is the same as the last channel value @@ -133,7 +143,8 @@ def test_channelAnimatorLastValue(self): self._animateForward( driver, "Image" ) time.sleep(2) self._stopAnimation( driver, "Image") - + time.sleep( timeout ) + # Click the first valid value button # Check that the image value is the same as the first image value self._getLastValue( driver, "Image" ) @@ -143,11 +154,14 @@ def test_channelAnimatorLastValue(self): # Test that the Channel Animator lower spin box cannot exceed boundary values def test_animatorBoundary(self): driver = self.driver + timeout = selectBrowser._getSleep() # Open a test image so we have something to animate Util.load_image( self, driver, "aH.fits") Util.load_image( self, driver, "aJ.fits") + Util.load_image( self, driver, "Default") + time.sleep( timeout ) # Find and record the last valid value of the animation self._getLastValue( driver, "Channel" ) @@ -212,9 +226,11 @@ def test_animatorBoundary(self): # Test that the Channel Animator upper and lower bound values do not change during animation def test_channelAnimatorBoundaryAnimation(self): driver = self.driver + timeout = selectBrowser._getSleep() # Open a test image so we have something to animate Util.load_image( self, driver, "Default") + time.sleep( timeout ) # Find and record the last valid value of the animation self._getLastValue( driver, "Channel" ) @@ -243,16 +259,21 @@ def test_channelAnimatorBoundaryAnimation(self): # Check that the lower and upper bound values did not change during animation lowerBound = lowerBoundText.get_attribute("value") upperBound = upperBoundText.get_attribute("value") + print "lowerBound=",lowerBound," lowerBoundValue=",lowerBoundValue self.assertEqual( int(lowerBound), int(lowerBoundValue), "Lower bound channel value changed during animation") + print "upperBound=", upperBound, " upperBoundValue=", upperBoundValue self.assertEqual( int(upperBound), int(upperBoundValue), "Upper bound channel value changed during animation") def test_imageAnimatorBoundaryAnimation(self): driver = self.driver + timeout = selectBrowser._getSleep() # Open a test image so we have something to animate Util.load_image( self, driver, "aH.fits") Util.load_image( self, driver, "aJ.fits") + Util.load_image( self, driver, "TWHydra_CO2_1line.image.fits") Util.load_image( self, driver, "Default") + time.sleep( timeout ) # Record the first image value self._getFirstValue( driver, "Image" ) diff --git a/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py b/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py index 6a670e01..23b04238 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py @@ -71,7 +71,7 @@ def test_globalIsGlobal(self): # Load 3 images # Select the bottom two in the stack - # Turn of global in the color map + # Turn off 'apply all' in the color map # Change the color map # Check that the first two have the changed color map and the third does not def test_notGlobalIsNotGlobal(self): @@ -98,8 +98,19 @@ def test_notGlobalIsNotGlobal(self): autoSelectCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "autoSelectImages"))) ActionChains(driver).click( autoSelectCheck ).perform() + # Find the colormap window. + colorWindow = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowColormap']"))) + ActionChains(driver).click(colorWindow).perform() + time.sleep( timeout ) + + #Open the colormap settings + Util.openSettings( self, driver, "Colormap", True ) + Util.clickTab( driver, "Color Map") + time.sleep( timeout ); + #Uncheck using a global color map globalCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "colorMapGlobal"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", globalCheck) ActionChains(driver).click( globalCheck ).perform() #Choose a new color map @@ -114,14 +125,17 @@ def test_notGlobalIsNotGlobal(self): #Animate through images. Make sure the first and second ones are using the old map #and the third is not self._nextImage( driver ) + time.sleep( timeout ) imageMapName = self._getColorMapName( driver ) print "Image 1 name=",imageMapName self.assertTrue( imageMapName == oldMapName, "Color map name 1 incorrect") self._nextImage( driver ) + time.sleep( timeout ) imageMapName = self._getColorMapName( driver ) print "Image 2 name=",imageMapName self.assertTrue( imageMapName == oldMapName, "Color map name 2 incorrect") self._nextImage( driver ) + time.sleep( timeout ) imageMapName = self._getColorMapName( driver ) print "Image 3 name=",imageMapName self.assertTrue( imageMapName == newMapName, "Color map name 3 incorrect") diff --git a/carta/html5/common/skel/source/class/skel/simulation/tHistogram.py b/carta/html5/common/skel/source/class/skel/simulation/tHistogram.py index 2e53ca03..62e5ea34 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tHistogram.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tHistogram.py @@ -19,11 +19,6 @@ def setUp(self): browser = selectBrowser._getBrowser() Util.setUp( self, browser ) - def _getTextValue(self, driver, id): - textField = driver.find_element_by_id(id) - textValue = textField.get_attribute("value") - return textValue - # Open histogram settings by clicking on the Histogram settings checkbox located on the menu bar def _openHistogramSettings(self, driver): histogramSettingsCheckbox = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.form.CheckBox']/div[text()='Histogram Settings']"))) @@ -151,9 +146,9 @@ def test_zoom(self): self._openHistogramSettings( driver ) # Look for the min and max zoom values and store their values. - minZoomValue = self._getTextValue( driver, "histogramZoomMinValue") + minZoomValue = Util._getTextValue( self, driver, "histogramZoomMinValue") print "Min zoom=", minZoomValue - maxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + maxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") print "Max zoom=", maxZoomValue # Find the min and max zoom percentages. Decrease their values. @@ -169,8 +164,8 @@ def test_zoom(self): time.sleep( timeout ) # Get the new min and max zoom values. - newMinZoomValue = self._getTextValue( driver, "histogramZoomMinValue") - newMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + newMinZoomValue = Util._getTextValue( self, driver, "histogramZoomMinValue") + newMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") # Check that the new min is larger than the old min print "oldMin=", minZoomValue," newMin=", newMinZoomValue @@ -201,7 +196,7 @@ def test_histogramAddImage(self): self._openHistogramSettings( driver ) # Get the max zoom value of the first image - maxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + maxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") print "First image maxZoomValue:", maxZoomValue # Load a different image in the same window @@ -209,7 +204,7 @@ def test_histogramAddImage(self): time.sleep( timeout ) # Check that the new max zoom value updates - newMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + newMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") self.assertNotEqual(float(newMaxZoomValue), float(maxZoomValue), "The histogram did not update when a new image was loaded.") print "Second image maxZoomValue:", newMaxZoomValue @@ -241,7 +236,7 @@ def test_histogramRemoveImage(self): self._openHistogramSettings( driver ) # Check that the histogram values are restored to default values - newMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + newMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") print "Zoom max value=", newMaxZoomValue self.assertEqual( float(newMaxZoomValue), 1, "Default values were not restored after image removal") @@ -256,6 +251,7 @@ def test_histogramChangeImage(self): # Load two images in the same image window Util.load_image( self, driver, "Default") Util.load_image( self, driver, "aH.fits") + time.sleep( timeout ) # Find and select the histogram histWindow = self._getHistogramWindow( driver ) @@ -266,7 +262,7 @@ def test_histogramChangeImage(self): time.sleep( timeout ) # Record the Histogram max zoom value of the second image - secondMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue" ) + secondMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue" ) print "Second image maxZoomValue:", secondMaxZoomValue # Find and click on the animation window @@ -284,7 +280,7 @@ def test_histogramChangeImage(self): time.sleep( timeout ) # Record the Histogram max zoom value of the first image - firstMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue" ) + firstMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue" ) print "First image maxZoomValue:", firstMaxZoomValue # Check that the Histogram updates its values @@ -315,7 +311,7 @@ def test_histogramLinking(self): self._openHistogramSettings( driver ) # Record the max zoom value of the first image - maxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue" ) + maxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue" ) print "First image maxZoomValue:", maxZoomValue time.sleep( timeout ) @@ -330,7 +326,7 @@ def test_histogramLinking(self): # Check that the second image is not linked to the Histogram # Check that the max zoom value did not change from the linking attempt to the second image - newMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + newMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") print "New maxZoomValue:", newMaxZoomValue self.assertEqual( float( maxZoomValue ), float( newMaxZoomValue), "Histogram should not link to second image.") @@ -364,7 +360,7 @@ def test_histogramLinkRemoval(self): self._openHistogramSettings( driver ) # Check that the histogram values are default values - newMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + newMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") self.assertEqual( float(newMaxZoomValue), 1, "Histogram is linked to image after link was removed") # Test that we can change the linked image to the Histogram @@ -401,7 +397,7 @@ def test_histogramChangeLinks(self): Util.link_second_image( self, driver, imageWindow2) # Check that the histogram values are not default values - newMaxZoomValue = self._getTextValue( driver, "histogramZoomMaxValue") + newMaxZoomValue = Util._getTextValue( self, driver, "histogramZoomMaxValue") self.assertNotEqual( float(newMaxZoomValue), 1, "Histogram did not update to newly linked image") def tearDown(self): diff --git a/carta/html5/common/skel/source/class/skel/simulation/tProfile.py b/carta/html5/common/skel/source/class/skel/simulation/tProfile.py new file mode 100644 index 00000000..16ad28b1 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/simulation/tProfile.py @@ -0,0 +1,289 @@ +import unittest +import Util +import time +import selectBrowser +from selenium import webdriver +from flaky import flaky +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +#Test drawing contours. +@flaky(max_runs=3) +class tProfile(unittest.TestCase): + def setUp(self): + browser = selectBrowser._getBrowser() + Util.setUp(self, browser) + + def _checkProfileCount(self, driver, expected): + Util.clickTab( driver, "Curves" ) + tableElement = driver.find_element_by_id('profileTable') + cells = tableElement.find_elements_by_xpath(".//div[@class='qooxdoo-table-cell']") + selectCount = len( cells ) + print "Profile curve count=", selectCount + self.assertEqual( float(selectCount), expected, "Incorrect number of profiles loaded.") + + # Show the profile window, which is at the moment hidden. + def _showProfile(self, driver ): + histWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowHistogram']"))) + ActionChains(driver).click( histWindow ).perform() + viewMenuButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.toolbar.MenuButton']/div[text()='View']/.."))) + ActionChains(driver).click( viewMenuButton ).send_keys(Keys.ARROW_DOWN).send_keys( + Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys( + Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() + + # Test that we can change from frequency to wavelength units, alter the rest + # frequency, and then reset it back to its original values. + def test_restFrequencyReset(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Load a default image + Util.load_image( self, driver, "Orion.methanol.cbc.contsub.image.fits") + + self._showProfile( driver ) + + #Open the profile settings to the Profiles tab. + profileWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowProfile']"))) + ActionChains(driver).click( profileWindow ).perform() + Util.openSettings( self, driver, "Profile", True ) + Util.clickTab( driver, "Profiles" ) + + #Set initial rest units to frequency in Hz and verify the + #rest frequency of the image is 229759000000. + frequencyUnitTypeRadio = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, 'profileFrequencyUnitType' ) ) ) + ActionChains(driver).click( frequencyUnitTypeRadio ).perform() + unitsCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, 'profileFrequencyUnits' ) ) ) + driver.execute_script( "arguments[0].scrollIntoView(true);", unitsCombo) + ActionChains(driver).click( unitsCombo).perform() + ActionChains(driver).send_keys(Keys.ENTER).perform() + restFrequencyText = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, 'profileRestFrequency' ) ) ) + restFrequency = restFrequencyText.get_attribute("value") + print "Rest frequency=",restFrequency + self.assertEqual( float(restFrequency), 229759000000, "Rest frequency was not correct") + + + #Change the units to wavelength + waveUnitTypeRadio = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, 'profileWaveUnitType' ) ) ) + ActionChains(driver).click( waveUnitTypeRadio ).perform() + + #Verify the rest frequency has been converted to wavelength + restFrequencyText = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, 'profileRestFrequency' ) ) ) + restFrequencyWave = str(restFrequencyText.get_attribute("value")) + print "Rest frequency wave=",restFrequencyWave + self.assertEqual( float(restFrequencyWave), 1.30481, "Rest frequency wave was not correct") + + #Change the rest frequency + Util._changeElementText(self, driver, restFrequencyText, '1.30482' ) + restFrequencyWave = str(restFrequencyText.get_attribute("value")) + print "Rest frequency wave2=", restFrequencyWave + self.assertEqual( float(restFrequencyWave), 1.30482, "Rest frequency wave was not correct") + + #Reset the rest frequency and verify it matches the original value. + resetButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, 'profileRestReset' ) ) ) + ActionChains(driver).click( resetButton).perform() + time.sleep( 1 ) + restFrequencyReset = str(restFrequencyText.get_attribute("value")) + print "Rest frequency reset=",restFrequencyReset + self.assertEqual( restFrequency, restFrequencyReset, "Rest frequency was not reset correctly") + + #Test that we can change the zoom percentage of the plot then the + #corresponding values will update. + def test_zoomValue(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + # Wait for the image window to be present (ensures browser is fully loaded) + imageWindow = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowImage']"))) + + # Load an image + Util.load_image(self, driver, "Orion.methanol.cbc.contsub.image.fits") + + self._showProfile( driver ) + + #Open the profile settings to the Range tab. + profileWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowProfile']"))) + ActionChains(driver).click( profileWindow ).perform() + Util.openSettings( self, driver, "Profile", True ) + Util.clickTab( driver, "Range" ) + + # Look for the min and max zoom values and check their values. + minZoomValue = Util._getTextValue( self, driver, 'profileZoomMinValue') + print "Min zoom=", minZoomValue + maxZoomValue = Util._getTextValue( self, driver, 'profileZoomMaxValue') + print "Max zoom=", maxZoomValue + self.assertEqual( float(minZoomValue), 0, "Min zoom value was not correct") + self.assertEqual( float(maxZoomValue), 40, "Max zoom value was not correct") + + # Find the min and max zoom percentages. Decrease their values. + minPercentText = driver.find_element_by_id("profileZoomMinPercent") + driver.execute_script( "arguments[0].scrollIntoView(true);", minPercentText) + minZoomPercent = minPercentText.get_attribute( "value") + maxPercentText = driver.find_element_by_id("profileZoomMaxPercent") + maxZoomPercent = maxPercentText.get_attribute( "value") + driver.execute_script( "arguments[0].scrollIntoView(true);", maxPercentText) + incrementAmount = 10; + newMinZoomPercent = Util._changeElementText(self, driver, minPercentText, str(float(minZoomPercent) + incrementAmount)) + newMaxZoomPercent = Util._changeElementText(self, driver, maxPercentText, str(float(maxZoomPercent) - incrementAmount)) + time.sleep( timeout ) + + # Get the new min and max zoom values. + newMinZoomValue = Util._getTextValue( self, driver, "profileZoomMinValue") + newMaxZoomValue = Util._getTextValue( self, driver, "profileZoomMaxValue") + print "New min zoom=",newMinZoomValue + print "New max zoom=",newMaxZoomValue + self.assertEqual( float(newMinZoomValue), 4, "Min zoom value was not correct") + self.assertEqual( float(newMaxZoomValue), 36, "Max zoom value was not correct") + + + #Test that we can change the zoom value of a plot and the + #corresponding zoom percentages will update. + def test_zoomPercentage(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + # Wait for the image window to be present (ensures browser is fully loaded) + imageWindow = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowImage']"))) + + # Load an image + Util.load_image(self, driver, "Orion.methanol.cbc.contsub.image.fits") + + # Show the profile view + self._showProfile( driver ) + + #Open the profile settings to the Range tab. + profileWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowProfile']"))) + ActionChains(driver).click( profileWindow ).perform() + Util.openSettings( self, driver, "Profile", True ) + Util.clickTab( driver, "Range" ) + + # Look for the min and max zoom percentages and check their values. + minZoomPercent = Util._getTextValue( self, driver, 'profileZoomMinPercent') + print "Min zoom percent=", minZoomPercent + maxZoomPercent = Util._getTextValue( self, driver, 'profileZoomMaxPercent') + print "Max zoom percent=", maxZoomPercent + self.assertEqual( float(minZoomPercent), 0, "Min zoom percent was not correct") + self.assertEqual( float(maxZoomPercent), 100, "Max zoom percent was not correct") + + # Find the min and max zoom values. Change their values. + minText = driver.find_element_by_id("profileZoomMinValue") + driver.execute_script( "arguments[0].scrollIntoView(true);", minText) + maxText = driver.find_element_by_id("profileZoomMaxValue") + driver.execute_script( "arguments[0].scrollIntoView(true);", maxText) + Util._changeElementText(self, driver, minText, '4') + Util._changeElementText(self, driver, maxText, '36') + time.sleep( timeout ) + + # Get the new min and max zoom values. + newMinZoomPercent = Util._getTextValue( self, driver, "profileZoomMinPercent") + newMaxZoomPercent= Util._getTextValue( self, driver, "profileZoomMaxPercent") + print "New min zoom=",newMinZoomPercent + print "New max zoom=",newMaxZoomPercent + self.assertEqual( float(newMinZoomPercent), 10, "Min zoom percent was not correct") + self.assertEqual( float(newMaxZoomPercent), 90, "Max zoom percent was not correct") + + #Load three images, one of which is a single plane image. + #Test that if the generate mode is current, only one profile will be seen. + #Test that if the generate mode is all, we see three images. + #Test that if the generate mode is all but single plane, we see two images. + def test_generateMode(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + # Wait for the image window to be present (ensures browser is fully loaded) + imageWindow = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowImage']"))) + + # Load three images + Util.load_image(self, driver, "Orion.methanol.cbc.contsub.image.fits") + time.sleep(1) + Util.load_image(self, driver, "TWHydra_CO2_1line.image.fits") + time.sleep(1) + Util.load_image(self, driver, "Orion.cont.image.fits") + time.sleep(1) + + # Show the profile view + self._showProfile( driver ) + + #Open the profile settings to the Profiles tab. + profileWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowProfile']"))) + ActionChains(driver).click( profileWindow ).perform() + Util.openSettings( self, driver, "Profile", True ) + Util.clickTab( driver, "Profiles" ) + + #Get the profiles combo and count how many are listed. Since + #the mode is current there should be just one. + self._checkProfileCount( driver, 1) + + #Change the generate mode to all + Util.clickTab( driver, "Profiles") + time.sleep(1) + genSelect = driver.find_element_by_id("profileGenerateMode") + driver.execute_script( "arguments[0].scrollIntoView(true);", genSelect ) + ActionChains(driver).click( genSelect ).send_keys( + Keys.ARROW_DOWN).send_keys( Keys.ENTER).perform() + + #There should now be three profiles loaded. + self._checkProfileCount( driver, 3 ) + + #Change the generate mode to all except single plane. + Util.clickTab( driver, "Profiles") + ActionChains(driver).click( genSelect ).send_keys(Keys.ARROW_DOWN).send_keys( + Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() + + #There should now be two profiles loaded. + self._checkProfileCount( driver, 2 ) + + + + #Test that we can remove a profile curve. + def test_removeProfile(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Load two profiles + Util.load_image(self, driver, "Orion.methanol.cbc.contsub.image.fits") + time.sleep(1) + Util.load_image(self, driver, "TWHydra_CO2_1line.image.fits") + time.sleep(1) + + # Show the profile view + self._showProfile( driver ) + + #Open the profile settings to the Profiles tab. + profileWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowProfile']"))) + ActionChains(driver).click( profileWindow ).perform() + Util.openSettings( self, driver, "Profile", True ) + Util.clickTab( driver, "Profiles" ) + + #Change the generate mode to all + Util.clickTab( driver, "Profiles") + time.sleep(1) + genSelect = driver.find_element_by_id("profileGenerateMode") + driver.execute_script( "arguments[0].scrollIntoView(true);", genSelect ) + ActionChains(driver).click( genSelect ).send_keys( + Keys.ARROW_DOWN).send_keys( Keys.ENTER).perform() + + #There should now be two profiles loaded. + self._checkProfileCount( driver, 2 ) + + #Hit the delete button + Util.clickTab( driver, "Profiles") + deleteButton = driver.find_element_by_id("profileRemoveButton") + driver.execute_script( "arguments[0].scrollIntoView(true);", deleteButton ) + ActionChains(driver).click( deleteButton ).perform() + + #There should now be just one profile loaded + self._checkProfileCount( driver, 1 ) + + def tearDown(self): + #Close the browser + self.driver.close() + #Allow browser to fully close before continuing + time.sleep(2) + #Close the session and delete temporary files + self.driver.quit() + +if __name__ == "__main__": + unittest.main() diff --git a/carta/html5/common/skel/source/class/skel/simulation/tSnapshot.py b/carta/html5/common/skel/source/class/skel/simulation/tSnapshot.py index 3ad84f74..d4145f30 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tSnapshot.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tSnapshot.py @@ -28,7 +28,8 @@ def _setChecked(self, driver, checkBox, checked): # Click the Restore... option in the Sessions submenu def _clickSessionRestoreButton(self,driver): - restoreButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[text()='Manage/Restore...']/.."))) + sessionButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[text()='Session']/.."))) + ActionChains( driver ).click( sessionButton ).perform(); ActionChains(driver).send_keys( Keys.ARROW_DOWN).send_keys( Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() # Click the Save... option in the Sessions submenu diff --git a/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py b/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py index c8403e7d..fda566c1 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py @@ -19,6 +19,12 @@ class tSnapshotData(tSnapshot.tSnapshot): def setUp(self): browser = selectBrowser._getBrowser() Util.setUp(self, browser) + + def open_restore(self, driver): + sessionButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[text()='Session']/.."))) + ActionChains( driver ).click( sessionButton ).perform(); + ActionChains(driver).send_keys( Keys.ARROW_DOWN).send_keys( Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() + def _verifyImage(self, driver, count ): #Get the upper bound of images from the image animator @@ -83,8 +89,7 @@ def test_animator_channel(self): Util._changeElementText( self, driver, indexText, 0) # Click the restore sessions button - self._clickSessionButton( driver ) - self._clickSessionRestoreButton( driver ) + self.open_restore( driver ) # Select tSnapshotData in the restore combo box self._selectRestoreSnapshot( driver, "tSnapshotData") @@ -98,7 +103,7 @@ def test_animator_channel(self): # Verify the animator channel is back to the last one channelVal = indexText.get_attribute( "value") - print "Channel value=",channelVal + print "Channel value=",channelVal, " lastChannel=", lastChannel self.assertEqual( channelVal, lastChannel, "Channel animator did not get restored to last channel") @@ -156,8 +161,7 @@ def test_image_load(self): self._verifyImage( driver, 2) # Click the restore sessions button - self._clickSessionButton( driver ) - self._clickSessionRestoreButton( driver ) + self.open_restore( driver ) # Select tSnapshotData in the restore combo box self._selectRestoreSnapshot( driver, "tSnapshotData") @@ -171,6 +175,8 @@ def test_image_load(self): # Verify that only the original two images are loaded self._verifyImage( driver, 1 ) + + # Test that a data snapshot can be taken with 3 images loaded and that they # will all be restored if the snapshot is reloaded. @@ -221,8 +227,7 @@ def test_multipleImagesRestored(self): self._verifyImage( driver, 0 ) # Click the restore sessions button - self._clickSessionButton( driver ) - self._clickSessionRestoreButton( driver ) + self.open_restore( driver ) # Select tSnapshotData in the restore combo box self._selectRestoreSnapshot( driver, "tSnapshotData") @@ -295,8 +300,7 @@ def test_image_remove(self): print "Test passed - all images are gone" # Click the restore sessions button - self._clickSessionButton( driver ) - self._clickSessionRestoreButton( driver ) + self.open_restore( driver ) # Select tSnapshotData in the restore combo box self._selectRestoreSnapshot( driver, "tSnapshotData") diff --git a/carta/html5/common/skel/source/class/skel/simulation/tStatistics.py b/carta/html5/common/skel/source/class/skel/simulation/tStatistics.py index 42c00ffe..c441a5d2 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tStatistics.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tStatistics.py @@ -105,6 +105,9 @@ def test_show_hide_stats(self): # Load a specific image. imageWindow = Util.load_image(self, driver, "Orion.methanol.cbc.contsub.image.fits") + # Load a region file. + Util.load_image( self, driver, "region.crtf") + # Replace the histogram window with the statistics window. self.show_statistics_window(driver) time.sleep( timeout ) @@ -112,9 +115,6 @@ def test_show_hide_stats(self): #Check that we see image stats. imageStatsNameCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,"ImageStatsCombo"))) - # Load a region file. - Util.load_image( self, driver, "region.crtf") - #Check that we see region stats. regionStatsNameCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,"RegionStatsCombo"))) ActionChains(driver).click( regionStatsNameCombo ).perform() diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorGradient.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorGradient.js index f75a54b2..ccb6955e 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorGradient.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorGradient.js @@ -1,10 +1,7 @@ /** * Displays a color map as a gradient. */ -/*global mImport */ -/******************************************************************************* - * @ignore( mImport) - ******************************************************************************/ + qx.Class.define("skel.widgets.Colormap.ColorGradient", { extend: qx.ui.embed.Canvas, @@ -28,11 +25,9 @@ qx.Class.define("skel.widgets.Colormap.ColorGradient", { */ _addStop : function( gradient, stopIndex, posIndex ){ var floatStop = posIndex / this.m_stops.length; - var redHex = this._processColor( this.m_stops[stopIndex], 1, this.m_scaleRed ); - var greenHex = this._processColor( this.m_stops[stopIndex], 3, this.m_scaleGreen ); - var blueHex = this._processColor( this.m_stops[stopIndex], 5, this.m_scaleBlue ); - var stop = "#"+redHex+greenHex+blueHex; - gradient.addColorStop( floatStop, stop ); + if ( this.m_stops[stopIndex].indexOf("#") >= 0 ){ + gradient.addColorStop( floatStop, this.m_stops[stopIndex] ); + } }, /** @@ -81,47 +76,8 @@ qx.Class.define("skel.widgets.Colormap.ColorGradient", { this.setAllowGrowY( true ); this.setMinWidth( 100 ); this.setMinHeight( 25 ); - this.m_connector = mImport("connector"); }, - /** - * Reads a color from the hex representation and applies inversion and scaling, - * if appropriate. - * @param stop {String} the hex representation of a color. - * @param hexIndex {Number} the index of the color in the hex representation. - * @param scale {Number} a scaling factor for the color. - */ - _processColor : function ( stop, hexIndex, scale ){ - var start = hexIndex; - var end = hexIndex + 2; - if ( stop.substring(0,1) === "0"){ - start = 1; - } - var colorHex = stop.substring( start, end ); - var rgbColor = parseInt( colorHex, 16 ); - if ( this.m_invert ){ - rgbColor = 255 - rgbColor; - } - rgbColor = Math.round(rgbColor * scale); - var processedHex = rgbColor.toString( 16 ); - if ( processedHex.length == 1 ){ - processedHex = "0"+processedHex; - } - return processedHex; - }, - - /** - * Requests information from the server about the colors comprising the - * current color gradient. - */ - _sendUpdateStopsCmd : function(){ - if ( this.m_connector !== null && this.m_name !== null ){ - var params = "name:"+this.m_name; - var path = skel.widgets.Path.getInstance(); - var cmd = path.COLORMAPS + path.SEP_COMMAND + "getColorStops"; - this.m_connector.sendCommand( cmd, params, this._updateColorStops(this)); - } - }, /** * Sets the name of the color map to display in the gradient. @@ -130,11 +86,9 @@ qx.Class.define("skel.widgets.Colormap.ColorGradient", { setColorName : function( colorMapName ){ if ( this.m_name !== colorMapName ){ this.m_name = colorMapName; - this._sendUpdateStopsCmd(); } }, - /** * Set whether or not to invert the color map. * @param invertMap {boolean} true to invert the map; false, otherwise. @@ -157,46 +111,19 @@ qx.Class.define("skel.widgets.Colormap.ColorGradient", { } }, - /** - * Set scale values for the colors in the map. - * @param redValue {Number} the amount of red scaling, range [0,1]. - * @param greenValue {Number} the amount of green scaling, range [0,1]. - * @param blueValue {Number} the amount of blue scaling, range [0,1]. - */ - setScales : function( redValue, greenValue, blueValue ){ - var scalesChanged = false; - if ( this.m_scaleRed != redValue ){ - this.m_scaleRed = redValue; - scalesChanged = true; - } - if ( this.m_scaleGreen != greenValue ){ - this.m_scaleGreen = greenValue; - scalesChanged = true; - } - if ( this.m_scaleBlue != blueValue ){ - this.m_scaleBlue = blueValue; - scalesChanged = true; - } - if ( scalesChanged ){ - this.update(); - } - }, /** * Returns a callback for updating the display with new color stops. * @param anObject {skel.widgets.ColorMap.ColorGradient}. * @return {Function} the callback for updating the gradient color stops. */ - _updateColorStops : function( anObject ){ - return function( colorStops ){ - if ( colorStops !==null && colorStops != anObject.m_stops ){ - anObject.m_stops = colorStops.split(","); - anObject.update(); - } - }; + setStops : function( colorStops ){ + if ( colorStops !==null ){ + this.m_stops = colorStops.split(","); + this.update(); + } }, - m_connector : null, m_scaleRed : 1, m_scaleBlue : 1, m_scaleGreen : 1, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js index ebef8b37..d981e577 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js @@ -18,10 +18,23 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { //Initiate the shared variable containing a list of all available color maps. this.m_connector = mImport("connector"); this._init( ); + + //Initialize the shared variable that manages the list of color maps. var pathDict = skel.widgets.Path.getInstance(); this.m_sharedVarMaps = this.m_connector.getSharedVar(pathDict.COLORMAPS); this.m_sharedVarMaps.addCB(this._mapsChangedCB.bind(this)); this._mapsChangedCB(); + + //Initialize the shared variable that manages the list of data transforms + var pathDict = skel.widgets.Path.getInstance(); + this.m_sharedVarTransform = this.m_connector.getSharedVar(pathDict.TRANSFORMS_DATA); + this.m_sharedVarTransform.addCB(this._transformChangedCB.bind(this)); + this._transformChangedCB(); + + //Initialize the shared variable that manages the units. + this.m_sharedVarUnits = this.m_connector.getSharedVar(pathDict.INTENSITY_UNITS); + this.m_sharedVarUnits.addCB(this._unitsChangedCB.bind(this)); + this._unitsChangedCB(); }, statics : { @@ -46,6 +59,7 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { } }, + /** * Callback for a server error when setting the map index. * @param anObject {skel.widgets.ColorMap.ColorScale}. @@ -62,11 +76,15 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { * Initializes the UI. */ _init : function( ) { - var widgetLayout = new qx.ui.layout.HBox(); + var widgetLayout = new qx.ui.layout.HBox(2); this._setLayout(widgetLayout); - this.m_intensityLowLabel = new qx.ui.basic.Label( ""); - this._add( this.m_intensityLowLabel ); + this.m_intensityLowText = new skel.widgets.CustomUI.NumericTextField( null, null); + this.m_intensityLowListenId = this.m_intensityLowText.addListener( "textChanged", + this._intensityChanged, this ); + this.m_intensityLowText.setToolTipText( "Set the lower intensity bound."); + this.m_intensityLowText.setIntegerOnly( false ); + this._add( this.m_intensityLowText ); this._add( new qx.ui.core.Spacer(), {flex:1}); this.m_mapCombo = new skel.widgets.CustomUI.SelectBox( skel.widgets.Colormap.ColorMapsWidget.CMD_SET_MAP, "name"); @@ -74,23 +92,47 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { this.m_mapCombo.setToolTipText( "Select a color map."); this._add( this.m_mapCombo ); - this.m_globalCheck = new qx.ui.form.CheckBox( "Global"); - skel.widgets.TestID.addTestId( this.m_globalCheck, "colorMapGlobal"); - this.m_globalCheck.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ + this.m_dataCombo = new qx.ui.form.ComboBox(); + this.m_dataCombo.setToolTipText( "Select a data transformation."); + this.m_dataCombo.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ if ( this.m_id !== null ){ - var global = this.m_globalCheck.getValue(); + var transformName = e.getData(); //Send a command to the server to let them know the map changed. var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + "setGlobal"; - var params = "global:"+global; + var cmd = this.m_id + path.SEP_COMMAND + "setDataTransform"; + var params = "dataTransform:"+transformName; this.m_connector.sendCommand( cmd, params, function(){}); } },this); - this._add( this.m_globalCheck ); + this._add( this.m_dataCombo ); + + this.m_imageUnitsCombo = new skel.widgets.CustomUI.SelectBox( "setImageUnits", "imageUnits"); + this.m_imageUnitsCombo.setToolTipText( "Select units for the intensity bounds."); + this._add( this.m_imageUnitsCombo ); this._add( new qx.ui.core.Spacer(), {flex:1}); - this.m_intensityHighLabel = new qx.ui.basic.Label(""); - this._add( this.m_intensityHighLabel ); + this.m_intensityHighText = new skel.widgets.CustomUI.NumericTextField(null, null); + this.m_intensityHighText.setToolTipText( "Set the upper intensity bound."); + this.m_intensityHighListenId = this.m_intensityHighText.addListener( "textChanged", + this._intensityChanged, this ); + this.m_intensityHighText.setIntegerOnly( false ); + this._add( this.m_intensityHighText ); + }, + + /** + * The user has changed either the lower or upper colormap intensity + * bound. + */ + _intensityChanged : function(){ + if ( this.m_id !== null ){ + var minInt = this.m_intensityLowText.getValue(); + var maxInt = this.m_intensityHighText.getValue(); + + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setIntensityRange"; + var params = "intensityMin:"+minInt+",intensityMax:"+maxInt; + this.m_connector.sendCommand( cmd, params, null); + } }, /** @@ -124,10 +166,39 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { * @param maxValue {Number} - the maximum intensity for the color map. */ _setIntensityRange : function( minValue, maxValue ){ - this.m_intensityLowLabel.setValue( minValue.toString() ); - this.m_intensityHighLabel.setValue( maxValue.toString() ); + var oldMin = this.m_intensityLowText.getValue(); + if ( oldMin != minValue ){ + if ( this.m_intensityLowListenId !== null ){ + this.m_intensityLowText.removeListenerById( this.m_intensityLowListenId ); + } + this.m_intensityLowText.setValue( minValue.toString() ); + this.m_intensityLowListenId = this.m_intensityLowText.addListener( "textChanged", + this._intensityChanged, this ); + } + var oldMax = this.m_intensityHighText.getValue(); + if ( oldMax != maxValue ){ + if ( this.m_intensityHighListenId !== null ){ + this.m_intensityHighText.removeListenerById( this.m_intensityHighListenId ); + } + this.m_intensityHighText.setValue( maxValue.toString() ); + this.m_intensityHighListenId = this.m_intensityHighText.addListener( "textChanged", + this._intensityChanged, this ); + } }, + /** + * Set the type of data transform based on server side values. + * @param transform {String} - the data transform. + */ + setDataTransform : function( transform ){ + var dSelectables = this.m_dataCombo.getChildrenContainer().getSelectables(true); + for ( var i = 0; i < dSelectables.length; i++ ){ + if ( dSelectables[i].getLabel() == transform ){ + this.m_dataCombo.setValue( dSelectables[i].getLabel() ); + break; + } + } + }, /** * Set the selected color map. @@ -144,6 +215,8 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { setId : function( id ){ this.m_id = id; this.m_mapCombo.setId( id ); + this.m_imageUnitsCombo.setId( id ); + var path = skel.widgets.Path.getInstance(); var dataPath = this.m_id + path.SEP + path.DATA; this.m_sharedVarData = this.m_connector.getSharedVar( dataPath ); @@ -152,23 +225,73 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { }, /** - * Server update as to whether this is a global colormap. - * @param globalMap {boolean} - true if this is a global color map; - * false otherwise. + * The list of available data transforms has changed on the server. */ - setGlobal : function( globalMap ){ - this.m_globalCheck.setValue( globalMap ); + _transformChangedCB : function(){ + if ( this.m_sharedVarTransform ){ + var val = this.m_sharedVarTransform.get(); + if ( val ){ + try { + var oldData = this.m_dataCombo.getValue(); + var transforms = JSON.parse( val ); + var transformCount = transforms.dataTransforms.length; + this.m_dataCombo.removeAll(); + for ( var i = 0; i < transformCount; i++ ){ + var transformName = transforms.dataTransforms[i]; + var tempItem = new qx.ui.form.ListItem( transformName ); + this.m_dataCombo.add( tempItem ); + } + //Try to reset the old selection + if ( oldData !== null ){ + this.m_dataCombo.setValue( oldData ); + } + //Select the first item + else if ( transformCount > 0 ){ + var selectables = this.m_dataCombo.getChildrenContainer().getSelectables(true); + if ( selectables.length > 0 ){ + this.m_dataCombo.setValue( selectables[0].getLabel()); + } + } + } + catch( err ){ + console.log( "Could not parse data transforms: "+val ); + } + } + } + }, + + /** + * The list of available image units has changed on the server. + */ + _unitsChangedCB : function(){ + if ( this.m_sharedVarUnits ){ + var val = this.m_sharedVarUnits.get(); + if ( val ){ + try { + var imageUnits = JSON.parse( val ); + this.m_imageUnitsCombo.setSelectItems(imageUnits.units); + } + catch( err ){ + console.log( "Could not parse image units: "+val ); + } + } + } }, - m_intensityLowLabel : null, - m_intensityHighLabel : null, + m_intensityLowText : null, + m_intensityHighText : null, + m_intensityLowListenId : null, + m_intensityHighListenId : null, m_mapCombo : null, - m_globalCheck : null, + m_dataCombo : null, + m_imageUnitsCombo : null, m_id : null, m_connector : null, m_sharedVarData : null, - m_sharedVarMaps : null + m_sharedVarMaps : null, + m_sharedVarTransform : null, + m_sharedVarUnits : null } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js index d95603ac..a4ed4789 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js @@ -194,7 +194,6 @@ qx.Class.define("skel.widgets.Colormap.ColorMix", { * @param bluePercent {Number} a decimal in [0,1] indicating the blue percentage. */ setMix : function( redPercent, greenPercent, bluePercent ){ - this.m_colorCanvas.setScales( redPercent, greenPercent, bluePercent ); this.m_redSlider.removeListenerById( this.m_redSliderListenerId ); this.m_greenSlider.removeListenerById( this.m_greenSliderListenerId ); this.m_blueSlider.removeListenerById( this.m_blueSliderListenerId ); @@ -216,19 +215,11 @@ qx.Class.define("skel.widgets.Colormap.ColorMix", { }, /** - * Invert the colors in the mix. - * @param invert {boolean} - invert the colors in the mix. + * Set specific color values along the map. + * @param stops {String} - list of hex color values. */ - setInvert : function( invert ){ - this.m_colorCanvas.setInvert( invert ); - }, - - /** - * Reverse the colors in the mix. - * @param reverse {boolean} - reverse the colors in the mix. - */ - setReverse : function( reverse ){ - this.m_colorCanvas.setReverse( reverse ); + setStops : function( stops ){ + this.m_colorCanvas.setStops( stops ); }, /** diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMixCanvas.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMixCanvas.js index 033bc778..9d0dfff4 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMixCanvas.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMixCanvas.js @@ -1,10 +1,6 @@ /** * Displays lines showing the amount of red, blue and green in the color map. */ -/*global mImport */ -/******************************************************************************* - * @ignore( mImport) - ******************************************************************************/ qx.Class.define("skel.widgets.Colormap.ColorMixCanvas", { extend: qx.ui.embed.Canvas, @@ -44,9 +40,8 @@ qx.Class.define("skel.widgets.Colormap.ColorMixCanvas", { * @param ctx {CanvasRenderingContext2D} The rendering ctx to draw to. * @param colorStr {String} a string naming a color. * @param hexIndex {Number} the location in the hex representation where the color can be found. - * @param scaleFactor {Number} a multiplier to adjust the color strength. */ - _drawColor : function( width, height, ctx, colorStr, hexIndex, scaleFactor ){ + _drawColor : function( width, height, ctx, colorStr, hexIndex ){ if ( this.m_stops !== null ){ ctx.beginPath(); var drawHeight = height; @@ -54,16 +49,10 @@ qx.Class.define("skel.widgets.Colormap.ColorMixCanvas", { for ( var i = 0; i < this.m_stops.length; i++ ){ var colorIndex = i; - if ( this.m_reverse ){ - colorIndex = this.m_stops.length - i - 1; - } var hexColor = this.m_stops[colorIndex].substring( hexIndex, hexIndex + 2); var intColor = parseInt( hexColor, 16 ); - if ( this.m_invert ){ - intColor = 255 - intColor; - } var percentColor = intColor / 255; - var lineY = drawHeight - drawHeight * percentColor * scaleFactor; + var lineY = drawHeight - drawHeight * percentColor; var lineX = i / this.m_stops.length * width; ctx.lineTo( lineX, lineY ); } @@ -80,9 +69,9 @@ qx.Class.define("skel.widgets.Colormap.ColorMixCanvas", { * @param ctx {CanvasRenderingContext2D} The rendering ctx to draw to. */ _drawColorGraph : function( width, height, ctx ){ - this._drawColor( width, height, ctx, "red", 1, this.m_scaleRed); - this._drawColor( width, height, ctx, "green", 3, this.m_scaleGreen ); - this._drawColor( width, height, ctx, "blue", 5, this.m_scaleBlue ); + this._drawColor( width, height, ctx, "red", 1 ); + this._drawColor( width, height, ctx, "green", 3 ); + this._drawColor( width, height, ctx, "blue", 5 ); }, /** @@ -93,23 +82,9 @@ qx.Class.define("skel.widgets.Colormap.ColorMixCanvas", { this.setAllowGrowY( true ); this.setMinWidth( 100 ); this.setHeight( 25 ); - this.m_connector = mImport("connector"); }, - /** - * Requests information from the server about the colors comprising the - * current color gradient. - */ - _sendUpdateStopsCmd : function(){ - if ( this.m_connector !== null && this.m_name !== null ){ - var params = "name:"+this.m_name; - var path = skel.widgets.Path.getInstance(); - var cmd = path.COLORMAPS + path.SEP_COMMAND + "getColorStops"; - this.m_connector.sendCommand( cmd, params, this._updateColorStops(this)); - } - }, - /** * Sets the name of the color map to display in the gradient. * @param colorMapName {String} the name of a colormap. @@ -117,79 +92,23 @@ qx.Class.define("skel.widgets.Colormap.ColorMixCanvas", { setColorName : function( colorMapName ){ if ( this.m_name !== colorMapName ){ this.m_name = colorMapName; - this._sendUpdateStopsCmd(); } }, - /** - * Set whether or not to invert the color map. - * @param invertMap {boolean} true to invert the map; false, otherwise. - */ - setInvert : function( invertMap ){ - if ( invertMap != this.m_invert ){ - this.m_invert = invertMap; - this.update(); - } - }, - - /** - * Set whether or not to reverse the color map. - * @param reverseMap {boolean} true to reverse the colors in the map; false, otherwise. - */ - setReverse : function( reverseMap ){ - if ( reverseMap != this.m_reverse ){ - this.m_reverse = reverseMap; - this.update(); - } - }, - - /** - * Set scale values for the colors in the map. - * @param redValue {Number} the amount of red scaling, range [0,1]. - * @param greenValue {Number} the amount of green scaling, range [0,1]. - * @param blueValue {Number} the amount of blue scaling, range [0,1]. - */ - setScales : function( redValue, greenValue, blueValue ){ - var scalesChanged = false; - if ( this.m_scaleRed != redValue ){ - this.m_scaleRed = redValue; - scalesChanged = true; - } - if ( this.m_scaleGreen != greenValue ){ - this.m_scaleGreen = greenValue; - scalesChanged = true; - } - if ( this.m_scaleBlue != blueValue ){ - this.m_scaleBlue = blueValue; - scalesChanged = true; - } - if ( scalesChanged ){ - this.update(); - } - }, - /** * Returns a callback for updating the display with new color stops. * @param anObject {skel.widgets.ColorMap.ColorGradient}. * @return {Function} the callback for updating the gradient color stops. */ - _updateColorStops : function( anObject ){ - return function( colorStops ){ - if ( colorStops !==null && colorStops != anObject.m_stops ){ - anObject.m_stops = colorStops.split(","); - anObject.update(); - } - }; + setStops : function( colorStops ){ + if ( colorStops !==null ){ + this.m_stops = colorStops.split(","); + this.update(); + } }, - - m_connector : null, - m_scaleRed : 1, - m_scaleBlue : 1, - m_scaleGreen : 1, + m_name : null, - m_invert : null, - m_reverse : null, m_stops : null } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorModel.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorModel.js index c9d8d7da..bfa138c6 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorModel.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorModel.js @@ -20,39 +20,69 @@ qx.Class.define("skel.widgets.Colormap.ColorModel", { members : { - /** * Initializes the UI. */ _init : function( ) { - this.setAllowGrowX( true ); - this.setAllowGrowY( true ); - var widgetLayout = new qx.ui.layout.VBox(2); + var widgetLayout = new qx.ui.layout.HBox(1); this._setLayout(widgetLayout); - this.m_gammaContainer = new qx.ui.groupbox.GroupBox( ""); - this.m_gammaContainer.setContentPadding( 0, 0, 0, 0); + this.m_gammaContainer = new qx.ui.container.Composite(); this.m_gammaContainer.setLayout( new qx.ui.layout.VBox(2)); + + var gammaTextContainer = new qx.ui.container.Composite(); + gammaTextContainer.setLayout( new qx.ui.layout.HBox(2) ); + var gammaLabel = new qx.ui.basic.Label( "Gamma:"); + this.m_gammaText = new skel.widgets.CustomUI.NumericTextField( 0, null ); + this.m_gammaListenId = this.m_gammaText.addListener( "textChanged", + this._sendGammaCmd, this ); + this.m_gammaText.setIntegerOnly( false ); + this.m_gammaText.setToolTipText( "Set gamma value."); + gammaTextContainer.add( new qx.ui.core.Spacer(2), {flex:1}); + gammaTextContainer.add( gammaLabel ); + gammaTextContainer.add( this.m_gammaText ); + gammaTextContainer.add( new qx.ui.core.Spacer(2), {flex:1}); + this.m_gammaContainer.add( gammaTextContainer ); + + this.m_gammaGrid = new skel.widgets.Colormap.TwoDSlider(); - this.m_gammaGrid.addListener( skel.widgets.Path.CHANGE_VALUE, function(evt){ + this.m_gridListenId = this.m_gammaGrid.addListener( skel.widgets.Path.CHANGE_VALUE, function(evt){ var data = evt.getData(); this._sendScaledChangedCmd( data.x, data.y ); }, this ); this.m_gammaContainer.add( this.m_gammaGrid, {flex:1}); + this._add( this.m_gammaContainer, {flex:1} ); }, + /** + * Notify the server that gamma has changed. + */ + _sendGammaCmd : function(){ + if ( this.m_id !== null ){ + var path = skel.widgets.Path.getInstance(); + var value = this.m_gammaText.getValue(); + if ( value >= 0 ){ + var cmd = this.m_id + path.SEP_COMMAND + "setGamma"; + var params = "gamma:"+value; + this.m_connector.sendCommand( cmd, params, function(){}); + } + } + }, + /** * Notify the server that the scales have changed. * @param scale1 {Number} the first gamma scale. * @param scale2 {Number} the second gamma scale. */ _sendScaledChangedCmd : function( scale1, scale2 ){ - var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + "setScales"; - var params = "scale1:"+scale1+",scale2:"+scale2; - this.m_connector.sendCommand( cmd, params, function(){}); + if ( this.m_id !== null ){ + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setScales"; + var params = "scale1:"+scale1+",scale2:"+scale2; + this.m_connector.sendCommand( cmd, params, function(){}); + } }, /** @@ -60,7 +90,28 @@ qx.Class.define("skel.widgets.Colormap.ColorModel", { * @param value {Number} the gamma value. */ setGamma : function( value ){ - this.m_gammaContainer.setLegend( "Gamma: "+value ); + if ( this.m_gammaListenId !== null ){ + this.m_gammaText.removeListenerById( this.m_gammaListenId ); + } + this.m_gammaText.setValue( value ); + this.m_gammaListenId = this.m_gammaText.addListener( "textChanged", + this._sendGammaCmd, this ); + }, + + /** + * Set the (x,y) grid coordinates based on server values. + * @param x {Number} - the x-location on the grid. + * @param y {Number} - the y-location on the grid. + */ + setGammaPosition : function( x, y ){ + if ( this.m_gridListenId !== null ){ + this.m_gammaGrid.removeListenerById( this.m_gridListenId ); + } + this.m_gammaGrid.setValue( x, y ); + this.m_gridListenId = this.m_gammaGrid.addListener( skel.widgets.Path.CHANGE_VALUE, function(evt){ + var data = evt.getData(); + this._sendScaledChangedCmd( data.x, data.y ); + }, this ); }, /** @@ -74,6 +125,16 @@ qx.Class.define("skel.widgets.Colormap.ColorModel", { m_id : null, m_connector : null, m_gammaGrid : null, + m_gammaText : null, + m_gammaListenId : null, + m_gridListenId : null, m_gammaContainer : null + }, + + properties : { + appearance : { + refine : true, + init : "internal-area" + } } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js index b6acab3b..1a2accf9 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js @@ -52,17 +52,48 @@ qx.Class.define("skel.widgets.Colormap.ColorScale", { }, + /** + * Whether or not color map changes should be applied to all color maps + * has changed in the UI. + */ + _globalChanged : function(){ + if ( this.m_id !== null ){ + var global = this.m_globalCheck.getValue(); + //Send a command to the server to let them know the map changed. + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setGlobal"; + var params = "global:"+global; + this.m_connector.sendCommand( cmd, params, function(){}); + } + }, + + /** * Initializes the UI. */ _init : function( ) { - var widgetLayout = new qx.ui.layout.HBox(); + var widgetLayout = new qx.ui.layout.VBox(); this._setLayout(widgetLayout); - this._add( new qx.ui.core.Spacer(), {flex:1}); this._initInvertReverse(); - this._add( new qx.ui.core.Spacer(), {flex:1}); - + this._initApplyAll(); + }, + + + /** + * Initialize the 'Apply All' functionality. + */ + _initApplyAll : function(){ + this.m_globalCheck = new qx.ui.form.CheckBox( "Apply All"); + this.m_globalCheck.setToolTipText( "Apply color map settings to all images in the stack."); + skel.widgets.TestID.addTestId( this.m_globalCheck, "colorMapGlobal"); + this.m_globalListenId = this.m_globalCheck.addListener( skel.widgets.Path.CHANGE_VALUE, this._globalChanged, this ); + var allComposite = new qx.ui.container.Composite(); + allComposite.setLayout(new qx.ui.layout.HBox(1)); + allComposite.add( new qx.ui.core.Spacer(1), {flex:1}); + allComposite.add( this.m_globalCheck ); + allComposite.add( new qx.ui.core.Spacer(1), {flex:1}); + this._add( allComposite ); }, /** @@ -89,13 +120,29 @@ qx.Class.define("skel.widgets.Colormap.ColorScale", { }, this ); var mapComposite = new qx.ui.container.Composite(); - mapComposite.setLayout(new qx.ui.layout.VBox(1)); - + mapComposite.setLayout(new qx.ui.layout.HBox(1)); + mapComposite.add( new qx.ui.core.Spacer(1), {flex:1}); mapComposite.add( this.m_reverseCheck ); mapComposite.add( this.m_invertCheck ); + mapComposite.add( new qx.ui.core.Spacer(1), {flex:1}); this._add( mapComposite ); }, + /** + * Server update as to whether this is a global colormap. + * @param globalMap {boolean} - true if this is a global color map; + * false otherwise. + */ + setGlobal : function( globalMap ){ + if ( this.m_globalCheck.getValue() != globalMap ){ + if ( this.m_globalListenId != null ){ + this.m_globalCheck.removeListenerById( this.m_globalListenId ); + } + this.m_globalCheck.setValue( globalMap ); + this.m_globalListenId = this.m_globalCheck.addListener( skel.widgets.Path.CHANGE_VALUE, this._globalChanged, this ); + } + }, + /** * Set whether or not to invert the color map. * @param invertMap {boolean} true if the map should be inverted; false otherwise. @@ -126,7 +173,8 @@ qx.Class.define("skel.widgets.Colormap.ColorScale", { } }, - + m_globalCheck : null, + m_globalListenId : null, m_invertCheck : null, m_reverseCheck : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorTransform.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorTransform.js index 5e6000b3..046c1aac 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorTransform.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorTransform.js @@ -19,11 +19,7 @@ qx.Class.define("skel.widgets.Colormap.ColorTransform", { this.m_connector = mImport("connector"); - //Initialize the shared variable that manages the list of data transforms var pathDict = skel.widgets.Path.getInstance(); - this.m_sharedVarData = this.m_connector.getSharedVar(pathDict.TRANSFORMS_DATA); - this.m_sharedVarData.addCB(this._dataChangedCB.bind(this)); - this._dataChangedCB(); //Initialize the shared variable that manages the list of image transforms. this.m_sharedVarImage = this.m_connector.getSharedVar(pathDict.TRANSFORMS_IMAGE); @@ -43,74 +39,19 @@ qx.Class.define("skel.widgets.Colormap.ColorTransform", { var widgetLayout = new qx.ui.layout.VBox(1); this._setLayout(widgetLayout); this._add( new qx.ui.core.Spacer(), {flex:1}); - var comp = new qx.ui.container.Composite(); - var gridLayout = new qx.ui.layout.Grid(); - gridLayout.setColumnAlign( 0, "right", "middle"); - comp.setLayout( gridLayout ); this.m_imageCombo = new qx.ui.form.ComboBox(); this.m_imageCombo.setToolTipText( "Select an image transformation."); this.m_imageCombo.setEnabled( false ); var imageLabel = new qx.ui.basic.Label( "Image:"); - comp.add( imageLabel, {row:0,column:0} ); - comp.add( this.m_imageCombo, {row:0,column:1} ); + this._add( imageLabel ); + this._add( this.m_imageCombo ); - this.m_dataCombo = new qx.ui.form.ComboBox(); - this.m_dataCombo.setToolTipText( "Select a data transformation."); - this.m_dataCombo.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ - if ( this.m_id !== null ){ - var transformName = e.getData(); - //Send a command to the server to let them know the map changed. - var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + "setDataTransform"; - var params = "dataTransform:"+transformName; - this.m_connector.sendCommand( cmd, params, function(){}); - } - },this); - var dataLabel = new qx.ui.basic.Label( "Data:"); - comp.add( dataLabel, {row:1,column:0} ); - comp.add( this.m_dataCombo, {row:1, column:1} ); - - this._add( comp ); this._add( new qx.ui.core.Spacer(), {flex:1}); }, /** - * Updates the UI when the data transform changes on the server. - */ - _dataChangedCB : function(){ - if ( this.m_sharedVarData ){ - var val = this.m_sharedVarData.get(); - if ( val ){ - try { - var oldData = this.m_dataCombo.getValue(); - var transforms = JSON.parse( val ); - var transformCount = transforms.dataTransformCount; - this.m_dataCombo.removeAll(); - for ( var i = 0; i < transformCount; i++ ){ - var transformName = transforms.dataTransforms[i]; - var tempItem = new qx.ui.form.ListItem( transformName ); - this.m_dataCombo.add( tempItem ); - } - //Try to reset the old selection - if ( oldData !== null ){ - this.m_dataCombo.setValue( oldData ); - } - //Select the first item - else if ( transformCount > 0 ){ - var selectables = this.m_dataCombo.getChildrenContainer().getSelectables(true); - if ( selectables.length > 0 ){ - this.m_dataCombo.setValue( selectables[0].getLabel()); - } - } - } - catch( err ){ - console.log( "Could not parse data transforms: "+val ); - } - } - } - }, - + /** * Updates the UI when the image transform changes on the server. */ @@ -121,7 +62,7 @@ qx.Class.define("skel.widgets.Colormap.ColorTransform", { try { var oldData = this.m_imageCombo.getValue(); var transforms = JSON.parse( val ); - var transformCount = transforms.imageTransformCount; + var transformCount = transforms.imageTransforms.length; this.m_imageCombo.removeAll(); for ( var i = 0; i < transformCount; i++ ){ var transformName = transforms.imageTransforms[i]; @@ -152,15 +93,8 @@ qx.Class.define("skel.widgets.Colormap.ColorTransform", { * @param controls {Object} - server-side color map state. */ setControls : function( controls ){ - var dSelectables = this.m_dataCombo.getChildrenContainer().getSelectables(true); - for ( var i = 0; i < dSelectables.length; i++ ){ - if ( dSelectables[i].getLabel() == controls.dataTransform ){ - this.m_dataCombo.setValue( dSelectables[i].getLabel() ); - break; - } - } var iSelectables = this.m_imageCombo.getChildrenContainer().getSelectables(true); - for ( i = 0; i < iSelectables.length; i++ ){ + for ( var i = 0; i < iSelectables.length; i++ ){ if ( iSelectables[i].getLabel() == controls.imageTransform ){ this.m_imageCombo.setValue( iSelectables[i].getLabel() ); break; @@ -178,7 +112,6 @@ qx.Class.define("skel.widgets.Colormap.ColorTransform", { m_id : null, m_imageCombo : null, - m_dataCombo : null, m_connector : null, m_sharedVarData : null, m_sharedVarImage : null diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js index 5d998fe5..3de70411 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js @@ -100,17 +100,15 @@ qx.Class.define("skel.widgets.Colormap.Colormap", if ( val ){ try { var cMap = JSON.parse( val ); - if ( this.m_view !== null ){ this.m_view.setColorName( cMap.colorMapName ); this.m_view.setInvert( cMap.invert ); this.m_view.setReverse( cMap.reverse ); - this.m_view.setScales( cMap.colorMix.red, cMap.colorMix.green, cMap.colorMix.blue ); + this.m_view.setStops( cMap.stops ); } this.m_settings.setControls( cMap ); this.m_mapControl.setMapName( cMap.colorMapName ); - this.m_mapControl.setGlobal( cMap.global ); - + this.m_mapControl.setDataTransform( cMap.dataTransform ); } catch( err ){ console.log( "Colormap could not parse: "+val ); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js index 13b42556..d113810a 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js @@ -43,17 +43,16 @@ qx.Class.define("skel.widgets.Colormap.PageColorMap", { */ setControls : function( controls ){ if ( this.m_scaleSettings !== null ){ - //this.m_scaleSettings.setMapName( controls.colorMapName); this.m_scaleSettings.setReverse( controls.reverse ); this.m_scaleSettings.setInvert( controls.invert ); + this.m_scaleSettings.setGlobal( controls.global ); } - if ( this.m_colorMixSettings !== null ){ - this.m_colorMixSettings.setReverse( controls.reverse ); - this.m_colorMixSettings.setInvert( controls.invert ); this.m_colorMixSettings.setMix( controls.colorMix.red, controls.colorMix.green, controls.colorMix.blue ); this.m_colorMixSettings.setColorMapName( controls.colorMapName ); + this.m_colorMixSettings.setStops( controls.stops ); + } }, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageTransform.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageTransform.js index 4bc7ed07..254a877d 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageTransform.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageTransform.js @@ -24,16 +24,16 @@ qx.Class.define("skel.widgets.Colormap.PageTransform", { */ _init : function( ) { this.setPadding( 0, 0, 0, 0 ); - this.setMargin( 1, 1, 1, 1 ); this._setLayout(new qx.ui.layout.HBox(2)); + this.m_preferences = new skel.widgets.CustomUI.SignificantDigits(); this.m_transformSettings = new skel.widgets.Colormap.ColorTransform(); this.m_modelSettings = new skel.widgets.Colormap.ColorModel(); - this.m_preferences = new skel.widgets.CustomUI.SignificantDigits(); - - this.add( this.m_transformSettings ); - this.add( this.m_modelSettings ); + this.add( this.m_preferences ); + this.add( this.m_modelSettings ); + this.add( this.m_transformSettings ); + }, @@ -46,6 +46,7 @@ qx.Class.define("skel.widgets.Colormap.PageTransform", { if ( this.m_modelSettings !== null ){ this.m_transformSettings.setControls( controls); this.m_modelSettings.setGamma( controls.gamma ); + this.m_modelSettings.setGammaPosition( controls.scale1, controls.scale2 ); this.m_preferences.setControls( controls ); } }, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/TwoDSlider.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/TwoDSlider.js index d30bd53b..c73c4d4f 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/TwoDSlider.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/TwoDSlider.js @@ -3,15 +3,9 @@ * */ -/* global qx,fv, fv.console */ - +/*global mImport */ /** - - @ignore(fv.console.log) - @ignore(fv.assert) - @ignore(fv.GLOBAL_DEBUG) - @ignore(qx) - + @ignore( mImport) ************************************************************************ */ qx.Class.define("skel.widgets.Colormap.TwoDSlider", @@ -24,6 +18,9 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", this.setSyncDimension( true); this.setAllowGrowX( true ); this.setAllowGrowY( true ); + + this.m_connector = mImport("connector"); + this._initLevelCurves(); this.m_mouse = null; @@ -44,7 +41,6 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", this.m_mouseIn = false; this.update(); }, this); - this.update(); }, @@ -62,8 +58,6 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", return this.m_currentValue; }, - m_parsedStateRef: null, - /** * Template method, which can be used by derived classes to redraw the * content. It is called each time the canvas dimension change and the @@ -87,12 +81,12 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", ctx.lineWidth = this.GRID_WIDTH; ctx.beginPath(); var xi, yi, nx = 11, ny = 11, x, y; - for( xi = 0 ; xi < nx ; xi ++ ) { + for( xi = 0 ; xi < nx ; /*xi ++*/ xi = xi + 10 ) { x = xi / (nx-1) * 2 - 1; ctx.moveTo( Math.round(this._ttx(x))+0.5, this._tty(-1)); ctx.lineTo( Math.round(this._ttx(x))+0.5, this._tty(1)); } - for( yi = 0 ; yi < ny ; yi ++ ) { + for( yi = 0 ; yi < ny ; /*yi ++*/ yi = yi + 10 ) { y = yi / (ny-1) * 2 - 1; ctx.moveTo( Math.round(this._ttx(-1))+0.5, this._tty(y)); ctx.lineTo( Math.round(this._ttx(1))+0.5, this._tty(y)); @@ -105,6 +99,26 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", ctx.moveTo( Math.round(this._ttx(-1))+0.5, this._tty(0)); ctx.lineTo( Math.round(this._ttx(1))+0.5, this._tty(0)); ctx.stroke(); + + // draw the curves + if ( this.m_levelCurves !== null ){ + for ( var i = 0; i < this.m_levelCurves.length; i++ ){ + ctx.beginPath(); + for ( var j = 0; j < this.m_levelCurves[i].length; j++ ){ + var x = this.m_levelCurves[i][j].x; + var y = this.m_levelCurves[i][j].y; + var xTT = this._ttx(x); + var yTT = this._tty(y); + if ( j == 0 ){ + ctx.moveTo( xTT, yTT ); + } + else { + ctx.lineTo( xTT, yTT ); + } + } + ctx.stroke(); + } + } // draw the current value ctx.fillStyle = "rgba(255,0,0,0.5)"; @@ -125,6 +139,49 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", ctx.fillText( "Right click or ESC to reset.", this.MARGIN+2, height - this.MARGIN-2); }, + /** + * Initialize the shared variable that contains the level + * curve points. + */ + _initLevelCurves : function(){ + //Initialize the shared variable that contains the list of level curves. + var pathDict = skel.widgets.Path.getInstance(); + this.m_sharedVarGamma = this.m_connector.getSharedVar(pathDict.GAMMA); + this.m_sharedVarGamma.addCB(this._gammaChangedCB.bind(this)); + this._gammaChangedCB(); + }, + + /** + * The gamma level curves changed on the server, update + * the UI. + */ + _gammaChangedCB : function(){ + if ( this.m_sharedVarGamma ){ + var val = this.m_sharedVarGamma.get(); + if ( val ){ + try { + var gamma = JSON.parse( val ); + this.m_levelCurves = gamma.levelCurves; + this.update(); + } + catch( err ){ + console.log( "Could not gamma level curves: "+val ); + console.log( "Err="+err ); + } + } + } + }, + + + + + + _keyDownCB: function (event) { + if( event.getKeyCode() == 27) { + this.setValue( 0, 0); + } + }, + /** * returns mouse event's local position (with respect to this widget) * @param event {MouseEvent} @@ -136,64 +193,13 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", y: event.getDocumentTop() - box.top }; }, - - /** - * Updates ttya, ttyb, ttxa and ttxb based on width/height - * @param width {Number} - * @param height {Number} - */ - _ttUpdate: function( width, height) { - this.m_ttxa = width / 2 - this.MARGIN; - this.m_ttxb = width / 2; - this.m_ttya = this.MARGIN - height / 2; - this.m_ttyb = height / 2; - }, - - /** - * Translate x from model coordinates to widget coordinates. - * @param x {Number} - */ - _ttx: function( x) { - return x * this.m_ttxa + this.m_ttxb; - }, - _tty: function( y) { - return y * this.m_ttya + this.m_ttyb; - }, - _ttxinv: function( x) { - return (x - this.m_ttxb) / this.m_ttxa; - }, - _ttyinv: function( y) { - return (y - this.m_ttyb) / this.m_ttya; - }, - - _mouseMoveCB: function (event) { - var pt = this._localPos(event); - this.m_mouse = pt; - - // if the mouse is down, adjust the values - if (this.m_mouseDownPt !== null) { - this.m_currentValue.x = this._ttxinv( pt.x); - this.m_currentValue.y = this._ttyinv( pt.y); - if( this.m_currentValue.x < -1) this.m_currentValue.x = -1; - if( this.m_currentValue.x > 1) this.m_currentValue.x = 1; - if( this.m_currentValue.y < -1) this.m_currentValue.y = -1; - if( this.m_currentValue.y > 1) this.m_currentValue.y = 1; - var data = { - x : this.m_currentValue.x, - y : this.m_currentValue.y - }; - this.fireDataEvent( skel.widgets.Path.CHANGE_VALUE, data); - } - - this.update(); - }, - + _mouseDownCB: function (event) { console.log("mouse down in canvas - button ", event.getButton()); if( event.getButton() === "right") { this.m_mouseDownPt = null; - this._setValue( 0, 0); + this.setValue( 0, 0); return; } else { @@ -216,7 +222,29 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", this.fireDataEvent( skel.widgets.Path.CHANGE_VALUE, data); this.update(); }, + + _mouseMoveCB: function (event) { + var pt = this._localPos(event); + this.m_mouse = pt; + + // if the mouse is down, adjust the values + if (this.m_mouseDownPt !== null) { + this.m_currentValue.x = this._ttxinv( pt.x); + this.m_currentValue.y = this._ttyinv( pt.y); + if( this.m_currentValue.x < -1) this.m_currentValue.x = -1; + if( this.m_currentValue.x > 1) this.m_currentValue.x = 1; + if( this.m_currentValue.y < -1) this.m_currentValue.y = -1; + if( this.m_currentValue.y > 1) this.m_currentValue.y = 1; + var data = { + x : this.m_currentValue.x, + y : this.m_currentValue.y + }; + this.fireDataEvent( skel.widgets.Path.CHANGE_VALUE, data); + } + this.update(); + }, + _mouseUpCB: function (event) { this.releaseCapture(); var pt = this._localPos(event); @@ -226,13 +254,7 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", this.update(); }, - _keyDownCB: function (event) { - if( event.getKeyCode() == 27) { - this._setValue( 0, 0); - } - }, - - _setValue: function ( x, y) { + setValue: function ( x, y) { this.m_currentValue.x = x; this.m_currentValue.y = y; var data = { @@ -242,11 +264,43 @@ qx.Class.define("skel.widgets.Colormap.TwoDSlider", this.fireDataEvent( skel.widgets.Path.CHANGE_VALUE, data); this.update(); }, + + /** + * Updates ttya, ttyb, ttxa and ttxb based on width/height + * @param width {Number} + * @param height {Number} + */ + _ttUpdate: function( width, height) { + this.m_ttxa = width / 2 - this.MARGIN; + this.m_ttxb = width / 2; + this.m_ttya = this.MARGIN - height / 2; + this.m_ttyb = height / 2; + }, + + /** + * Translate x from model coordinates to widget coordinates. + * @param x {Number} + */ + _ttx: function( x) { + return x * this.m_ttxa + this.m_ttxb; + }, + _tty: function( y) { + return y * this.m_ttya + this.m_ttyb; + }, + _ttxinv: function( x) { + return (x - this.m_ttxb) / this.m_ttxa; + }, + _ttyinv: function( y) { + return (y - this.m_ttyb) / this.m_ttya; + }, m_mouse: null, m_mouseDownPt: null, m_currentValue: null, m_mouseIn: false, + m_connector : null, + m_sharedVarGamma: null, + m_levelCurves : null, m_ttxa: 1, m_ttxb: 1, m_ttya: 1, m_ttyb: 1, GRID_COLOR: "rgba(0,0,64,0.2)", GRID_COLOR2: "rgba(0,0,128,0.7)", diff --git a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ItemTable.js b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ItemTable.js index c2ccca10..3777a2ea 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ItemTable.js +++ b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ItemTable.js @@ -71,6 +71,10 @@ qx.Class.define("skel.widgets.CustomUI.ItemTable", { return items; }, + setTestId : function( id ){ + skel.widgets.TestID.addTestId( this.m_table, id ); + }, + /** * Initializes the UI. */ diff --git a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js index c1cf5e71..f752d4db 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js +++ b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js @@ -91,23 +91,25 @@ qx.Class.define("skel.widgets.CustomUI.SelectBox", { */ setSelectItems : function ( items ){ this.removeListenerById( this.m_selectListener ); - this.removeAll(); var oldValue = this.getValue(); + this.removeAll(); + var selection = []; for ( var i = 0; i < items.length; i++ ){ var newValue = items[i]+""; + var tempItem = new qx.ui.form.ListItem( newValue ); + if ( newValue == oldValue ){ + selection.push( tempItem ); + } this.add( tempItem ); } //Try to reset the old selection if ( oldValue !== null ){ if ( items.length > 0 ){ - var selection = []; - selection.push( oldValue ); this.setSelection( selection ); } } - //Select the first item else if ( items.length > 0 ){ var selectables = this.getSelectables(true); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Path.js b/carta/html5/common/skel/source/class/skel/widgets/Path.js index be014f31..83fbab6f 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Path.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Path.js @@ -19,7 +19,8 @@ qx.Class.define("skel.widgets.Path", { this.DATA_COUNT = this.BASE_PATH + "controller"+ this.SEP + "dataCount"; this.ERROR_HANDLER = this.BASE_PATH + "ErrorManager"; this.FONTS = this.BASE_PATH + "Fonts"; - this.INTENSITY_UNITS = this.BASE_PATH + "IntensityUnits"; + this.GAMMA = this.BASE_PATH + "Gamma"; + this.INTENSITY_UNITS = this.BASE_PATH + "UnitsIntensity"; this.LABEL_FORMATS = this.BASE_PATH + "LabelFormats"; this.LAYER_COMPOSITION_MODES = this.BASE_PATH + "LayerCompositionModes"; this.LAYOUT = this.BASE_PATH + "Layout"; @@ -30,12 +31,14 @@ qx.Class.define("skel.widgets.Path", { this.PLOT_STYLES = this.BASE_PATH + "ProfilePlotStyles"; this.PREFERENCES = this.BASE_PATH + "Preferences"; this.PREFERENCES_SAVE = this.BASE_PATH + "PreferencesSave"; + this.PROFILE_FREQ_UNITS = this.BASE_PATH + "UnitsFrequency"; + this.PROFILE_WAVE_UNITS = this.BASE_PATH + "UnitsWavelength"; this.PROFILE_GEN_MODES = this.BASE_PATH + "GenerateModes"; this.PROFILE_STATISTICS = this.BASE_PATH + "ProfileStatistics"; this.REGION = this.BASE_PATH + this.REGION_DATA + this.SEP; this.SETTINGS = this.BASE_PATH + "Settings"; this.SNAPSHOTS = this.BASE_PATH + "Snapshots"; - this.SPECTRAL_UNITS = this.BASE_PATH + "SpectralUnits"; + this.SPECTRAL_UNITS = this.BASE_PATH + "UnitsSpectral"; this.THEMES = this.BASE_PATH + "Themes"; this.TRANSFORMS_DATA = this.BASE_PATH +"TransformsData"; this.TRANSFORMS_IMAGE = this.BASE_PATH + "TransformsImage"; @@ -72,6 +75,7 @@ qx.Class.define("skel.widgets.Path", { DATA_LOADER : "DataLoader", ERROR_HANDLER : "", FONTS : "", + GAMMA : "", GRID_PLUGIN : "GridControls", HIDDEN : "Hidden", HIDE_IMAGE : "hideImage", diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/Settings.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/Settings.js index e537641c..61c0aba7 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Profile/Settings.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/Settings.js @@ -39,6 +39,7 @@ qx.Class.define("skel.widgets.Profile.Settings", { this.m_pages[this.m_INDEX_PLOT] = new skel.widgets.Profile.SettingsDisplay(); this.m_pages[this.m_INDEX_PROFILES] = new skel.widgets.Profile.SettingsProfiles(); this.m_pages[this.m_INDEX_CURVES] = new skel.widgets.Profile.SettingsCurves(); + this.m_pages[this.m_INDEX_RANGE] = new skel.widgets.Profile.SettingsRange(); for ( var i = 0; i < this.m_pages.length; i++ ){ this.m_tabView.add( this.m_pages[i] ); @@ -73,7 +74,7 @@ qx.Class.define("skel.widgets.Profile.Settings", { var profileData = JSON.parse( val ); this.m_pages[this.m_INDEX_CURVES].dataUpdate( profileData ); this.m_pages[this.m_INDEX_PROFILES].dataUpdate( profileData ); - this.m_pages[this.m_INDEX_PLOT].dataUpdate( profileData ); + this.m_pages[this.m_INDEX_RANGE].dataUpdate( profileData ); } catch( err ){ console.log( "TabSettings Could not parse: "+val+" error: "+err ); @@ -110,8 +111,9 @@ qx.Class.define("skel.widgets.Profile.Settings", { m_sharedVar : null, m_sharedVarData : null, - m_INDEX_CURVES : 2, - m_INDEX_PROFILES : 1, + m_INDEX_RANGE : 1, + m_INDEX_CURVES : 3, + m_INDEX_PROFILES : 2, m_INDEX_PLOT : 0 } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsAxis.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsAxis.js index 5dc78ac7..08beb4f4 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsAxis.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsAxis.js @@ -98,7 +98,7 @@ qx.Class.define("skel.widgets.Profile.SettingsAxis", { if ( val ){ try { var obj = JSON.parse( val ); - var units = obj.spectralUnits; + var units = obj.units; this.m_axisBottom.setSelectItems( units ); } catch( err ){ @@ -118,7 +118,7 @@ qx.Class.define("skel.widgets.Profile.SettingsAxis", { if ( val ){ try { var obj = JSON.parse( val ); - var units = obj.intensityUnits; + var units = obj.units; this.m_axisLeft.setSelectItems( units ); } catch( err ){ diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsCurves.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsCurves.js index 038710a2..716d6185 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsCurves.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsCurves.js @@ -66,6 +66,7 @@ qx.Class.define("skel.widgets.Profile.SettingsCurves", { var TABLE_WIDTH = 150; this.m_curveList = new skel.widgets.CustomUI.ItemTable( "Profiles", TABLE_WIDTH); + this.m_curveList.setTestId( "profileTable"); this.m_curveList.setToolTipText( "Select one or more profiles to customize the display." ); this.m_curveList.setWidth( TABLE_WIDTH ); this.m_curveList.addListener( "itemsSelected", this._updateColor, this ); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDigits.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDigits.js new file mode 100644 index 00000000..aa7d3de6 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDigits.js @@ -0,0 +1,62 @@ +/** + * UI for displaying profile significant digits. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.widgets.Profile.SettingsDigits", { + extend : qx.ui.core.Widget, + + /** + * Constructor. + */ + construct : function( ) { + this.base(arguments); + this._init( ); + }, + + + members : { + + /** + * Initializes the UI. + */ + _init : function( ) { + var widgetLayout = new qx.ui.layout.VBox(1); + this._setLayout(widgetLayout); + var displayContainer = new qx.ui.groupbox.GroupBox("Precision", ""); + displayContainer.setContentPadding(1,1,1,1); + displayContainer.setLayout( new qx.ui.layout.VBox(1)); + this._add( displayContainer ); + this.m_digitSettings = new skel.widgets.CustomUI.SignificantDigits(); + displayContainer.add( this.m_digitSettings ); + }, + + /** + * Updates the significant digits with a server-side value. + * @param controls {String} - server-side information. + */ + prefUpdate : function( profilePrefs ){ + this.m_digitSettings.setControls( profilePrefs ); + }, + + /** + * Set the server side id of this profile. + * @param id {String} the server side id of the object that produced this profile. + */ + setId : function( id ){ + this.m_digitSettings.setId( id ); + }, + + m_digitSettings : null + }, + + properties : { + appearance : { + refine : true, + init : "internal-area" + } + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDisplay.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDisplay.js index cee0ec6e..f9da6042 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDisplay.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsDisplay.js @@ -30,14 +30,14 @@ qx.Class.define("skel.widgets.Profile.SettingsDisplay", { this.m_axesSettings = new skel.widgets.Profile.SettingsAxis(); this.add( this.m_axesSettings ); - this.m_zoomSettings = new skel.widgets.Profile.SettingsZoom(); - this.add( this.m_zoomSettings ); - this.m_canvasSettings = new skel.widgets.Profile.SettingsCanvas(); this.add( this.m_canvasSettings ); this.m_legendSettings = new skel.widgets.Profile.SettingsLegend(); this.add( this.m_legendSettings ); + + this.m_digitSettings = new skel.widgets.Profile.SettingsDigits(); + this.add( this.m_digitSettings ); }, @@ -57,16 +57,8 @@ qx.Class.define("skel.widgets.Profile.SettingsDisplay", { if ( this.m_canvasSettings !== null ){ this.m_canvasSettings.prefUpdate( profilePrefs ); } - }, - - /** - * Update data based server state. - * @param profileData {Object} - information about the data state - * from the server. - */ - dataUpdate : function( profileData ){ - if ( this.m_zoomSettings !== null ){ - this.m_zoomSettings.dataUpdate( profileData ); + if ( this.m_digitSettings != null ){ + this.m_digitSettings.prefUpdate( profilePrefs ); } }, @@ -81,13 +73,13 @@ qx.Class.define("skel.widgets.Profile.SettingsDisplay", { this.m_axesSettings.setId( id ); this.m_canvasSettings.setId( id ); this.m_legendSettings.setId( id ); - this.m_zoomSettings.setId( id ); + this.m_digitSettings.setId( id ); }, m_id : null, m_axesSettings : null, m_canvasSettings : null, m_legendSettings : null, - m_zoomSettings : null + m_digitSettings : null } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfiles.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfiles.js index ebcc16ff..14ee948a 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfiles.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfiles.js @@ -50,6 +50,7 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { var selectIndex = curveUpdate.selectCurve; this._updateSelection( selectIndex ); this.m_genSelect.setSelectValue( curveUpdate.genMode ); + this.m_statSelect.setSelectValue( curveUpdate.stat ); } }, @@ -61,27 +62,15 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { var widgetLayout = new qx.ui.layout.HBox(1); this._setLayout(widgetLayout); - var overallContainer = new qx.ui.container.Composite(); overallContainer.setLayout( new qx.ui.layout.VBox(1)); - //Init generate. - var genContainer = new qx.ui.container.Composite(); - genContainer.setLayout( new qx.ui.layout.HBox(1)); - this.m_genSelect = new skel.widgets.CustomUI.SelectBox(); - this.m_genSelect.addListener( "changeValue", this._genModeChangedCB, this ); - var genLabel = new qx.ui.basic.Label( "Auto Generate:"); - genContainer.add( new qx.ui.core.Spacer(1), {flex:1}); - genContainer.add( genLabel ); - genContainer.add( this.m_genSelect ); - genContainer.add( new qx.ui.core.Spacer(1), {flex:1}); - overallContainer.add( genContainer ); - //Custom Name var nameContainer = new qx.ui.container.Composite(); nameContainer.setLayout( new qx.ui.layout.HBox(1)); this.m_nameSelect = new qx.ui.form.ComboBox(); this.m_nameSelect.addListener( "changeValue", this._nameChangedCB, this ); + skel.widgets.TestID.addTestId( this.m_nameSelect, "profileNameSelect" ); this.m_nameSelect.setToolTipText( "Specify a custom name for the profile."); var nameLabel = new qx.ui.basic.Label( "Name:"); nameContainer.add( new qx.ui.core.Spacer(1), {flex:1}); @@ -113,16 +102,13 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { selectContainer.add( this.m_regionSelect, {row:0, column:3} ); //Initialize rest frequency - gridLayout.setRowAlign( 1, "right", "middle"); - var restFreqLabel = new qx.ui.basic.Label( "Rest Frequency:"); - this.m_restFreqText = new skel.widgets.CustomUI.NumericTextField( 0, null ); - this.m_restFreqText.setToolTipText( "Specify the rest frequency."); - selectContainer.add( restFreqLabel, {row:1, column:0}); - selectContainer.add( this.m_restFreqText, {row:1, column:1} ); + this.m_restWidget = new skel.widgets.Profile.SettingsProfilesRest("",""); + selectContainer.add( this.m_restWidget, {row:1, column:0, colSpan:2, rowSpan:2} ); //Initialize the statistic var statLabel = new qx.ui.basic.Label( "Statistic:"); this.m_statSelect = new skel.widgets.CustomUI.SelectBox(); + this.m_statSelect.addListener( "selectChanged", this._sendStatCmd, this ); this.m_statSelect.setToolTipText( "Specify the method used to generate the profile."); selectContainer.add( statLabel, {row:1, column:2}); selectContainer.add( this.m_statSelect, {row:1, column:3}); @@ -142,6 +128,12 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { var butContainer = new qx.ui.container.Composite(); butContainer.setLayout( new qx.ui.layout.HBox(1)); butContainer.add( new qx.ui.core.Spacer(1), {flex:1}); + this.m_genSelect = new skel.widgets.CustomUI.SelectBox(); + skel.widgets.TestID.addTestId( this.m_genSelect, "profileGenerateMode" ); + this.m_genSelect.setToolTipText( "Specify which images should be profiled by default when they are loaded.") + this.m_genSelect.addListener( "selectChanged", this._genModeChangedCB, this ); + var genLabel = new qx.ui.basic.Label( "Auto Generate:"); + this.m_addButton = new qx.ui.form.Button( "New"); this.m_addButton.setToolTipText( "Create a new profile using default settings."); this.m_addButton.addListener( "execute", this._sendNewCmd, this ); @@ -149,12 +141,15 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { this.m_copyButton.setToolTipText( "Create a new profile using the same settings as the selected profile."); this.m_copyButton.addListener( "execute", this._sendCopyCmd, this ); this.m_removeButton = new qx.ui.form.Button( "Remove"); + skel.widgets.TestID.addTestId( this.m_removeButton, "profileRemoveButton" ); this.m_removeButton.setToolTipText( "Delete the selected profile."); this.m_removeButton.addListener( "execute",this._sendRemoveCmd, this ); + butContainer.add( genLabel ); + butContainer.add( this.m_genSelect ); + butContainer.add( new qx.ui.core.Spacer(1), {flex:1}); butContainer.add( this.m_addButton ); butContainer.add( this.m_copyButton ); butContainer.add( this.m_removeButton ); - butContainer.add( new qx.ui.core.Spacer(1), {flex:1}); return butContainer; }, @@ -270,6 +265,21 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { } }, + /** + * Send a command to the server to change the method used to summarize + * profile points. + */ + _sendStatCmd : function(){ + if ( this.m_id !== null && this.m_connector !== null ){ + var newName = this.m_nameSelect.getValue(); + var newStat = this.m_statSelect.getValue(); + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setStatistic"; + var params = "name:"+newName+",stat:"+newStat; + this.m_connector.sendCommand( cmd, params, null ); + } + }, + /** * Set the server side id of the object managing profile information. @@ -277,6 +287,7 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { */ setId : function( id ){ this.m_id = id; + this.m_restWidget.setId( id ); }, /** @@ -353,8 +364,7 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { if ( imageName != this.m_imageSelect.getValue() ){ this.m_imageSelect.setSelectValue( imageName ); } - var restFreq = Number(this.m_curveInfo[selectIndex].restFrequency); - this.m_restFreqText.setValue( restFreq ); + this.m_restWidget.update( this.m_curveInfo[selectIndex] ); var statName = this.m_curveInfo[selectIndex].stat; if ( statName != this.m_statSelect.getValue() ){ this.m_statSelect.setSelectValue( statName ); @@ -371,7 +381,7 @@ qx.Class.define("skel.widgets.Profile.SettingsProfiles", { m_genSelect : null, m_nameSelect : null, m_regionSelect : null, - m_restFreqText : null, + m_restWidget : null, m_statSelect : null, m_connector : null, m_sharedVarStats : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfilesRest.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfilesRest.js new file mode 100755 index 00000000..5380cb20 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsProfilesRest.js @@ -0,0 +1,277 @@ +/** + * Controls for setting the rest frequency used to calculate a profile. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.widgets.Profile.SettingsProfilesRest", { + extend : qx.ui.core.Widget, + + construct : function( ) { + this.base(arguments); + this._init( ); + + //Initiate connector. + if ( typeof mImport !== "undefined"){ + this.m_connector = mImport("connector"); + + var path = skel.widgets.Path.getInstance(); + + //Frequency units + this.m_sharedVarFreqUnits = this.m_connector.getSharedVar(path.PROFILE_FREQ_UNITS); + this.m_sharedVarFreqUnits.addCB(this._freqUnitsChangedCB.bind(this)); + this._freqUnitsChangedCB(); + + //Wavelength units + this.m_sharedVarWaveUnits = this.m_connector.getSharedVar(path.PROFILE_WAVE_UNITS); + this.m_sharedVarWaveUnits.addCB(this._waveUnitsChangedCB.bind(this)); + this._waveUnitsChangedCB(); + } + }, + + + members : { + + /** + * Notification that the available frequency units have changed on the + * server. + */ + _freqUnitsChangedCB : function(){ + if ( this.m_sharedVarFreqUnits ){ + var val = this.m_sharedVarFreqUnits.get(); + if ( val ){ + try { + var obj = JSON.parse( val ); + this.m_unitsFreq = obj.units; + this._unitChanged(); + } + catch( err ){ + console.log( "Could not parse rest frequency units: "+val ); + console.log( "Err: "+err); + } + } + } + }, + + + /** + * Initializes the UI. + */ + _init : function( ) { + var widgetLayout = new qx.ui.layout.VBox(1); + this._setLayout(widgetLayout); + + var overallContainer = new qx.ui.container.Composite(); + overallContainer.setLayout( new qx.ui.layout.VBox(1)); + this._add( overallContainer ); + + var checkContainer = new qx.ui.container.Composite(); + checkContainer.setLayout( new qx.ui.layout.HBox(1)); + var typeLabel = new qx.ui.basic.Label( "Rest:"); + this.m_freqRadio = new qx.ui.form.RadioButton( "Frequency"); + skel.widgets.TestID.addTestId( this.m_freqRadio, "profileFrequencyUnitType" ); + this.m_freqRadio.setToolTipText( "Specify rest frequency in frequency units.") + this.m_freqRadio.setValue( true ); + this.m_unitListenId = this.m_freqRadio.addListener( "changeValue", this._unitChanged, this ); + this.m_waveRadio = new qx.ui.form.RadioButton( "Wavelength"); + skel.widgets.TestID.addTestId( this.m_waveRadio, "profileWaveUnitType" ); + this.m_waveRadio.setToolTipText( "Specify rest frequency in wavelength units."); + var typeGroup = new qx.ui.form.RadioGroup(); + typeGroup.add( this.m_freqRadio ); + typeGroup.add( this.m_waveRadio ); + checkContainer.add( new qx.ui.core.Spacer(1), {flex:1}); + checkContainer.add( typeLabel ); + checkContainer.add( this.m_freqRadio ); + checkContainer.add( this.m_waveRadio ); + checkContainer.add( new qx.ui.core.Spacer(1), {flex:1}); + overallContainer.add( checkContainer, {flex:1}); + + var valueContainer = new qx.ui.container.Composite(); + valueContainer.setLayout( new qx.ui.layout.HBox(1)); + this.m_freqText = new skel.widgets.CustomUI.NumericTextField(0,null); + this.m_freqText.setTextId( "profileRestFrequency" ); + this.m_freqText.setIntegerOnly( false ); + this.m_freqText.setToolTipText( "Specify the rest frequency to use in computing the profile.") + this.m_freqTextListenId = this.m_freqText.addListener( "textChanged", + this._sendRestFreqCmd, this ); + this.m_freqUnitsSelect = new skel.widgets.CustomUI.SelectBox(); + skel.widgets.TestID.addTestId( this.m_freqUnitsSelect, "profileFrequencyUnits" ); + this.m_freqUnitsSelect.setToolTipText( "Rest frequency units."); + this.m_freqUnitsSelectListenId = this.m_freqUnitsSelect.addListener( "selectChanged", + this._sendUnitsCmd, this ); + this.m_resetButton = new qx.ui.form.Button( "Reset"); + skel.widgets.TestID.addTestId( this.m_resetButton, "profileRestReset" ); + this.m_resetButton.setToolTipText( "Reset the rest frequency for the profile back to its original value." ); + this.m_resetButton.addListener( "execute", this._sendResetCmd, this ); + valueContainer.add( this.m_freqText ); + valueContainer.add( this.m_freqUnitsSelect ); + valueContainer.add( this.m_resetButton ); + overallContainer.add( valueContainer ); + }, + + /** + * Send a command to the server to set the rest frequency. + */ + _sendRestFreqCmd : function(){ + if ( this.m_id !== null && this.m_connector !== null ){ + var path = skel.widgets.Path.getInstance(); + var restFreq = this.m_freqText.getValue(); + var cmd = this.m_id + path.SEP_COMMAND + "setRestFrequency"; + var params = "name:"+this.m_curveName+", restFrequency:"+restFreq; + this.m_connector.sendCommand( cmd, params, null ); + } + }, + + /** + * Send a command to the server to reset the rest frequency to its + * original value. + */ + _sendResetCmd : function(){ + if ( this.m_id !== null && this.m_connector !== null ){ + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "resetRestFrequency"; + var params = "name:"+this.m_curveName; + this.m_connector.sendCommand( cmd, params, null ); + } + }, + + /** + * Send a command to the server to set the type of units used to + * specify rest frequency. + */ + _sendRestUnitsChangedCmd : function(){ + if ( this.m_id !== null && this.m_connector !== null ){ + var freqUnits = this.m_freqRadio.getValue(); + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setRestUnitType"; + var params = "restFrequencyUnits:"+freqUnits+",name:"+this.m_curveName; + this.m_connector.sendCommand( cmd, params, null ); + } + }, + + /** + * Send a command to the server to set the rest frequency units. + */ + _sendUnitsCmd : function(){ + if ( this.m_id !== null && this.m_connector !== null ){ + var restUnits = this.m_freqUnitsSelect.getValue(); + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setRestUnit"; + var params = "restUnitFreq:"+restUnits+",name:"+this.m_curveName; + this.m_connector.sendCommand( cmd, params, null ); + } + }, + + /** + * Set the server side id of the object managing state for this UI.. + * @param id {String} the server side id of the object. + */ + setId : function( id ){ + this.m_id = id; + }, + + /** + * The user has changed the type of units used to specify rest + * frequency. + */ + _unitChanged : function(){ + //Update the available units based on what is checked. + if ( this.m_freqRadio.getValue() ){ + if ( this.m_unitsFreq != null ){ + this.m_freqUnitsSelect.setSelectItems( this.m_unitsFreq ); + } + } + else { + if ( this.m_unitsWave != null ){ + this.m_freqUnitsSelect.setSelectItems( this.m_unitsWave ); + } + } + this._sendRestUnitsChangedCmd(); + }, + + /** + * A new profile curve has been selected. + * @param curveInfo {Object} - information about the selected curve. + */ + update : function( curveInfo ){ + this.m_curveName = curveInfo.name; + if ( this.m_freqText !== null ){ + if ( this.m_freqTextListenId !== null ){ + this.m_freqText.removeListenerById( this.m_freqTextListenId ); + } + this.m_freqText.setValue( curveInfo.restFrequency ); + this.m_freqTextListenId = this.m_freqText.addListener( "textChanged", + this._sendRestFreqCmd, this ); + } + var freq = curveInfo.restFrequencyUnits; + if ( this.m_freqRadio.getValue() != freq ){ + if ( this.m_unitListenId !== null ){ + this.m_freqRadio.removeListenerById( this.m_unitListenId ); + } + this.m_freqRadio.setValue( freq ); + this.m_unitListenId = this.m_freqRadio.addListener( "changeValue", this._unitChanged, this ); + } + if ( this.m_freqUnitsSelectListenId !== null ){ + this.m_freqUnitsSelect.removeListenerById( this.m_freqUnitsSelectListenId ); + } + if ( this.m_freqRadio.getValue()){ + this.m_freqUnitsSelect.setSelectItems( this.m_unitsFreq ); + this.m_freqUnitsSelect.setSelectValue( curveInfo.restUnitFreq ); + } + else { + this.m_freqUnitsSelect.setSelectItems( this.m_unitsWave ); + this.m_freqUnitsSelect.setSelectValue( curveInfo.restUnitWave ); + } + this.m_freqUnitsSelectListenId = this.m_freqUnitsSelect.addListener( "selectChanged", + this._sendUnitsCmd, this ); + }, + + /** + * Update from the server indicating the available wavelength units have + * changed. + */ + _waveUnitsChangedCB : function(){ + if ( this.m_sharedVarWaveUnits ){ + var val = this.m_sharedVarWaveUnits.get(); + if ( val ){ + try { + var obj = JSON.parse( val ); + this.m_unitsWave = obj.units; + this._unitChanged(); + } + catch( err ){ + console.log( "Could not parse rest frequency units: "+val ); + console.log( "Err: "+err); + } + } + } + }, + + + m_id : null, + m_connector : null, + m_curveName : "", + m_sharedVarFreqUnits : null, + m_sharedVarWaveUnits : null, + m_unitListenId : null, + m_unitsFreq : null, + m_unitsWave : null, + + m_freqRadio : null, + m_waveRadio : null, + m_freqText : null, + m_freqTextListenId : null, + m_freqUnitsSelect : null, + m_freqUnitsSelectListenId : null, + m_resetButton : null + }, + + properties : { + appearance : { + refine : true, + init : "internal-area" + } + } +}); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsRange.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsRange.js new file mode 100755 index 00000000..c48f568a --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsRange.js @@ -0,0 +1,60 @@ +/** + * Displays controls for customizing profile range settings. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.widgets.Profile.SettingsRange", { + extend : qx.ui.tabview.Page, + + /** + * Constructor. + */ + construct : function( ) { + this.base(arguments, "Range", ""); + this._init( ); + }, + + members : { + + /** + * Initializes the UI. + */ + _init : function( ) { + this.setPadding( 0, 0, 0, 0 ); + this.setMargin( 1, 1, 1, 1 ); + this._setLayout(new qx.ui.layout.HBox(2)); + + this.m_zoomSettings = new skel.widgets.Profile.SettingsZoom(); + this.add( this.m_zoomSettings ); + }, + + + /** + * Update data based server state. + * @param profileData {Object} - information about the data state + * from the server. + */ + dataUpdate : function( profileData ){ + if ( this.m_zoomSettings !== null ){ + this.m_zoomSettings.dataUpdate( profileData ); + } + }, + + + /** + * Set the server side id of this control UI. + * @param id {String} the server side id of the object that contains + * data for this control UI. + */ + setId : function( id ){ + this.m_id = id; + this.m_zoomSettings.setId( id ); + }, + + m_id : null, + m_zoomSettings : null + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsZoom.js b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsZoom.js index d8241adf..84a7b229 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsZoom.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Profile/SettingsZoom.js @@ -64,18 +64,18 @@ qx.Class.define("skel.widgets.Profile.SettingsZoom", { var minLabel = new qx.ui.basic.Label( "Min"); minLabel.setTextAlign( "center"); this.m_minClipText = new skel.widgets.CustomUI.NumericTextField( null, null); + this.m_minClipText.setTextId( 'profileZoomMinValue'); this.m_minClipText.setToolTipText( "Smallest value on the horizontal axis."); this.m_minClipText.setIntegerOnly( false ); - this.m_minClipText.setTextId( "profileZoomMinValue"); this.m_minClipListenerId = this.m_minClipText.addListener( "textChanged", this._sendRangeCmd, this ); var valueLabel = new qx.ui.basic.Label( "Value:"); valueLabel.setTextAlign( "right"); this.m_percentMinClipText = new skel.widgets.CustomUI.NumericTextField( 0, 100); + this.m_percentMinClipText.setTextId( 'profileZoomMinPercent'); this.m_percentMinClipText.setToolTipText( "Percentage to zoom from the left on the horizontal axis; 0 is no left zoom."); this.m_percentMinClipText.setIntegerOnly( false ); - this.m_percentMinClipText.setTextId( "profileZoomMinPercent"); this.m_percentMinClipListenerId = this.m_percentMinClipText.addListener( "textChanged", this._sendRangePercentCmd, this ); @@ -88,8 +88,8 @@ qx.Class.define("skel.widgets.Profile.SettingsZoom", { var maxLabel = new qx.ui.basic.Label( "Max"); maxLabel.setTextAlign( "center"); this.m_maxClipText = new skel.widgets.CustomUI.NumericTextField( null, null ); + this.m_maxClipText.setTextId( 'profileZoomMaxValue'); this.m_maxClipText.setToolTipText( "Largest value on the horizontal axis"); - this.m_maxClipText.setTextId( "profileZoomMaxValue"); this.m_maxClipText.setIntegerOnly( false ); this.m_maxClipListenerId = this.m_maxClipText.addListener( "textChanged", this._sendRangeCmd, this ); @@ -97,9 +97,9 @@ qx.Class.define("skel.widgets.Profile.SettingsZoom", { var percentLabel = new qx.ui.basic.Label( "Percent:"); percentLabel.setTextAlign( "right"); this.m_percentMaxClipText = new skel.widgets.CustomUI.NumericTextField( 0, 100); + this.m_percentMaxClipText.setTextId( 'profileZoomMaxPercent'); this.m_percentMaxClipText.setToolTipText( "Percentage to zoom in from the right on the horizontal axis; 100 is no right zoom."); this.m_percentMaxClipText.setIntegerOnly( false ); - this.m_percentMaxClipText.setTextId( "profileZoomMaxPercent"); this.m_percentMaxClipListenerId = this.m_percentMaxClipText.addListener( "textChanged", this._sendRangePercentCmd, this ); @@ -214,8 +214,11 @@ qx.Class.define("skel.widgets.Profile.SettingsZoom", { } }, + /** + * Update from the server containing information about zoom bounds. + * @param data {Object} - zoom bound information. + */ dataUpdate : function( data ){ - console.log( "Settings zoom data Update"); this.setZoomBounds( data.zoomMin, data.zoomMax ); this.setZoomPercents( data.zoomMinPercent, data.zoomMaxPercent ); this.setBuffer( data.zoomBuffer ); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Statistics/Statistics.js b/carta/html5/common/skel/source/class/skel/widgets/Statistics/Statistics.js index bd307aed..a4c66e3b 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Statistics/Statistics.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Statistics/Statistics.js @@ -30,13 +30,6 @@ qx.Class.define("skel.widgets.Statistics.Statistics", { //Image Statistics this.m_statsImage = new skel.widgets.Statistics.StatisticsImage(); - this.m_statsImage.addListener( "imageChanged", function(evt){ - var data = evt.getData(); - if ( this.m_selectIndex != data.index ){ - this.m_selectIndex = data.index; - this._statsChanged(); - } - }, this ); //Divider this.m_divWidget = new qx.ui.core.Widget(); @@ -46,6 +39,14 @@ qx.Class.define("skel.widgets.Statistics.Statistics", { //RegionStatistics this.m_statsRegions = new skel.widgets.Statistics.StatisticsRegion(); this._add( this.m_statContainer ); + + this.m_statsImage.addListener( "imageChanged", function(evt){ + var data = evt.getData(); + if ( this.m_selectIndex != data.index ){ + this.m_selectIndex = data.index; + this._statsChanged(); + } + }, this ); }, @@ -56,7 +57,6 @@ qx.Class.define("skel.widgets.Statistics.Statistics", { this.m_statContainer.removeAll(); if ( this.m_stats !== null ){ this.m_statContainer.add( this.m_statsImage ); - var regionStats = this.m_statsRegions.isStats(); if ( regionStats ){ if ( this.m_showRegionStats ){ @@ -108,7 +108,6 @@ qx.Class.define("skel.widgets.Statistics.Statistics", { * from the server; false if other data updates have previously happened. */ _statsChanged : function( firstInit ){ - this.m_statsImage.updateImages( this.m_stats ); if ( 0 <= this.m_selectIndex && this.m_selectIndex < this.m_stats.length ){ //Image stats are always the first. diff --git a/carta/html5/common/skel/source/resource/skel/decoration/brightness-field-128.png b/carta/html5/common/skel/source/resource/skel/decoration/brightness-field-128.png new file mode 100644 index 00000000..14d0303a Binary files /dev/null and b/carta/html5/common/skel/source/resource/skel/decoration/brightness-field-128.png differ diff --git a/carta/html5/common/skel/source/resource/skel/decoration/brightness-handle-128.gif b/carta/html5/common/skel/source/resource/skel/decoration/brightness-handle-128.gif new file mode 100644 index 00000000..6ab84f08 Binary files /dev/null and b/carta/html5/common/skel/source/resource/skel/decoration/brightness-handle-128.gif differ diff --git a/carta/scriptedClient/carta/colormap.py b/carta/scriptedClient/carta/colormap.py index 09fefec7..6ebe7a3c 100644 --- a/carta/scriptedClient/carta/colormap.py +++ b/carta/scriptedClient/carta/colormap.py @@ -74,6 +74,16 @@ def setInterpolatedColormap(self, interpolatedStr='toggle'): interpolatedString=str(interpolatedStr)) return result + def setNanDefault(self, nanDefault = 'TRUE'): + """ + Set whether or not to use the default nan color (bottom of the color + map). + """ + result = self.con.cmdTagList("setNanDefault", + colormapId=self.getId(), + nanDefaultString=str(nanDefault)) + return result + def invertColormap(self, invertStr='toggle'): """ Invert the colormap. diff --git a/carta/scriptedClient/carta/image.py b/carta/scriptedClient/carta/image.py index d172efa5..fb9de8d6 100644 --- a/carta/scriptedClient/carta/image.py +++ b/carta/scriptedClient/carta/image.py @@ -510,7 +510,7 @@ def getIntensity(self, frameLow, frameHigh, percentile): def centerOnCoordinate(self, skyCoord): """ Centers the image on an Astropy SkyCoord object. - + Astropy needs to be installed for this command to work. See http://www.astropy.org for more information about Astropy and how to install it. @@ -646,7 +646,7 @@ def closeImage(self, imageName): def setGridAxesColor(self, red, green, blue): """ Set the color of the grid axes. - + Parameters ---------- red: integer @@ -1337,7 +1337,7 @@ def setContourLevelMin(self, value): imageView=self.getId(), value=value) return result - def setContourLevels(self, contourName, levels): + def setContourLevels(self, contourName, levels): """ Update the contour levels within the given contour set. @@ -1359,6 +1359,96 @@ def setContourLevels(self, contourName, levels): contourName=contourName, levels=levels) return result + def showImage(self, imageName): + """ + Show the image. + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("showImage", imageName, imageView=self.getId()) + return result + + def hideImage(self, imageName): + """ + Hide the image. + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("hideImage", imageName, imageView=self.getId()) + return result + + def setStackSelectAuto(self, stackSelectFlag = 'true'): + """ + Set whether or not selection of layers in the stack should be based on the + current layer or whether the user wants to make a manual selection. + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("setStackSelectAuto", imageView=self.getId(), stackSelectFlag) + return result + + def setPanZoomAll(self, setPanZoomAllFlag = 'true'): + """ + Set whether or not a pan/zoom operation should affect all layers in the stack + or just the top layer. + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("setPanZoomAll", imageView=self.getId(), setPanZoomAllFlag) + return result + + def setMaskAlpha(self, imageName, alphaAmount = 0): + """ + Set the transparency of the layer. + alphaAmount - the transparency level in [0,255] with 255 being opaque. + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("setMaskAlpha", imageView=self.getId(), imageName, alphaAmount) + return result + + def setMaskColor(self, imageName, redAmount = 0, greenAmount = 0, blueAmount = 0): + """ + Set the color to use for the mask. + redAmount - the amount of red in [0,255]. + greenAmount - the amount of green in [0,255]. + blueAmount - the amount of blue in [0,255]. + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("setMaskAlpha", imageView=self.getId(), imageName, redAmount, greenAmount, blueAmount) + return result + + def setCompositionMode(self, imageName ): + """ + Set whether or not to apply a composition mode to the image + + Returns + ------- + list + Error message if an error occurred; nothing otherwise. + """ + result = self.con.cmdTagList("setCompositionMode", imageView=self.getId(), imageName) + return result + def isEmpty(self): """ Checks whether or not the image view is empty.