;-------------------------------------------------------------------------
;
; Automatic file identification, please don't touch or remove :
;
; $Id: festival_data__define.pro 1199 2009-02-24 13:56:17Z esoubrie $
;
; ------------------------------------------------------------------------

PRO FESTIVAL_DATA::SETPROPERTY, $
				FILTERED = filtered, $
				PROJECTED_IMAGE = oProjectedImage, $
				ENHANCED = enhanced, $orbit_errmsg
				FITSHEADER = fitsHeader, $
				CALIBRATED = calibrated, $
				FILTERING_SEQUENCE = filteringSequence, $
				PROJCOORD = projCoord, $
				MASK_OF_NONMISSING = maskOfNonMissing, $
				PROJECTION_MASK = projectionMask, $
				PROBE = probe, $
				INSTRUMENT = instrument, $
				VISU_ELEMENT = oVisuElt

	IF N_ELEMENTS(filtered) GT 0 THEN BEGIN

		IF PTR_VALID(self._pFilteredImage) THEN $
			*self._pFilteredImage = filtered ELSE $
			self._pFilteredImage = PTR_NEW(filtered)

	END

	IF N_ELEMENTS(oProjectedImage) GT 0 THEN BEGIN

		; Get interpolation method :
		self._oDataEngine->GETPROPERTY, SELECTION_GUI = oSelectionGUI
		IF ~OBJ_VALID(oSelectionGUI) THEN $
			MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"

		; Get system GUI :
		oSelectionGUI->GETPROPERTY, SYSTEM_OPTIONS_WIZARD = oSystemGUI
		IF ~OBJ_VALID(oSystemGUI) THEN $
			MESSAGE, "An invalid FESTIVAL_SYSTEM_GUI object was found"
		oSystemGUI->GETPROPERTY, INTERPOLATE = interpolate

		oProjectedImage->GETPROPERTY, DATA = image, PALETTE = oPalette
		self._oProjectedImage = OBJ_NEW("IDLGRIMAGE", image, INTERPOLATE = interpolate)

		IF OBJ_VALID(oPalette) THEN BEGIN

			oPalette->GETPROPERTY, RED_VALUES = red, GREEN_VALUES = green, BLUE_VALUES = blue
			copyPalette = OBJ_NEW("IDLGRPALETTE", red, green, blue)
			self._oProjectedImage->SETPROPERTY, PALETTE = copyPalette

		END

	END

	IF N_ELEMENTS(enhanced) GT 0 THEN BEGIN

		IF PTR_VALID(self._pEnhancedImage) THEN $
			*self._pEnhancedImage = enhanced ELSE $
			self._pEnhancedImage = PTR_NEW(enhanced)

	END

	IF N_ELEMENTS(fitsHeader) GT 0 THEN BEGIN

		IF PTR_VALID(self._pFITSHeader) THEN $
			*self._pFITSHeader = fitsHeader ELSE $
			self._pFITSHeader = PTR_NEW(fitsHeader)

	END

	IF N_ELEMENTS(calibrated) GT 0 THEN BEGIN

		IF PTR_VALID(self._pCalibratedImage) THEN $
			*self._pCalibratedImage = calibrated ELSE $
			self._pCalibratedImage = PTR_NEW(calibrated)

	END

	IF N_ELEMENTS(filteringSequence) GT 0 THEN BEGIN

		IF PTR_VALID(self._pFilteringSequence) THEN $
			*self._pFilteringSequence = filteringSequence ELSE $
			self._pFilteringSequence = PTR_NEW(filteringSequence)

	END

	IF N_ELEMENTS(projCoord) GT 0 THEN BEGIN

		IF PTR_VALID(self._pProjCoord) THEN $
			*self._pProjCoord = projCoord ELSE $
			self._pProjCoord = PTR_NEW(projCoord)

	END

	IF N_ELEMENTS(maskOfNonMissing) GT 0 THEN BEGIN

		IF PTR_VALID(self._pContourOfNonMissing) THEN $
			*self._pContourOfNonMissing = maskOfNonMissing ELSE $
			self._pContourOfNonMissing = PTR_NEW(maskOfNonMissing)

	END

	IF N_ELEMENTS(projectionMask) GT 0 THEN BEGIN

		IF PTR_VALID(self._pProjectedUserDefinedMaskCoord) THEN $
			*self._pProjectedUserDefinedMaskCoord = projectionMask ELSE $
			self._pProjectedUserDefinedMaskCoord = PTR_NEW(projectionMask)

	END

	IF N_ELEMENTS(probe) GT 0 THEN $
		self._probe = probe

	IF N_ELEMENTS(instrument) GT 0 THEN $
		self._instrument = instrument

	IF N_ELEMENTS(oVisuElt) GT 0 THEN BEGIN

		IF OBJ_VALID(oVisuElt) THEN BEGIN

			IF OBJ_ISA(oVisuElt, "FESTIVAL_VISU_ELEMENT") THEN $
				self._oVisuElt = oVisuElt

		END

	END

END

PRO FESTIVAL_DATA::GETPROPERTY, $

					CALIBRATION_ARRAY = calibrationArray, $
					CALIBRATED = pCalibrated, $

					FILTERING_SEQUENCE = pFilteringSequence, $
					FILTERED = pFiltered, $

					ENHANCEMENT = enhancement, $
					ENHANCED = pEnhanced, $

					PROJCOORD = pProjCoord, $

					MASKING = masking, $

					UTCDATE = utcDate, $
					PROBE = probe, $
					INSTRUMENT = instrument, $

					IMAGE_COORDINATES = imageCoordinates, $
					PROJECTED_IMAGE = oProjectedImage, $

					PROJECTION_MASK = pProjectionMask, $
					UVRANGE = uvrange, $

					DELTAOFIMAGEDIFF = deltaOfImageDiff, $
					DISPLAY_ORDER = displayOrder, $
					SIZE_OF_DATA = objSize, $
					FITSHEADER = pFitsHeader, $

					REF_SEARCH_RESULT = oRefSearchResult, $
					PALETTE = oPalette, $

					UVPERPIXEL_RESOLUTION = uvPerPixelResolution, $
					VISU_ELEMENT = oVisuElt, $

					SUN_DISTANCE = sun_distance, $
					SUNDISTANCE_NORMALIZATION = sundistance_normalization, $
					CARRINGTON_LONGITUDE = CarringtonLongitude, $
					CARRINGTON_LATITUDE = CarringtonLatitude, $
					SOLAR_NORTH_ANGLE = SolarNorthAngle, $
					ECLIPTIC_SOLAR_NORTH_ANGLE = EclipticSolarNorthAngle, $

					HAEX = haex, $
					HAEY = haey, $
					HAEZ = haez, $

					POLARITY_TYPE = polarityType, $

                    FILESPECIFICATION = filespec


	IF ARG_PRESENT(calibrationArray) THEN $
		calibrationArray = self._calibrationArray

	IF ARG_PRESENT(pCalibrated) THEN $
		pCalibrated = self._pCalibratedImage

	IF ARG_PRESENT(pFilteringSequence) THEN $
		pFilteringSequence = self._pFilteringSequence

	IF ARG_PRESENT(pFiltered) THEN $
		pFiltered = self._pFilteredImage

	IF ARG_PRESENT(enhancement) THEN $
		enhancement = self._enhancement

	IF ARG_PRESENT(pEnhanced) THEN $
		pEnhanced = self._pEnhancedImage

	IF ARG_PRESENT(pProjCoord) THEN $
		pProjCoord = self._pProjCoord

	IF ARG_PRESENT(masking) THEN $
		masking = self._userMaskParameters

	IF ARG_PRESENT(utcDate) THEN $
		utcDate = self._utcDate

	IF ARG_PRESENT(probe) THEN $
		probe = self._probe

	IF ARG_PRESENT(instrument) THEN $
		instrument = self._instrument

	IF ARG_PRESENT(imageCoordinates) THEN $
		imageCoordinates = self._imageCoordinates

	IF ARG_PRESENT(oProjectedImage) THEN $
		oProjectedImage = self._oProjectedImage

	IF ARG_PRESENT(pProjectionMask) THEN $
		pProjectionMask = self._pProjectedUserDefinedMaskCoord

	IF ARG_PRESENT(uvRange) THEN $
		uvRange = self._UVRange

	IF ARG_PRESENT(deltaOfImageDiff) THEN $
		deltaOfImageDiff = self._deltaOfImageDiff

	IF ARG_PRESENT(displayOrder) THEN $
		displayOrder = self._displayOrder

 	IF ARG_PRESENT(objSize) THEN $
		objSize = float(self._sizeInMemory)/1024.0 ;expressed in Ko

	IF ARG_PRESENT(pFitsHeader) THEN $
		pFitsHeader = self._pFITSHeader

	IF ARG_PRESENT(oRefSearchResult) THEN $
		oRefSearchResult = self._oRefSearchResult

	IF ARG_PRESENT(oPalette) THEN $
		IF OBJ_VALID(self._oProjectedImage) THEN $
			self._oProjectedImage->GETPROPERTY, PALETTE = oPalette

	IF ARG_PRESENT(uvPerPixelResolution) THEN $
		uvPerPixelResolution = self._UVPerPixelResolution

	IF ARG_PRESENT(oVisuElt) THEN $
		oVisuElt = self._oVisuElt

	IF ARG_PRESENT(sun_distance) THEN $
		sun_distance = self._SunDist

	IF ARG_PRESENT(sundistance_normalization) THEN $
		sundistance_normalization = self._SunDistNormalization

	IF ARG_PRESENT(CarringtonLongitude) THEN $
		CarringtonLongitude = self._CarringtonLongitude

	IF ARG_PRESENT(CarringtonLatitude) THEN $
		CarringtonLatitude = self._CarringtonLatitude

	IF ARG_PRESENT(SolarNorthAngle) THEN $
		SolarNorthAngle = self._SolarNorthAngle

	IF ARG_PRESENT(EclipticSolarNorthAngle) THEN $
		EclipticSolarNorthAngle = self._EclipticSolarNorthAngle

	IF ARG_PRESENT(HAEX) THEN $
		haex = self._HAEX

	IF ARG_PRESENT(HAEY) THEN $
		haey = self._HAEY

	IF ARG_PRESENT(HAEZ) THEN $
		haez = self._HAEZ

	IF ARG_PRESENT(polarityType) THEN $
		polarityType = self._polarityType

	IF ARG_PRESENT(filespec) THEN $
		filespec = self._fileSpecification
END

;+
; NAME:
;   FESTIVAL_DATA::GETMISSINGMASK
;
; PURPOSE:
;   Returns a pointer to a byte of "good" values.
;
; INPUTS:
;	None
;
; KEYWORD PARAMETERS:
;
;	None
;
; OUTPUTS:
;
;	A pointer to a byte array of "good" projected images values.
;
; MODIFICATION HISTORY:
;   31/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::GETMISSINGMASK

	RETURN, self._pContourOfNonMissing

END

;+
; NAME:
;   FESTIVAL_DATA::BUILD_PROJ_MASK
;
; PURPOSE:
;
;   Defines the _pProjectedUserDefinedMaskCoord property that will be
;   used to set the position of the data and its mask.
;   If a user mask is defined, its projection is computed.
;   Otherwise, the mask is computed using the "good/missing" contour
;   computed in the CREATE_PROJECTED_IMAGE procedure.
;
; INPUTS:
;	oMaskingGUI
;
; KEYWORD PARAMETERS:
;
;	None
;
; OUTPUTS:
;
;
;
; MODIFICATION HISTORY:
;   31/08/06 - IAS ORSAY
;   23/01/07 - IAS ORSAY Correction of the case where the contour is
;                        build using the "missing/good value" mask.
;                        New UVRange definition is also taken into account
;-
FUNCTION FESTIVAL_DATA::BUILD_PROJ_MASK, oMaskingGUI, oProjcoordGUI

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL

		; Free memory :
		IF PTR_VALID(self._pProjectedUserDefinedMaskCoord) THEN $
			PTR_FREE, self._pProjectedUserDefinedMaskCoord

		; Reset object properties :
		self._userMaskParameters = {FESTIVAL_MASKING}

		RETURN, !FALSE

	END

	IF ~OBJ_VALID(oMaskingGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_MASKING_GUI object was passed"

	IF ~OBJ_ISA(oMaskingGUI, "FESTIVAL_MASKING_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_MASKING_GUI object was passed"

	; Test _oProjectedImage :
	IF ~OBJ_VALID(self._oProjectedImage) THEN $
		MESSAGE, "No valid _oProjectImage buffer available"

	IF ~PTR_VALID(self._pProjCoord) THEN $
		MESSAGE, "No valid _pProjCoord structure available"


	; Get projCoordGUI object properties :
	oProjcoordGUI->GETPROPERTY, ALL = all

	; Get target projCoord structure :
	projCoordStruct = all[self._probe]


	; Get oMaskingGUI properties :
	oMaskingGUI->GETPROPERTY, ALL = all

	; Get target masking structure :
	maskingStruct = all[self._probe, self._instrument]



        projectionON = projCoordStruct._instrument[self._instrument]


        IF !DEBUG EQ !TRUE THEN $
          PRINT, "-------------------------------------------------------------------> BUILD_PROJ_MASK"



	; Check if masking is required :
	IF maskingStruct._maskingON THEN BEGIN

                ; Get image coordinates :
                lon1 = self._imageCoordinates[0]
                lat1 = self._imageCoordinates[1]
                lon2 = self._imageCoordinates[2]
                lat2 = self._imageCoordinates[3]

		; Get normalised coordinates of the center and radii of the mask :
		xCenter = maskingStruct._imageXCenter
		yCenter = maskingStruct._imageYCenter
		percentRadii = maskingStruct._radii


		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, "MASKING PARAMETERS : "
			PRINT, "xCenter = ", xCenter
			PRINT, "yCenter = ", yCenter
			PRINT, "radii = ", percentRadii

		END

		r = ((lon2-lon1) < (lat2-lat1))*percentRadii / 2.0

		; Compute (longitude/latitude) mask :
		angles = (1/!DRADEG)*INDGEN(360)
		lon = lon1 > (lon1 + xCenter*(lon2-lon1) + r*COS(angles)) < lon2
		lat = lat1 > (lat1 + yCenter*(lat2-lat1) + r*SIN(angles)) < lat2

		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, "LONGITUDE : ", MIN(lon), MAX(lon)
			PRINT, "LATITUDE : ", MIN(lat), MAX(lat)

		END



                IF (projectionON EQ !TRUE) THEN BEGIN

                                ; Change it to UV coordinates :
                                ; lat/lon to UV conversion :
                    IF !DEBUG EQ !TRUE THEN $
                      PRINT, "--------------------------------> MASK IS PROJECTED"

                    UVProj = MAP_PROJ_FORWARD(lon, lat, MAP_STRUCTURE = (*self._pProjCoord)._sMap)


                ENDIF ELSE BEGIN

                                ; in this case, lon,lat just needs to
                                ; be interpolated but not really
                                ; projected because image has not been
                                ; projected

                    U_min = self._UVRange[0,0]
                    V_min = self._UVRange[1,0]
                    U_max = self._UVRange[0,2]
                    V_max = self._UVRange[1,2]

					IF !DEBUG EQ !TRUE THEN $
						PRINT, "--------------------------------> MASK IS INTERPOLATED"

                    U_lon = ( ( lon - lon1 ) / ( lon2 - lon1 ) )*( U_max - U_min ) +  U_min
                    V_lat = ( ( lat - lat1 ) / ( lat2 - lat1 ) )*( V_max - V_min ) +  V_min

                    UVProj= transpose([[U_lon],[V_lat]])


                ENDELSE

	; Use mask of "good" values to build _pProjectedUserDefinedMaskCoord :
	END ELSE BEGIN

                                ; in this case, the UV coordinates of
                                ; the mask have already been computed :

		IF ~PTR_VALID(self._pContourOfNonMissing) THEN $
			MESSAGE, "Mask of non missing values is not available"

		UVProj = *self._pContourOfNonMissing

		IF !DEBUG EQ !TRUE THEN $
                  PRINT, "------------------------------> MASK IS BUILD USING NON MISSING DATA AFTER PROJECTION"

	END

	; Store mask contour values into _pProjectedUserDefinedMaskCoord buffer :
	IF N_ELEMENTS(UVProj) EQ 0 THEN $
		MESSAGE, "Projection mask could not be computed"

	; Always set _userMaskParameters property :
	self._userMaskParameters = maskingStruct

	IF PTR_VALID(self._pProjectedUserDefinedMaskCoord) THEN $
		*self._pProjectedUserDefinedMaskCoord = TEMPORARY(UVProj) ELSE $
		self._pProjectedUserDefinedMaskCoord = PTR_NEW(UVProj, /NO_COPY)


	RETURN, !TRUE

END

;+
; NAME:
;   FESTIVAL_DATA::CREATE_PROJECTED_IMAGE
;
; PURPOSE:
;   Create the projected image from the user settings.
;
; INPUTS:
;
;	oProjcoordGUI : object reference to a FESTIVAL_PROJCOORD_GUI object.
;
;	oColourGUI : object reference to a FESTIVAL_COLOUR_GUI object.
;
; KEYWORD PARAMETERS:
;
;	NO_PROJECTION_NEEDED : set this keyword to a non-null value to indicate that no projection should be computed.
;
; OUTPUTS:
;
;	!TRUE if projection was computed OK, !FALSE otherwise.
;
;        Properties affected by this method :
;
;        - self._UVRange : if image is projected, it is set to the
;          projected matrice coordinates (given by MAP_PROJ_IMAGE), otherwise,
;          it is set to the projection of the corners of the data
;
;        - self._oProjectedImage : the projected image or the
;          enhanced image if no projection is performed
;
;        - self._pContourOfNonMissing : (variable contourXY in the
;          code) : set to the contour of the missing/good values if
;          projection has been performed, set to :
;          [[self._UVRange], [self._UVRange[*, 0]]]  otherwise.
;
;        - self._UVPerPixelResolution
;
;        - self._pProjCoord
;
;
; MODIFICATION HISTORY:
;   31/08/06 - IAS ORSAY
;   23/01/07 - IAS ORSAY UVRange definition with 4 points instead of 2
;                        and _pContourOfNonMissing is always in UV
;                        space.
;
;-
FUNCTION FESTIVAL_DATA::CREATE_PROJECTED_IMAGE, oProjcoordGUI, oColourGUI, $
  MISSING = missing, $
  NO_PROJECTION_NEEDED = No_Projection_Needed

	; Error handler :
        err = 0
        CATCH, err
        IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

                    PRINT, !error_state.msg

                END

                CATCH, /CANCEL

		; Free memory :
		IF OBJ_VALID(self._oProjectedImage) THEN $
			OBJ_DESTROY, self._oProjectedImage

		IF PTR_VALID(self._pContourOfNonMissing) THEN $
			PTR_FREE, self._pContourOfNonMissing

		IF PTR_VALID(self._pProjCoord) THEN $
			PTR_FREE, self._pProjCoord

		RETURN, !FALSE

        END

	; Get interpolation method :
	self._oDataEngine->GETPROPERTY, SELECTION_GUI = oSelectionGUI
	IF ~OBJ_VALID(oSelectionGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"

	; Get system GUI :
	oSelectionGUI->GETPROPERTY, SYSTEM_OPTIONS_WIZARD = oSystemGUI
	IF ~OBJ_VALID(oSystemGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SYSTEM_GUI object was found"
	oSystemGUI->GETPROPERTY, INTERPOLATE = interpolate

	; Check for input objects validity :
	IF ~OBJ_VALID(oProjcoordGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_PROJCOORD_GUI object was passed"

	IF ~OBJ_ISA(oProjcoordGUI, "FESTIVAL_PROJCOORD_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_PROJCOORD_GUI object was passed"

	IF ~OBJ_VALID(oColourGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_COLOUR_GUI object was passed"

	IF ~OBJ_ISA(oColourGUI, "FESTIVAL_COLOUR_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_COLOUR_GUI object was passed"

	; Need an enhanced image to be projected :
	IF ~PTR_VALID(self._pEnhancedImage) THEN $
		MESSAGE, "No enhanced image available"

	; Check keywords :
	IF N_ELEMENTS(No_Projection_Needed) EQ 0 THEN $
		No_Projection_Needed = !FALSE

	; Get projCoordGUI object properties :
	oProjcoordGUI->GETPROPERTY, ALL = all

	; Get target projCoord structure :
	projCoordStruct = all[self._probe]

	; Default value for uvrange :
        ; remember that
        ; imageCoordinates = [ lon1, lat1, lon2, lat2 ]
        ; and that UVRange is defined like that :
        ;
        ; C0 = UVRange(*,0) = lower left corner
        ; C1 = UVRange(*,1) = lower right corner
        ; C2 = UVRange(*,2) = upper right corner
        ; C3 = UVRange(*,3) = upper right corner
        ;
        ;    C3 ---- C2
        ;     |      |
        ;     |      |
        ;    C0 ---- C1

       ; These default values will be used if
       ; no projection of the image is done :

	; before projection, saturate latitude in [-89.95, 89.95]

    self._imageCoordinates[1] = self._imageCoordinates[1] >  (-89.5)
	self._imageCoordinates[3] = self._imageCoordinates[3] <  89.5

	self._UVRange = MAP_PROJ_FORWARD(self._imageCoordinates[[0, 2, 2, 0]], $
                                         self._imageCoordinates[[1, 1, 3, 3]], $
                                         MAP = projCoordStruct._sMap)


        ; THE FOLLOWING 'RECTANGULARISATION' MAY BE CHANGED IN THE FUTURE
        ; BUT NORMALISATION WOULD HAVE TO BE REDEFINED :

        ; we computed the 'equivalent' rectangle image
        ; Rectangle position is correctly centered

        C0 = self._UVRange[*,0]
        C1 = self._UVRange[*,1]
        C2 = self._UVRange[*,2]
        C3 = self._UVRange[*,3]

        u_min = (C0[0]+C3[0])/2.0
        u_max = (C1[0]+C2[0])/2.0
        v_min = (C0[1]+C1[1])/2.0
        v_max = (C2[1]+C3[1])/2.0

        self._UVRange = [[u_min,v_min],[u_max,v_min],[u_max,v_max],[u_min,v_max]]

        ; END OF 'RECTANGULARISATION'

	; Default contour of "good" values :
	contourXY = [[self._UVRange], [self._UVRange[*, 0]]]

	; Case where projection has not been prohibited :

	IF ((No_Projection_Needed EQ !FALSE) AND (all[self._probe]._smap.up_name NE 'Equirectangular')) THEN BEGIN

            projectionON = projCoordStruct._instrument[self._instrument]

            ; Need to project :
            IF (projectionON EQ !TRUE) THEN BEGIN

                IF !DEBUG EQ !TRUE THEN $
                  PRINT, "-------------------------------------------------------------------> IMAGE IS PROJECTED"

                projectedBuffer = MAP_PROJ_IMAGE($

                                ; IN :
                                  *self._pEnhancedImage, $
                                  self._imageCoordinates, $
                                  MAP_STRUCTURE = projCoordStruct._sMap, $

                                ; OUT :
                                  UVRANGE = uvrangeImage, $
                                  MASK = mask, $

                                  /BILINEAR)

                                ; uvrangeImage contains the range of the UV coordinates of the Result
                                ; in a four-element array of the form [U min, V min, U max, V max]

		; We're not interested by the mask, but by its contours :
                ; Find boundaries :

                indices =  WHERE(mask GT 0, count, NCOMPLEMENT = nComp)

                IF ~count THEN $
                  MESSAGE, "Mask of non missing values is incorrect"


                ; If some values are missing, build new contour

                IF (nComp NE 0) THEN BEGIN

                    info = SIZE(mask)
                    nCols = info[1]
                    nRows = info[2]

                                ; Call FIND_BOUNDARY :
                    contourXY = FIND_BOUNDARY(indices, XSIZE = nCols, YSIZE = nRows)
                    contourXY = [[contourXY], [contourXY[*, 0]]]

                    ; now contourXY needs to be translated in UV coordinates :

                    ; Get projected matrice coordinates :
                    u1 = uvrangeImage[0]
                    v1 = uvrangeImage[1]
                    u2 = uvrangeImage[2]
                    v2 = uvrangeImage[3]

                                ; Find XMin, XMax :
                    xMin = MIN(contourXY[0, *], MAX = xMax)
                    xFac = (u2-u1) / (xMax-xMin)

                                ; Find YMin, YMax :
                    yMin = MIN(contourXY[1, *], MAX = yMax)
                    yFac = (v2-v1) / (yMax-yMin)

                                ; Change it to longitude, latitude :
                    u_tab = REFORM(contourXY[0, *] * xFac + u1)
                    v_tab = REFORM(contourXY[1, *] * yFac + v1)

                    contourXY = TRANSPOSE([[u_tab], [v_tab]])


                END ELSE BEGIN
                    ; the projection did not produce any missing value

                    contourXY = [ [uvrangeImage[[0, 1]]],$
                                  [uvrangeImage[[2, 1]]],$
                                  [uvrangeImage[[2, 3]]],$
                                  [uvrangeImage[[0, 3]]],$
                                  [uvrangeImage[[0, 1]]] ]
                ENDELSE ; End if test to know if values are missing or not

                ; now that projection is done,
                ; store the coordinates of the projected matrice
                ; which is not equivalent to the UV coordinates
                ; of the initial image corners (the projection
                ; of the corners may not be a rectangle, this result is
                ; by construction a rectangle)

                self._UVRange = [ [uvrangeImage[[0, 1]]],$
                                  [uvrangeImage[[2, 1]]],$
                                  [uvrangeImage[[2, 3]]],$
                                  [uvrangeImage[[0, 3]]] ]

                ; This property will be used to normalize the texture_coord
                ; attribute of the visu_element, i.e. to normalize the
                ; user mask



            END ELSE BEGIN ; END OF CASE (projectionON EQ !TRUE)

                ; case where the user chose the projection OFF

                projectedBuffer = *self._pEnhancedImage

                IF !DEBUG EQ !TRUE THEN $
                  PRINT, "-------------------------------------------------------------------> IMAGE IS NOT PROJECTED"

            ENDELSE

        END ELSE BEGIN ; END OF CASE ((~No_Projection_Needed) EQ !TRUE)

            projectedBuffer = *self._pEnhancedImage

            IF !DEBUG EQ !TRUE THEN $
              PRINT, "-------------------------------------------------------------------> IMAGE IS NOT PROJECTED"

        ENDELSE


                                ; Build an IDLGRIMAGE object out of projectedBuffer :
        IF N_ELEMENTS(projectedBuffer) EQ 0 THEN $
          MESSAGE, "The projected image could not be computed"

		self._oRefSearchResult->GETPROPERTY, F_FILTER = filter, F_WAVE = wave

		deltaOfImageDiff = self._deltaOfImageDiff NE 0

		; Check waves array :
		IF PTR_VALID(!WAVES_ARRAY[self._probe, self._instrument]) THEN BEGIN

			; Get the waves :
			waves = *!WAVES_ARRAY[self._probe, self._instrument]

			; Is there an 'All' string ?
			IF waves[N_ELEMENTS(waves)-1] EQ 'All' THEN BEGIN

				; 'All' is at the first position :
				IF waves[0] EQ 'All' THEN $
					vecWave = 0 ELSE BEGIN

						; Remove extra 'All' string :
						waves = waves[0:(N_ELEMENTS(waves)-2)]

						; Find target wave :
						vecWave = WHERE(STRUPCASE(waves) EQ STRUPCASE(wave)) > 0

				END

				; No 'All' at the end :
			END ELSE $

				; If no wave is found, uses vecWave = 0.
				vecWave = WHERE(waves EQ wave) > 0

		END ELSE $
			MESSAGE, "The !WAVES_ARRAY array does not contain valid information"

		IF PTR_VALID(!FILTERS_ARRAY[self._probe, self._instrument]) THEN BEGIN

			; Get the filters :
			filters = *!FILTERS_ARRAY[self._probe, self._instrument]

			; Perform comparison : if no filter is found, uses vecFilter = 0
			vecFilter = WHERE(STRUPCASE(filters) EQ STRUPCASE(filter), countFilter) > 0

		END ELSE $
			MESSAGE, "The !FILTERS_ARRAY array does not contain valid information"

		; Build the default palette if necessary :
		oColourGUI->GETPROPERTY, ALL = allPalettes
		IF ~OBJ_VALID(allPalettes[self._probe, self._instrument, $
		vecFilter[0], vecWave[0], deltaOfImageDiff]) THEN $
			oColourGUI->CREATE_PALETTE, self._probe, self._instrument, $
			vecFilter[0], vecWave[0], deltaOfImageDiff
		oColourGUI->GETPROPERTY, ALL = allPalettes

		; Update object properties :
        IF OBJ_VALID(self._oProjectedImage) THEN $
        	self._oProjectedImage->SETPROPERTY, DATA = TEMPORARY(projectedBuffer), $
            PALETTE = allPalettes[self._probe, self._instrument, vecFilter[0], vecWave[0], deltaOfImageDiff] ELSE $
          	self._oProjectedImage = OBJ_NEW("IDLGRIMAGE", TEMPORARY(projectedBuffer), $
          	PALETTE = allPalettes[self._probe, self._instrument, vecFilter[0], vecWave[0], deltaOfImageDiff], $
          	INTERPOLATE = interpolate)

        ;WARNING : MAYBE TO BE CHANGED TO BE COMPLETELY CORRECT.
        self._oProjectedImage->GETPROPERTY, DIMENSIONS = dims
        self._UVPerPixelResolution = (self._uvRange[*,2]-self._uvRange[*,0])/DOUBLE(dims)

                                ; Store byte array of "good" values in a pointer :
        IF PTR_VALID(self._pContourOfNonMissing) THEN $
          *self._pContourOfNonMissing = contourXY ELSE $
          self._pContourOfNonMissing = PTR_NEW(contourXY, /NO_COPY)

                                ; Store projection/coordinates structure :
        IF PTR_VALID(self._pProjCoord) THEN $
          *self._pProjCoord = TEMPORARY(projCoordStruct) ELSE $
          self._pProjCoord = PTR_NEW(projCoordStruct, /NO_COPY)

        IF (No_Projection_Needed EQ !TRUE)  THEN BEGIN
            ; if data is just computed in order to build a difference,
            ; record the information.
            ; Otherwise, COMPUTE_DATA_SIMILARITY would think it has been projected
            (*self._pProjCoord)._instrument[self._instrument]=!FALSE
        ENDIF

                                ; Update size in memory :
        self->UPDATESIZEINMEMORY

        RETURN, !TRUE

    END

;+
; NAME:
;   FESTIVAL_DATA::MAKE_DIFF
;
; PURPOSE:
;   Compute the difference between two FESTIVAL_DATA objects.
;
; INPUTS:
;
;	oFestivalData1 : first FESTIVAL_DATA object.
;
;	oFestivalData2 : second FESTIVAL_DATA object. Difference is computed by oFestivalData2-oFestivalData1.
;
;	oFestivalFilterGUI : a reference to a FESTIVAL_FILTER_GUI object.
;
;	oFestivalCalibrationGUI : a reference to a FESTIVAL_CALIBRATION_GUI object.
;
;	oFestivalProjCoordGUI : a reference to a FESTIVAL_PROJCOORD_GUI object.
;
; KEYWORD PARAMETERS:
;	None
;
; OUTPUTS:
;	A FESTIVAL_DATA difference object.
;
; MODIFICATION HISTORY:
;   31/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::MAKE_DIFF, oFestivalData1, oFestivalData2, $
								   oFestivalFilterGUI, $
								   oFestivalCalibrationGUI, $
								   oFestivalProjCoordGUI

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL
		RETURN, !FALSE

	END

	IF !DEBUG EQ !TRUE THEN $
		PRINT, "-------------------------------------------------------------------> MAKE_DIFF"

	; Check probe/instrument values :
	oFestivalData1->GETPROPERTY, PROBE = probe1, INSTRUMENT = instrument1, $
	FILTERING_SEQUENCE = pFilteringSequence1, CALIBRATION_ARRAY = calibrationArray1, $
	PROJCOORD = pProjCoord1, FILTERED = pFiltered1, UTCDATE = utcDate1, $
	POLARITY_TYPE = polarityType1

	oFestivalData2->GETPROPERTY, PROBE = probe2, INSTRUMENT = instrument2, $
	FILTERING_SEQUENCE = pFilteringSequence2, CALIBRATION_ARRAY = calibrationArray2, $
	PROJCOORD = pProjCoord2, FILTERED = pFiltered2, UTCDATE = utcDate2, $
	POLARITY_TYPE = polarityType2

	; These errors are directly thrown by the catch :
	IF probe1 NE probe2 OR instrument1 NE instrument2 THEN $
		MESSAGE, "Incompatible probe/instrument FESTIVAL_DATA objects"

	IF ~PTR_VALID(pFiltered1) OR ~PTR_VALID(pFiltered2) THEN $
		MESSAGE, "Cannot compute object difference as one or both _pFilteredImages buffers are empty"

;	; **********************************************************************************
;
;	; Check oFestival1, oFestival2, oFestivalFilterGUI compatibility :
;	oFestivalFilterGUI->GETPROPERTY, ALL = all
;
;	; Get the target property :
;	targetProp = all[probe1, instrument1]
;
;	array0 = ''
;	IF PTR_VALID(targetProp._pFilteringSequence) THEN $
;		array0 = STRCOMPRESS(STRUPCASE(*targetProp._pFilteringSequence), /REMOVE_ALL)
;	array1 = ''
;	IF PTR_VALID(pFilteringSequence1) THEN $
;		array1 = STRCOMPRESS(STRUPCASE(*pFilteringSequence1), /REMOVE_ALL)
;	array2 = ''
;	IF PTR_VALID(pFilteringSequence2) THEN $
;		array2 = STRCOMPRESS(STRUPCASE(*pFilteringSequence2), /REMOVE_ALL)
;
;	; Check for array equality :
;	IF ~(ARRAY_EQUAL(array0, array1) AND ARRAY_EQUAL(array1, array2)) THEN $
;		MESSAGE, "Incompatible FESTIVAL_FILTER_GUI/FESTIVAL_DATA objects"

	; **********************************************************************************

	; Check oFestival1, oFestival2, oFestivalCalibrationGUI compatibility :
	oFestivalCalibrationGUI->GETPROPERTY, ALL = all

	; Get the target property :
	targetProp = all[probe1, instrument1]

	; Check for calibration models equality :
	IF ~(TESTSTRUCTEQUALITY(targetProp, calibrationArray1) AND TESTSTRUCTEQUALITY(calibrationArray1, calibrationArray2)) THEN $
		MESSAGE, "Incompatible FESTIVAL_CALIBRATION_GUI/FESTIVAL_DATA objects"

	; **********************************************************************************

	; Check oFestivalData1, oFestivalData2, oFestivalProjCoordGUI compatibility :
	oFestivalProjCoordGUI->GETPROPERTY, ALL = all

	; Get the target property :
	targetProp = all[probe1]

	IF ~(targetProp._coordSystem EQ (*pProjCoord1)._coordSystem AND $
	targetProp._coordSystem EQ (*pProjCoord2)._coordSystem) THEN $
		MESSAGE, "Incompatible coordinate systems"

	; Copy objects by assignment :
	STRUCT_ASSIGN, oFestivalData2, self

	; Remove unwanted items : :

	; Reset _oProjectedImage property :
	self._oProjectedImage = OBJ_NEW()

	; Reset _pEnhancedImage property :
	self._pEnhancedImage = PTR_NEW()

	; Reset _pFITSHeader property :
	self._pFITSHeader = PTR_NEW()
	oFestivalData2->GETPROPERTY, FITSHEADER = pFitsHeader
	IF PTR_VALID(pFitsHeader) THEN $
		self._pFITSHeader = PTR_NEW(*pFitsHeader)

	; Reset _pCalibratedImage property :
	self._pCalibratedImage = PTR_NEW()

	; Reset _pFilteringSequence :
	self._pFilteringSequence = PTR_NEW()
	oFestivalData2->GETPROPERTY, FILTERING_SEQUENCE = pFilteringSequence
	IF PTR_VALID(pFilteringSequence) THEN $
		self._pFilteringSequence = PTR_NEW(*pFilteringSequence)

	; Reset _pProjCoord :
	self._pProjCoord = PTR_NEW()
	oFestivalData2->GETPROPERTY, PROJCOORD = pProjCoord
	IF PTR_VALID(pProjCoord) THEN $
		self._pProjCoord = PTR_NEW(*pProjCoord)

	; Reset _pContourOfNonMissing :
	self._pContourOfNonMissing = PTR_NEW()

	; Reset _pProjectedUserDefinedMaskCoord :
	self._pProjectedUserDefinedMaskCoord = PTR_NEW()

	; Compute _pFilteredImage :
	self._pFilteredImage = PTR_NEW(*pFiltered2 - *pFiltered1)

	; Set _UTCDate property :
	self._UTCDate = utcDate2

	; Set _deltaOfImageDiff property :
	self._deltaOfImageDiff = utcDate2-utcDate1

	oFestivalData2->GETPROPERTY, REF_SEARCH_RESULT = oSearchResult2
	IF ~OBJ_VALID(oSearchResult2) THEN $
		MESSAGE, "A FESTIVAL_DATA object with an invalid associated FESTIVAL_SEARCH_RESULT object was found"

	oSearchResult2->SETPROPERTY, FESTIVAL_DATA = self

	RETURN, !TRUE

END

PRO FESTIVAL_DATA::RESETFILTEREDIMAGE

	self._pFilteredImage = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETENHANCEDPROJECTEDIMAGE

	self._oProjectedImage = OBJ_NEW()

END

PRO FESTIVAL_DATA::RESETENHANCEDIMAGE

	self._pEnhancedImage = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETFITSHEADER

	self._pFITSHeader = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETCALIBRATEDIMAGE

	self._pCalibratedImage = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETFILTERINGSEQUENCE

	self._pFilteringSequence = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETPROJCOORD

	self._pProjCoord = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETMASKOFNONMISSING

	self._pContourOfNonMissing = PTR_NEW()

END

PRO FESTIVAL_DATA::RESETPROJECTEDUSERDEFINEDMASKCOORD

	self._pProjectedUserDefinedMaskCoord = PTR_NEW()

END


;+
; NAME:
;   FESTIVAL_DATA::COPY
;
; PURPOSE:
;   Copy this object to a new object.
;
; INPUTS:
;   None
;
; KEYWORD PARAMETERS:
;
;	WITH_FILTERED_IMAGE : also copies the _pFilteredImage buffer.
;
;	WITH_ENHANCED_PROJECTED_IMAGE : also copies the _oProjectedImage object.
;
; OUTPUTS:
;
;	A copy of self.
;
; MODIFICATION HISTORY:
;   31/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::COPY, $
					WITH_FILTERED_IMAGE = withFilteredImage, $
					WITH_ENHANCED_PROJECTED_IMAGE = withEnhancedProjectedImage

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		IF OBJ_VALID(out) THEN $
			OBJ_DESTROY, out

		CATCH, /CANCEL

		; Return a NULL object :
		RETURN, OBJ_NEW()

	END

	IF !DEBUG EQ !TRUE THEN $
		PRINT, "-------------------------------------------------------------------> COPY"

	; Create an "empty" output object :
	out = OBJ_NEW("FESTIVAL_DATA", DATA_ENGINE = self._oDataEngine)

	; Perform copy :
   	STRUCT_ASSIGN, self, out

	; Reset fields :
   	out->RESETFILTEREDIMAGE
   	out->RESETENHANCEDPROJECTEDIMAGE
   	out->RESETENHANCEDIMAGE
   	out->RESETFITSHEADER
   	out->RESETCALIBRATEDIMAGE
   	out->RESETFILTERINGSEQUENCE
   	out->RESETPROJCOORD
   	out->RESETMASKOFNONMISSING
   	out->RESETPROJECTEDUSERDEFINEDMASKCOORD

	; Copy of filtered image is required :
	IF KEYWORD_SET(withFilteredImage) THEN BEGIN

		IF PTR_VALID(self._pFilteredImage) THEN $
			out->SETPROPERTY, FILTERED = *self._pFilteredImage

	END

	IF KEYWORD_SET(withEnhancedProjectedImage) THEN BEGIN

		IF OBJ_VALID(self._oProjectedImage) THEN $
			out->SETPROPERTY, PROJECTED_IMAGE = self._oProjectedImage

	END

	IF PTR_VALID(self._pEnhancedImage) THEN $
		out->SETPROPERTY, ENHANCED = *self._pEnhancedImage

  	IF PTR_VALID(self._pFITSHeader) THEN $
  		out->SETPROPERTY, FITSHEADER = *self._pFITSHeader

	IF PTR_VALID(self._pCalibratedImage) THEN $
		out->SETPROPERTY, CALIBRATED = *self._pCalibratedImage

	IF PTR_VALID(self._pFilteringSequence) THEN $
		out->SETPROPERTY, FILTERING_SEQUENCE = *self._pFilteringSequence

	IF PTR_VALID(self._pProjCoord) THEN $
		out->SETPROPERTY, PROJCOORD = *self._pProjCoord

	IF PTR_VALID(self._pContourOfNonMissing) THEN $
		out->SETPROPERTY, MASK_OF_NONMISSING = *self._pContourOfNonMissing

	IF PTR_VALID(self._pProjectedUserDefinedMaskCoord) THEN $
		out->SETPROPERTY, PROJECTION_MASK = *self._pProjectedUserDefinedMaskCoord

	; Return copy :
	RETURN, out

END

;+
; NAME:
;   FESTIVAL_DATA::UPDATESIZEINMEMORY
;
; PURPOSE:
;   Update object size in memory.
;
; INPUTS:
;   None
;
; KEYWORD PARAMETERS:
;   None
;
; OUTPUTS:
;   None
;
; MODIFICATION HISTORY:
;   30/08/06 - IAS ORSAY
;-
PRO FESTIVAL_DATA::UPDATESIZEINMEMORY

	self._sizeInMemory = 0L

	IF PTR_VALID(self._pCalibratedImage) THEN $
		self._sizeInMemory = self._sizeInMemory + 4*N_ELEMENTS(*self._pCalibratedImage)

	IF PTR_VALID(self._pFilteredImage) THEN $
		self._sizeInMemory = self._sizeInMemory + 4*N_ELEMENTS(*self._pFilteredImage)

	IF PTR_VALID(self._pEnhancedImage) THEN $
		self._sizeInMemory = self._sizeInMemory + N_ELEMENTS(*self._pEnhancedImage)

	; Get FAST_DISPLAY mode :
	self._oDataEngine->GETPROPERTY, SELECTION_GUI = oSelectionGUI
	IF ~OBJ_VALID(oSelectionGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"
	oSelectionGUI->GETPROPERTY, SYSTEM_OPTIONS_WIZARD = oSystemGUI
	IF ~OBJ_VALID(oSystemGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SYSTEM_GUI object was found"
	oSystemGUI->GETPROPERTY, FAST_DISPLAY = fastDisplay, $
	REMOVE_FILTERED_IMAGES_FROM_STACK = removeFilteredImagesFromStack

	IF OBJ_VALID(self._oProjectedImage) AND PTR_VALID(self._pFilteredImage) THEN BEGIN

            self._sizeInMemory = self._sizeInMemory + N_ELEMENTS(*self._pFilteredImage)

            IF fastDisplay THEN $
              self._sizeInMemory = self._sizeInMemory*2

        ENDIF

        IF removeFilteredImagesFromStack THEN BEGIN

            IF OBJ_VALID(self._oProjectedImage) AND ~PTR_VALID(self._pFilteredImage) THEN BEGIN

                self._oProjectedImage -> getproperty, DIMENSIONS = dims
                self._sizeInMemory = dims[0]*dims[1]

                IF fastDisplay THEN $
                  self._sizeInMemory = self._sizeInMemory*5

            ENDIF
        ENDIF



	IF !DEBUG EQ !TRUE THEN $
		PRINT, "------------------------------------------------------------------------------------> Size of object in memory : " + STRTRIM(STRING(self._sizeInMemory), 2)

END

;+
; NAME:
;   FESTIVAL_DATA::CLEAN_BEFORE_PUSH
;
; PURPOSE:
;   Destroy object properties that don't need to be stored in the history stack.
;
; INPUTS:
;   None
;
; KEYWORD PARAMETERS:
;   None
;
; OUTPUTS:
;   None
;
; MODIFICATION HISTORY:
;   30/08/06 - IAS ORSAY
;-
PRO FESTIVAL_DATA::CLEAN_BEFORE_PUSH

	; Get FAST_DISPLAY mode :
	self._oDataEngine->GETPROPERTY, SELECTION_GUI = oSelectionGUI
	IF ~OBJ_VALID(oSelectionGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"
	oSelectionGUI->GETPROPERTY, SYSTEM_OPTIONS_WIZARD = oSystemGUI
	IF ~OBJ_VALID(oSystemGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SYSTEM_GUI object was found"
	oSystemGUI->GETPROPERTY, REMOVE_FILTERED_IMAGES_FROM_STACK = removeFilteredImagesFromStack

	; Free _pCalibratedImage :
	IF PTR_VALID(self._pCalibratedImage) THEN $
		PTR_FREE, self._pCalibratedImage

	; Free _pEnhancedImage :
	IF PTR_VALID(self._pEnhancedImage) THEN $
		PTR_FREE, self._pEnhancedImage

	; If specified by user, free _pFilteredImage :
        IF removeFilteredImagesFromStack THEN $
          IF PTR_VALID(self._pFilteredImage) THEN $
            PTR_FREE, self._pFilteredImage

	; Update size in memory :
	self->UPDATESIZEINMEMORY

END

;+
; NAME:
;   FESTIVAL_DATA::CLEAN_BEFORE_PUSH
;
; PURPOSE:
;   Destroy object properties that don't need to be stored in the history stack.
;
; INPUTS:
;   None
;
; KEYWORD PARAMETERS:
;   None
;
; OUTPUTS:
;   None
;
; MODIFICATION HISTORY:
;   30/08/06 - IAS ORSAY
;-
PRO FESTIVAL_DATA::CLEAN_BEFORE_PUSH

	; Get FAST_DISPLAY mode :
	self._oDataEngine->GETPROPERTY, SELECTION_GUI = oSelectionGUI
	IF ~OBJ_VALID(oSelectionGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"
	oSelectionGUI->GETPROPERTY, SYSTEM_OPTIONS_WIZARD = oSystemGUI
	IF ~OBJ_VALID(oSystemGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_SYSTEM_GUI object was found"
	oSystemGUI->GETPROPERTY, REMOVE_FILTERED_IMAGES_FROM_STACK = removeFilteredImagesFromStack

	; Free _pCalibratedImage :
	IF PTR_VALID(self._pCalibratedImage) THEN $
		PTR_FREE, self._pCalibratedImage

	; Free _pEnhancedImage :
	IF PTR_VALID(self._pEnhancedImage) THEN $
		PTR_FREE, self._pEnhancedImage

	; If specified by user, free _pFilteredImage :
        IF removeFilteredImagesFromStack THEN $
          IF PTR_VALID(self._pFilteredImage) THEN $
            PTR_FREE, self._pFilteredImage

	; Update size in memory :
	self->UPDATESIZEINMEMORY

END

FUNCTION FESTIVAL_DATA::ROTATE, oFestivalProjCoordGUI

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL

		RETURN, !FALSE

	END

	IF !DEBUG EQ !TRUE THEN $
		PRINT, "-------------------------------------------------------------------> ROTATE"

	; An invalid object was passed :
	IF ~OBJ_VALID(oFestivalProjCoordGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_PROJCOORD_GUI object was passed"

	; A wrong object was passed :
	IF ~OBJ_ISA(oFestivalProjCoordGUI, "FESTIVAL_PROJCOORD_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_PROJCOORD_GUI object was passed"

	; Check FITS header availability :
	IF ~PTR_VALID(self._pFITSHeader) THEN $
		MESSAGE, "No valid FITS header is associated to that image"

	; Check _pCalibratedImage buffer :
	IF ~PTR_VALID(self._pCalibratedImage) THEN $
		MESSAGE, "No valid calibrated image available"

	; Check projection/coordinates property validity :
	IF ~PTR_VALID(self._pProjCoord) THEN $
		MESSAGE, "Invalid _pProjCoord property"

	IF ~PTR_VALID(self._pFITSHeader) THEN $
		MESSAGE, "No valid FITS header available"

   	; Check requested coordinate system :

	; Get object properties :
	oFestivalProjCoordGUI->GETPROPERTY, ALL = all

	; Get target structure :
	projCoordStruct = all[self._probe]

	; Default value for rotated buffer :
	rotated = *self._pCalibratedImage

    ; Compare coordinate systems :

	CASE projCoordStruct._coordSystem OF

		!HELIOCENTRIC_SOLAR_NORTH_UP: rotAngle = self._SolarNorthAngle*!DDTOR

		!HELIOCENTRIC_ECLIPTIC: BEGIN

			;Computes direction of ecliptic north in local instrument heliocentric frame

			dk = self._EclipticSolarNorthAngle*!DDTOR

			ecl_lat = ASIN(self._haez / SQRT(self._haex^2.0d + self._haey^2.0d + self._haez^2.0d)) ;observer ecliptic latitutde

			x = COS(ecl_lat)*COS(dk)	;coordinates of ecliptic north rotated in heliocentric frame
			y = COS(ecl_lat)*SIN(dk)
			z = -SIN(ecl_lat)

			lon = ATAN(y, z) - self._CRVAL1*!DDTOR	;conversion to instrument frame
			COSlat = COS(ASIN(x))

			y = COSlat*SIN(lon)
			x = TEMPORARY(x)*COS(self._CRVAL2*!DDTOR) - COSlat*COS(lon)*SIN(self._CRVAL2*!DDTOR)

			rotAngle = self._SolarNorthAngle*!DDTOR + ATAN(x, y) - !DPI/2.0

		END

	 	ELSE: MESSAGE, "Unknown coordinate system"

	END

	; Computes rotation.

;	IF ABS(rotAngle) GT !ROTATION_THRESHOLD_ANGLE*!DDTOR THEN BEGIN

		img_size = SIZE(rotated)
		nx = img_size[1]
		ny = img_size[2]

		xc = nx/2.0
		yc = ny/2.0

        dq = self._CRVAL1*!DDTOR
        dp = self._CRVAL2*!DDTOR

		CASE projCoordStruct._coordSystem OF

			!HELIOCENTRIC_SOLAR_NORTH_UP:

			!HELIOCENTRIC_ECLIPTIC: BEGIN

				;dk = self._EclipticSolarNorthAngle*!DDTOR  defined above

				z = COS(dp)*COS(dq)
				yp = COS(dp)*SIN(dq)
				xp = SIN(dp)
				x = xp*COS(dk) - yp*SIN(dk)
				y = xp*SIN(dk) + yp*COS(dk)

				dp = ASIN(x)
				dq = ATAN(y, z)

			END

		 	ELSE: MESSAGE, "Unknown coordinate system"

		END

		;IF !FULL_ROTATION_ACCURACY[self._PROBE, self._INSTRUMENT] EQ !TRUE THEN BEGIN
		IF projCoordStruct._Instrument[self._Instrument] EQ !TRUE THEN BEGIN

			plon = (self._CDELT1/self._SunDistNormalization)*([0, nx-1, xc, xc] - self._CRPIX1)*!DDTOR/3600.0
			plat = (self._CDELT2/self._SunDistNormalization)*([yc, yc, 0, ny-1] - self._CRPIX2)*!DDTOR/3600.0

			zp = COS(plat)*COS(plon)
			yp = COS(plat)*SIN(plon)
			xp = SIN(plat)

			x = xp*COS(rotAngle) + yp*SIN(rotAngle)
			y = -xp*SIN(rotAngle) + yp*COS(rotAngle)

			xp = x*COS(dp) + zp*SIN(dp)
			zp = -x*SIN(dp) + TEMPORARY(zp)*COS(dp)

			plon = ATAN(yp, zp) + dq
			plat = ASIN(xp)

			lonmin = MIN(plon)
			lonmax = MAX(plon)
			latmin = MIN(plat)
			latmax = MAX(plat)

			; Updates image coordinates
			self._imageCoordinates = [lonmin, latmin, lonmax, latmax]*!DRADEG

            lon = lonmin + (lonmax - lonmin)*FINDGEN(nx)/nx - dq
			lat = latmin + (latmax - latmin)*FINDGEN(ny)/ny

			COSLon = COS(lon) # REPLICATE(1.0, ny)	;matrices of longitudes/latitudes
			SINLon = SIN(lon) # REPLICATE(1.0, ny)
			COSLat = REPLICATE(1.0, nx) # COS(lat)
			SINLat = REPLICATE(1.0, nx) # SIN(lat)

			z = COSLat*COSLon	;rotation in longitude
			y = TEMPORARY(COSLat)*TEMPORARY(SINLon)

			xp = SINLat*COS(dp) - z*SIN(dp)	;rotation in latitude
			z = TEMPORARY(SINLat)*SIN(dp) + TEMPORARY(z)*COS(dp)

			x = xp*COS(rotAngle) - y*SIN(rotAngle)	;rotation around local Z axis
			y = TEMPORARY(xp)*SIN(rotAngle) + TEMPORARY(y)*COS(rotAngle)

			SWITCH self._probe OF	;conversion to pixels (with distortion correction or not)

				!SOHO: BEGIN

					SWITCH self._INSTRUMENT OF

						!EIT:

						!NRH:

						!MK4:

						!TRACE:

						!XRT:

						!C1: BEGIN

							rotated = INTERPOLATE(TEMPORARY(rotated), $
								self._CRPIX1 + 3600.0*ATAN(y, z)*!DRADEG/(self._CDELT1/self._SunDistNormalization), $
								self._CRPIX2 + 3600.0*ASIN(x)*!DRADEG/(self._CDELT2/self._SunDistNormalization), $
								MISSING = !MISSING_VALUE)

							BREAK

						END

						!C2:

						!C3: BEGIN

							IF self._INSTRUMENT EQ !C2 THEN $
								Cf = [-9.5479180d-015, 1.0051345, -7.8216544d-011, -16.209086] $
							ELSE $
								Cf = [-5.3290705d-015, 0.98483431, -1.8189894d-012, 0.98990313]

							plate = !DDTOR*(self._CDELT1/self._SunDistNormalization)/3600.0 ;radians/pixel

							theta = ACOS(z)
							phi = ATAN(x, y)

							rho = Cf[0] + theta*(Cf[1] + theta*(Cf[2] + theta*Cf[3]))

							rotated = INTERPOLATE(TEMPORARY(rotated), $
								self._CRPIX1 + rho * COS(phi) / plate, $
								self._CRPIX2 + rho * SIN(phi) / plate, $
								MISSING = !MISSING_VALUE)

							BREAK

						END

						; Undefined :
						ELSE: MESSAGE, "Unknown SOHO instruments"

					ENDSWITCH

					BREAK

				END

				!STEREOA:

				!STEREOB: BEGIN

					SWITCH self._INSTRUMENT OF

						!EUVI:

						!COR1: BEGIN

							rotated = INTERPOLATE(TEMPORARY(rotated), $
								self._CRPIX1 + 3600.0*ATAN(y, z)*!DRADEG/(self._CDELT1/self._SunDistNormalization), $
								self._CRPIX2 + 3600.0*ASIN(x)*!DRADEG/(self._CDELT2/self._SunDistNormalization), $
								MISSING = !MISSING_VALUE)

							BREAK

						END

						!COR2: BEGIN

							IF self._Probe EQ !STEREOA THEN $ ; COR2A coeffs (pixels)
    							Cf = [1.7505732e-005,1.0080725, -0.15074487, 0.045028273, -0.015651654] $
    						ELSE $
    							Cf = [-0.00034103570, 1.0207708, -0.29248398, 0.16572936, -0.098049484]

							plate = !DDTOR*(self._CDELT1/self._SunDistNormalization)/3600.0 ;radians/pixel

							theta = ACOS(z)
							phi = ATAN(x, y)

							rho = Cf[0] + theta*(Cf[1] + theta*(Cf[2] + theta*(Cf[3] + theta*Cf[4])))

							rotated = INTERPOLATE(TEMPORARY(rotated), $
								self._CRPIX1 + rho * COS(phi) / plate, $
								self._CRPIX2 + rho * SIN(phi) / plate, $
								MISSING = !MISSING_VALUE)

							BREAK

						END

						!HI1:

					  	!HI2: BEGIN

					  		plate = !DDTOR*(self._CDELT1/self._SunDistNormalization)/3600.0 ;radians/pixel
					  		mu = (*self._pFITSHeader).PV2_1

                            distortion = (mu + 1.0)/(mu + z)
 							;distortion = (mu + 1.0)*SIN(theta)/(mu + COS(theta))

							rotated = INTERPOLATE(TEMPORARY(rotated), $
							self._CRPIX1 + distortion * y / plate, $
							self._CRPIX2 + distortion * x / plate, $
							MISSING = !MISSING_VALUE)

							BREAK

						END

						; Undefined :
						ELSE: MESSAGE, "Unknown STEREO instruments"

					ENDSWITCH

					BREAK

				END

				; Undefined :
				ELSE: MESSAGE, "Unknown probe"

			ENDSWITCH

		ENDIF ELSE BEGIN

			xs = self._CRPIX1 - 3600.0*self._CRVAL1/(self._CDELT1/self._SunDistNormalization) - xc
    		ys = self._CRPIX2 - 3600.0*self._CRVAL2/(self._CDELT2/self._SunDistNormalization) - yc
    		SunX = xs*COS(rotAngle) - ys*SIN(rotAngle) + xc
    		SunY = xs*SIN(rotAngle) + ys*COS(rotAngle) + yc

			self._imageCoordinates = [-SunX*self._CDELT1/self._SunDistNormalization, -SunY*self._CDELT2/self._SunDistNormalization, $
				(nx - SunX)*self._CDELT1/self._SunDistNormalization, (ny - SunY)*self._CDELT2/self._SunDistNormalization]/3600.

  			rotated = ROT(TEMPORARY(rotated), -rotAngle*!DRADEG, 1.0, xc, yc, /INTERP, /PIVOT, MISSING = !MISSING_VALUE)

		ENDELSE

	;ENDIF


	CASE projCoordStruct._coordSystem OF

		!HELIOCENTRIC_SOLAR_NORTH_UP: BEGIN

			IF !DEBUG EQ !TRUE THEN $
				;IF ABS(rotAngle) GT !ROTATION_THRESHOLD_ANGLE THEN $
					PRINT, "****** Image Rotated to Solar north up ", rotAngle, " degrees"

				; Set object property :
			(*self._pProjCoord)._coordSystem = !HELIOCENTRIC_SOLAR_NORTH_UP

		END

	  	!HELIOCENTRIC_ECLIPTIC: BEGIN

	   		IF !DEBUG EQ !TRUE THEN $
				;IF ABS(rotAngle) GT !ROTATION_THRESHOLD_ANGLE THEN $
					PRINT, "****** Image Rotated to Ecliptic north up ", rotAngle, " degrees"

			; Set object property :
			(*self._pProjCoord)._coordSystem = !HELIOCENTRIC_ECLIPTIC

		END

	 	ELSE: MESSAGE, "Unknown coordinate system"

	END

	; Rotation could not be performed :
	IF N_ELEMENTS(rotated) EQ 0 THEN $
		MESSAGE, "Rotated image could not be computed"

	; Update _pCalibrated property :
	*self._pCalibratedImage = rotated

	RETURN, !TRUE

END


;+
; NAME:
;   FESTIVAL_DATA::DATA_PREP
;
; PURPOSE:
;   Perform calibration on an image depending on probe/instrument.
;
; INPUTS:
;   oFestivalCalibrationGUI: an object reference to the FESTIVAL_CALIBRATION_GUI wizard.
;
; KEYWORD PARAMETERS:
;   None.
;
; OUTPUTS:
;   1 if calibration could be performed OK, 0 otherwise.
;
; MODIFICATION HISTORY:
;   29/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::DATA_PREP, oFestivalCalibrationGUI

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, "WARNING : " + !error_state.msg

		END

		CATCH, /CANCEL

		; Reinitialize some object properties :

		; Calibrated  buffer :
		IF PTR_VALID(self._pCalibratedImage) THEN $
			PTR_FREE, self._pCalibratedImage

		; FITS header :
		IF PTR_VALID(self._pFITSHeader) THEN $
			PTR_FREE, self._pFITSHeader

		; Calibration model :
		self._calibrationArray._calibrationModel = !OFF
		self._calibrationArray._desmear = !OFF

		; Image coordinates :
		self._imageCoordinates = [0.0, 0.0, 0.0, 0.0]

		; Update size in memory :
		self->UPDATESIZEINMEMORY

		RETURN, !FALSE

	END

	IF !DEBUG EQ !TRUE THEN $
		PRINT, "-------------------------------------------------------------------> DATA_PREP"

	; Return value :
	out = !TRUE

	; An invalid object was passed :
	IF ~OBJ_VALID(oFestivalCalibrationGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_CALIBRATION_GUI object was passed"

	; A wrong object was passed :
	IF ~OBJ_ISA(oFestivalCalibrationGUI, "FESTIVAL_CALIBRATION_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_CALIBRATION_GUI object was passed"

	oFestivalCalibrationGUI->GETPROPERTY, ALL = all, PARENT = oParent
	IF ~OBJ_VALID(oParent) THEN $
		MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"
	IF ~OBJ_ISA(oParent, "FESTIVAL_SELECTION_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_SELECTION_GUI object was found"


        ; functions defined in other files and
        ; that need to be declared as functions to the compiler

        FORWARD_FUNCTION EIT_FXPAR, EIT_PIXSIZE, EIT_POINT, GET_ORBIT, $
          SCC_GETBKGIMG, COR_POLARIZ


	; Probe testing :
	SWITCH self._probe OF

		; SOHO probe :
		!SOHO: BEGIN

                    if !DEBUG EQ !TRUE then begin
                        print, "preping :"+self._fullfilepath[0]
                    endif

			; Instrument testing :
			SWITCH self._instrument OF

				!EIT: BEGIN

                                ; mod by A. Antunes to allow
                                ; catalog_lz usage
                                ; at NRL-- eit_prep requires the /nrl flag
                                        if (getenv('SITE') eq 'nrl') then nrl=1 else nrl=0

					EIT_PREP, self._fullfilepath[0], hdr, image, nrl=nrl, /NO_ROLL, $
					NO_CALIBRATE = (all[self._probe, self._instrument]._on EQ !OFF), VERBOSE = !DEBUG

					; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

					IF (self._userFilter EQ !UNDEF_FIELD) OR (self._userFilter EQ '') THEN BEGIN
						self._userFilter = STRTRIM(EIT_FXPAR(hdr, 'FILTER'), 2)
						IF self._userFilter EQ 'Al +1' THEN self._userFilter = 'Al+1'
						IF self._userFilter EQ 'Al +2' THEN self._userFilter = 'Al+2'
					ENDIF

					; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

					; Update _imageCoordinates property :

					P1_X = EIT_FXPAR(hdr, 'P1_X') - 1
					P1_Y = EIT_FXPAR(hdr, 'P1_Y') - 20
					P2_X = EIT_FXPAR(hdr, 'P2_X') - 1
					P2_Y = EIT_FXPAR(hdr, 'P2_Y') - 20

                    xpix = EIT_FXPAR(hdr, 'NAXIS1')
                    ypix = EIT_FXPAR(hdr, 'NAXIS2')

                    binx = ((P2_X - P1_X + 1)/xpix)>1
                    biny = ((P2_Y - P1_Y + 1)/ypix)>1

					; Normalize to binning
					image = TEMPORARY(image)/(binx*biny)

					self._CDELT1 = EIT_FXPAR(hdr, 'CDELT1')
					self._CDELT2 = EIT_FXPAR(hdr, 'CDELT2')

                    IF self._CDELT1 EQ 0 THEN self._CDELT1 = EIT_PIXSIZE()*binx
                    IF self._CDELT2 EQ 0 THEN self._CDELT2 = EIT_PIXSIZE()*biny

					self._CRPIX1 = EIT_FXPAR(hdr, 'CRPIX1')
					self._CRPIX2 = EIT_FXPAR(hdr, 'CRPIX2')

                    ; mod by A. Antunes to allow catalog_lz usage at NRL, keyname used for
                    ; both eit_point and get_orbit as 'CORRECTED DATE_OBS' does not exist
                    ; in NRL FITS files

                    if (nrl EQ 1) then date_keyname='DATE_OBS' $
                    else date_keyname='CORRECTED DATE_OBS'

					IF (self._CRPIX1 LT 0) OR (self._CRPIX2 LT 0) THEN BEGIN

					 	point = EIT_POINT(EIT_FXPAR(hdr,date_keyname, image_no=0), $
					 	                  EIT_FXPAR(HDR, 'WAVELNTH', image_no=0), BIN=binx)
					 	self._CRPIX1 = point[0] - P1_X
					 	self._CRPIX2 = point[1] - P1_Y

					END

					self._CRVAL1 = FLOAT(EIT_FXPAR(hdr, 'CRVAL1'))/3600.0
					self._CRVAL2 = FLOAT(EIT_FXPAR(hdr, 'CRVAL2'))/3600.0

					; Extract Geometry information from header

					errmsg = ''
					date = EIT_FXPAR(hdr, date_keyname)
					orbit = GET_ORBIT(date, errmsg=errmsg)
					IF (errmsg EQ '') AND TEST_ORBIT(orbit) THEN BEGIN

						self._HAEX = orbit.HEC_X*1000.0d
						self._HAEY = orbit.HEC_Y*1000.0d
						self._HAEZ = orbit.HEC_Z*1000.0d
						self._CarringtonLongitude = (orbit.HEL_LON_SOHO*!DRADEG) MOD 360
						self._CarringtonLatitude = orbit.HEL_LAT_SOHO*!DRADEG

						self._SunDist = SQRT(self._HAEX^2.0 + self._HAEY^2.0 + self._HAEZ^2.0)

					ENDIF ELSE BEGIN

						; Computes geometry information: 1% correction from Earth view

						dum = GET_SUN(date, HE_LON=hel_lon, HE_LAT=hel_lat)

						alpha = !DDTOR*360.0*dum[6]/24.0  ;RA
						delta = !DDTOR*dum[7]			;DEC

						epsilon = 23.4392911*!DDTOR  ;inclination of the ecliptic (J2000)
						beta = -ASIN(SIN(delta)*COS(epsilon) - COS(delta)*SIN(epsilon)*SIN(alpha))  ;ecliptic latitude of Earth as seen from Sun
						lambda = !dpi + ATAN(SIN(epsilon)*SIN(delta) + SIN(alpha)*COS(delta)*COS(epsilon), COS(alpha)*COS(delta)) ;ecliptic longitude of Earth as seen from Sun

						SunDist = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO*dum[0]*!ONE_ASTRONOMICAL_UNIT  ;1% correction for SOHO

	                    self._HAEX = SunDist*COS(beta)*COS(lambda)
	                    self._HAEY = SunDist*COS(beta)*SIN(lambda)
	                    self._HAEZ = SunDist*SIN(beta)

						self._CarringtonLongitude = hel_lon MOD 360
						self._CarringtonLatitude = hel_lat
						self._SunDist = SunDist

					ENDELSE

					self._SunDistNormalization = 1.0d

					self._SolarNorthAngle = -(FLOAT(EIT_FXPAR(hdr, 'SC_ROLL')) MOD 360)
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END

				!C1:
				!C2:
				!C3: BEGIN

		  			IF all[self._probe, self._instrument]._calibrationModel EQ !OFF THEN $
						LASCO_PREP, self._fullfilepath[0], hdr, image, /MASK_OCC, /NORECTIFY, SUNC = sunc, ROLL = roll ELSE $
						LASCO_PREP, self._fullfilepath[0], hdr, image, /MASK_OCC, /NORECTIFY, SUNC = sunc, ROLL = roll, $
						USE_MODEL = all[self._probe, self._instrument]._calibrationModel

				; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

					IF (self._userWave EQ !UNDEF_FIELD) OR (self._userWave EQ '') THEN $
						self._userWave = hdr.filter

					IF (self._userFilter EQ !UNDEF_FIELD) OR (self._userFilter EQ '') THEN $
						self._userFilter = hdr.polar

					; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

					; Update _imageCoordinates property :
					binx = hdr.LEBXSUM
					biny = hdr.LEBYSUM

					P1_Y = hdr.P1COL - 20
					P2_Y = hdr.P2COL - 20
				 	P1_X = hdr.P1ROW - 1
                    P2_X = hdr.P2ROW - 1

					self._CRPIX1 = sunc.xcen
					self._CRPIX2 = sunc.ycen

					self._CDELT1 = hdr.CDELT1
					self._CDELT2 = hdr.CDELT2

					self._CRVAL1 = 0.0
					self._CRVAL2 = 0.0

					; Extract Geometry information from header

					errmsg = ''
					date = hdr.DATE_OBS + ' ' + hdr.TIME_OBS
					orbit = GET_ORBIT(date, errmsg=errmsg)
					IF (errmsg EQ '') AND TEST_ORBIT(orbit) THEN BEGIN

						self._HAEX = orbit.HEC_X*1000.0d
						self._HAEY = orbit.HEC_Y*1000.0d
						self._HAEZ = orbit.HEC_Z*1000.0d
						self._CarringtonLongitude = (orbit.HEL_LON_SOHO*!DRADEG) MOD 360
						self._CarringtonLatitude = orbit.HEL_LAT_SOHO*!DRADEG

						self._SunDist = SQRT(self._HAEX^2.0 + self._HAEY^2.0 + self._HAEZ^2.0)

					ENDIF ELSE BEGIN

						; Computes geometry information: 1% correction from Earth view

						dum = GET_SUN(date, HE_LON=hel_lon, HE_LAT=hel_lat)

						alpha = !DDTOR*360.0*dum[6]/24.0  ;RA
						delta = !DDTOR*dum[7]			;DEC

						epsilon = 23.4392911*!DDTOR  ;inclination of the ecliptic (J2000)
						beta = -ASIN(SIN(delta)*COS(epsilon) - COS(delta)*SIN(epsilon)*SIN(alpha))  ;ecliptic latitude of Earth as seen from Sun
						lambda = !dpi + ATAN(SIN(epsilon)*SIN(delta) + SIN(alpha)*COS(delta)*COS(epsilon), COS(alpha)*COS(delta)) ;ecliptic longitude of Earth as seen from Sun

						SunDist = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO*dum[0]*!ONE_ASTRONOMICAL_UNIT  ;1% correction for SOHO

	                    self._HAEX = SunDist*COS(beta)*COS(lambda)
	                    self._HAEY = SunDist*COS(beta)*SIN(lambda)
	                    self._HAEZ = SunDist*SIN(beta)

						self._CarringtonLongitude = hel_lon MOD 360
						self._CarringtonLatitude = hel_lat
						self._SunDist = SunDist

					ENDELSE

					self._SunDistNormalization = 1.0d

					self._SolarNorthAngle = !DRADEG * roll
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END


		; *** ADDED FOR NRH ***
				!NRH : BEGIN

                                ; by definition, self._fullFilePath is
                                ; a strarr(3) but only first component
                                ; makes sense here

					MREADFITS, self._fullFilePath[0], hdr, image, /QUIET

					; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

					; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

					; Update _imageCoordinates property :

					self._CRPIX1 = hdr.CRPIX1 - 1.0
					self._CRPIX2 = hdr.CRPIX2 - 1.0

					self._CDELT1 = hdr.CDELT1
					self._CDELT2 = hdr.CDELT2

					self._CRVAL1 = 0.0
					self._CRVAL2 = 0.0


					; Computes geometry information for Earth view

					dum = GET_SUN(hdr.DATE_OBS, HE_LON=hel_lon, HE_LAT=hel_lat)

					alpha = !DDTOR*360.0*dum[6]/24.0  ;RA
					delta = !DDTOR*dum[7]			;DEC

					epsilon = 23.4392911*!DDTOR  ;inclination of the ecliptic (J2000)
					beta = -ASIN(SIN(delta)*COS(epsilon) - COS(delta)*SIN(epsilon)*SIN(alpha))  ;ecliptic latitude of Earth as seen from Sun
					lambda = !dpi + ATAN(SIN(epsilon)*SIN(delta) + SIN(alpha)*COS(delta)*COS(epsilon), COS(alpha)*COS(delta)) ;ecliptic longitude of Earth as seen from Sun

					SunDist = dum[0]*!ONE_ASTRONOMICAL_UNIT

                    self._HAEX = SunDist*COS(beta)*COS(lambda)
                    self._HAEY = SunDist*COS(beta)*SIN(lambda)
                    self._HAEZ = SunDist*SIN(beta)

					self._CarringtonLongitude = hel_lon MOD 360
					self._CarringtonLatitude = hel_lat
					self._SunDist = SunDist
					self._SunDistNormalization = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO
					self._SolarNorthAngle = 0	;By definition NRH data have solar north up
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END

		; *** END OF ADDED FOR NRH ***

		; *** ADDED FOR Mk4 ***
                                !Mk4 : BEGIN

                                ; by definition, self._fullFilePath is
                                ; a strarr(3) but only first component
                                ; makes sense here

                                    extension = strmid(self._fullFilePath[0], 1, /REVERSE )

                                    if (extension EQ 'gz') THEN BEGIN

                                        new_file = !MK4_UNZIPPED_DIR + $
                                                   PATH_SEP() + file_basename(self._fullFilePath[0])

                                        file_copy, self._fullFilePath[0], new_file, /OVERWRITE

                                        if (os_family(/lower) eq 'unix') then $

                                          gzip, new_file, unzipped_file, /UNZIP $

                                        else begin

                                          ; Windows case (not supported by $SSW/gen/idl/system/gzip.pro) :

                                          ; gzip.exe must be in PATH (it can be downloaded at :
                                          ; http://www.gzip.org/ )

                                          gunzip_cmd = 'gzip -d -f "'+new_file+'"'
                                          espawn,gunzip_cmd,out_txt,/noshell
                                          if (out_txt[0] NE '') then begin
                                             print,"------------------------------------------------------------------------------------"
                                             print,"Have you really installed gzip (from http://www.gzip.org/) and put it in the Path variable?"
                                             print,out_txt
                                             print,"------------------------------------------------------------------------------------"
                                          endif
                                          unzipped_file=str_replace(new_file,'.gz','')

                                        endelse

                                        self._fullFilePath[0] = unzipped_file

                                    endif

					mreadfits, self._fullFilePath[0], hdr, image, /QUIET

					; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

					bad = WHERE(image EQ -32000, count)
					IF count GT 0 THEN image[bad] = !MISSING_VALUE

					; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

					; Update _imageCoordinates property :

					self._CRPIX1 = hdr.CRPIX1 - 1.0
					self._CRPIX2 = hdr.CRPIX2 - 1.0

					self._CDELT1 = hdr.CDELT1
					self._CDELT2 = hdr.CDELT2

					self._CRVAL1 = hdr.CRVAL1
					self._CRVAL2 = hdr.CRVAL1

					; Computes geometry information for Earth view

					dum = GET_SUN(hdr.DATE_OBS, HE_LON=hel_lon, HE_LAT=hel_lat)

					alpha = !DDTOR*360.0*dum[6]/24.0  ;RA
					delta = !DDTOR*dum[7]			;DEC

					epsilon = 23.4392911*!DDTOR  ;inclination of the ecliptic (J2000)
					beta = -ASIN(SIN(delta)*COS(epsilon) - COS(delta)*SIN(epsilon)*SIN(alpha))  ;ecliptic latitude of Earth as seen from Sun
					lambda = !dpi + ATAN(SIN(epsilon)*SIN(delta) + SIN(alpha)*COS(delta)*COS(epsilon), COS(alpha)*COS(delta)) ;ecliptic longitude of Earth as seen from Sun

					SunDist = dum[0]*!ONE_ASTRONOMICAL_UNIT

                    self._HAEX = SunDist*COS(beta)*COS(lambda)
                    self._HAEY = SunDist*COS(beta)*SIN(lambda)
                    self._HAEZ = SunDist*SIN(beta)

					self._CarringtonLongitude = hel_lon MOD 360
					self._CarringtonLatitude = hel_lat
					self._SunDist = SunDist
					self._SunDistNormalization = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO
					self._SolarNorthAngle = 0	;By definition NRH data have solar north up
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END

		; *** END OF ADDED FOR Mk4 ***

		; *** ADDED FOR TRACE ***
                                !TRACE : BEGIN


                                    self._oRefSearchResult-> GetProperty, INDEX_IN_FILE = index_in_file, $
                                      DATE = date_obs

                                    ; case of TRACE files downloaded with VSO is
                                    ; different because index of data in
                                    ; file is not known yet : first look
                                    ; for the index of that date.
                                    ; Note : 128x128 images are only accepted
                                    ; when no bigger data at that date is found

                    if (index_in_file EQ -1) then begin

                    	read_trace, self._fullFilePath[0], -1, headers
                        good_files_index = where(headers.DATE_OBS EQ date_obs, count)

                        if (count EQ 0) then begin
                          MESSAGE, "Calibration could not be performed for : " + self._fileSpecification
                        endif

                        index_in_file = good_files_index[0]

                    	self._oRefSearchResult-> SetProperty, INDEX_IN_FILE = index_in_file

                    endif

					no_calibrate = all[self._probe, self._instrument]._on EQ !OFF

                    TRACE_PREP, self._fullFilePath[0], index_in_file, hdr, image, $
                    	NO_DARKSUB = no_calibrate, $
                    	NO_FLATFIELD = no_calibrate, $
                    	;NO_CALIB = no_calibrate, $
                    	NORMALIZE = ~no_calibrate, $
                    	/WAVE2POINT_CORRECT, /QUIET, VERBOSE =!DEBUG, /FLOAT

					; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

;					bad = WHERE(image EQ -32000, count)
;					IF count GT 0 THEN image[bad] = !MISSING_VALUE

					; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

					; Update _imageCoordinates property :

					self._CRPIX1 = hdr.CRPIX1 - 1.0
					self._CRPIX2 = hdr.CRPIX2 - 1.0

					self._CDELT1 = hdr.CDELT1
					self._CDELT2 = hdr.CDELT2

					self._CRVAL1 = hdr.CRVAL1
					self._CRVAL2 = hdr.CRVAL1

					; Computes geometry information for Earth view

					dum = GET_SUN(hdr.DATE_OBS, HE_LON=hel_lon, HE_LAT=hel_lat)

					alpha = !DDTOR*360.0*dum[6]/24.0  ;RA
					delta = !DDTOR*dum[7]			;DEC

					epsilon = 23.4392911*!DDTOR  ;inclination of the ecliptic (J2000)
					beta = -ASIN(SIN(delta)*COS(epsilon) - COS(delta)*SIN(epsilon)*SIN(alpha))  ;ecliptic latitude of Earth as seen from Sun
					lambda = !dpi + ATAN(SIN(epsilon)*SIN(delta) + SIN(alpha)*COS(delta)*COS(epsilon), COS(alpha)*COS(delta)) ;ecliptic longitude of Earth as seen from Sun

					SunDist = dum[0]*!ONE_ASTRONOMICAL_UNIT

                    self._HAEX = SunDist*COS(beta)*COS(lambda)
                    self._HAEY = SunDist*COS(beta)*SIN(lambda)
                    self._HAEZ = SunDist*SIN(beta)

					self._CarringtonLongitude = hel_lon MOD 360
					self._CarringtonLatitude = hel_lat
					self._SunDist = SunDist
					self._SunDistNormalization = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO
					self._SolarNorthAngle = 0
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END

		; *** END OF ADDED FOR TRACE ***

		; *** ADDED FOR XRT ***
                !XRT : BEGIN


                    self._oRefSearchResult-> GetProperty, INDEX_IN_FILE = index_in_file, $
                         DATE = date_obs

					no_calibrate = all[self._probe, self._instrument]._on EQ !OFF

					XRT_PREP, self._fullFilePath[0], 0, hdr, image, $
						NORMALIZE = ~no_calibrate, $
						/FLOAT, /QUIET, VERBOSE = !DEBUG

					; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

					offsets = [0.0, 0.0]
					XRT_JITTER, hdr, offsets

					IF (self._userWave EQ !UNDEF_FIELD) OR (self._userWave EQ '') THEN $
						self._userWave = hdr.EC_FW1_

					IF (self._userFilter EQ !UNDEF_FIELD) OR (self._userFilter EQ '') THEN $
						self._userFilter = hdr.EC_FW2_

					; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

					; Update _imageCoordinates property :

					bin = hdr.CHIP_SUM

					self._CRPIX1 = (hdr.CRPIX1 - 9)/bin
					self._CRPIX2 = (hdr.CRPIX2 - 1)/bin

					default_cdelt = 1.031 ;arcseconds/pixel
										  ;default value if header value seems too far off

					IF ABS(hdr.CDELT1 - default_cdelt) GT 0.001 THEN $
						self._CDELT1 = default_cdelt*bin $
					ELSE $
						self._CDELT1 = hdr.CDELT1

					IF ABS(hdr.CDELT2 - default_cdelt) GT 0.001 THEN $
						self._CDELT2 = default_cdelt*bin $
					ELSE $
						self._CDELT2 = hdr.CDELT2

					self._CRVAL1 = (hdr.CRVAL1 - offsets[0])/3600.0
					self._CRVAL2 = (hdr.CRVAL2 - offsets[1])/3600.0

					; Computes geometry information for Earth view

					dum = GET_SUN(hdr.DATE_OBS, HE_LON=hel_lon, HE_LAT=hel_lat)

					alpha = !DDTOR*360.0*dum[6]/24.0  ;RA
					delta = !DDTOR*dum[7]			  ;DEC

					epsilon = 23.4392911*!DDTOR  ;inclination of the ecliptic (J2000)
					beta = -ASIN(SIN(delta)*COS(epsilon) - COS(delta)*SIN(epsilon)*SIN(alpha))  ;ecliptic latitude of Earth as seen from Sun
					lambda = !dpi + ATAN(SIN(epsilon)*SIN(delta) + SIN(alpha)*COS(delta)*COS(epsilon), COS(alpha)*COS(delta)) ;ecliptic longitude of Earth as seen from Sun

					SunDist = dum[0]*!ONE_ASTRONOMICAL_UNIT
					self._SunDistNormalization = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO

                    self._HAEX = SunDist*COS(beta)*COS(lambda)
                    self._HAEY = SunDist*COS(beta)*SIN(lambda)
                    self._HAEZ = SunDist*SIN(beta)

					self._CarringtonLongitude = hel_lon MOD 360
					self._CarringtonLatitude = hel_lat
					self._SunDist = SunDist
					self._SunDistNormalization = !MEAN_SUN_SOHO_EARTH_DISTANCE_RATIO
					self._SolarNorthAngle = hdr.CROTA1
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END

		; *** END OF ADDED FOR XRT ***

				; Undefined :
				ELSE: MESSAGE, "Unknown SOHO instruments"

			END

			BREAK

		END

		; STEREOA probe :
		!STEREOA:

		; STEREOB probe :
		!STEREOB: BEGIN
        
            SWITCH self._instrument OF
				!EUVI: 
				!COR1:
				!COR2: 
				!HI1:
				!HI2: BEGIN

					IF all[self._probe, self._instrument]._on THEN BEGIN
						CASE all[self._probe, self._instrument]._calibrationModel OF
							!MONTHLYMINIMUM: BEGIN
								calfac_off = 1
								calimg_off = 1
								calibrate_off = self._Instrument EQ !COR2 AND self._polarityType NE !TB
								daily = 0
								totalb = (self._Instrument EQ !HI1 OR self._Instrument EQ !HI2) $
										OR ((self._Instrument EQ !COR2) AND (self._matchedPolarity EQ !TRUE) AND (self._polarityType EQ !TB))
							END
							!DAILYMEDIAN: BEGIN
								calfac_off = 1
								calimg_off = 1
								calibrate_off = self._Instrument EQ !COR2 AND self._polarityType NE !TB
								daily = 1
								totalb = (self._Instrument EQ !HI1 OR self._Instrument EQ !HI2) $
										OR ((self._Instrument EQ !COR2) AND (self._matchedPolarity EQ !TRUE) AND (self._polarityType EQ !TB))
							END
							!OFF: BEGIN
								calfac_off = 0
								calimg_off = 0
								calibrate_off = 0
							END
							ELSE: MESSAGE, 'Uknown calibration model'
						ENDCASE

					ENDIF ELSE BEGIN

						calfac_off = 1
						calimg_off = 1
						calibrate_off = 1

					ENDELSE

				 ; Create an object FESTIVAL_KEYWORD_READER :
				  keywordsFile = !FESTIVAL_SAVE_DIRECTORY + PATH_SEP() + 'config' + PATH_SEP() + !KEYWORDS_FILE
				  oKeywordReader = OBJ_NEW("FESTIVAL_KEYWORD_READER", keywordsFile)
				  IF OBJ_VALID(oKeywordReader) THEN BEGIN

  					oKeywordReader->READSECTION, 'SECCHI_PREP', RESULT = extraKwds, /CASTTOINTEGER
    					; Free memory :
  					OBJ_DESTROY, oKeywordReader

				  ENDIF

					; Stereo calibration :
					
					precomm = self._instrument EQ !COR2
				
					IF self._matchedPolarity EQ !TRUE THEN $
						SECCHI_PREP, self._fullFilePath, hdr, image, CALIBRATE_OFF = calibrate_off, CALFAC_OFF = calfac_off, CALIMG_OFF = calimg_off, SILENT = (!DEBUG EQ !FALSE), $
							DESMEAR_OFF = (all[self._probe, self._instrument]._desmear EQ !FALSE), /NOWARP, $
							/NOSPICE, PRECOMMCORRECT_ON = precomm, /BKGIMG_OFF, _EXTRA = extraKwds $
					ELSE $
						SECCHI_PREP, self._fullFilePath[0], hdr, image, CALIBRATE_OFF = calibrate_off, CALFAC_OFF = calfac_off, CALIMG_OFF = calimg_off, SILENT = (!DEBUG EQ !FALSE), $
							DESMEAR_OFF = (all[self._probe, self._instrument]._desmear EQ !FALSE), /NOWARP, $
							/NOSPICE, PRECOMMCORRECT_ON = precomm, /BKGIMG_OFF, _EXTRA = extraKwds

					CASE self._Instrument OF
						!EUVI: BEGIN
								IF (self._userFilter EQ !UNDEF_FIELD) OR (self._userFilter EQ '') THEN $
									self._userFilter = hdr[0].filter
							END
						!COR1: BEGIN
							END
						!COR2: BEGIN

							END
						!HI1: BEGIN

							END
						!HI2: BEGIN
							END
						ELSE: MESSAGE, "Unknown SECCHI Instrument"
					ENDCASE

					FOR i=0, N_ELEMENTS(hdr)-1 DO BEGIN
						CASE self._Instrument OF
							!EUVI: bin = 2.0^(hdr[i].SUMMED-1)
							!COR1: bin = 2.0^(hdr[i].SUMMED-2)
							!COR2: bin = 2.0^(hdr[i].SUMMED-1)
							!HI1: bin = 2.0^(hdr[i].SUMMED-2)
							!HI2: bin = 2.0^(hdr[i].SUMMED-2)
							ELSE: MESSAGE, "Unknown SECCHI Instrument"
						ENDCASE
						IF bin GT 1 THEN image[*, *, i] = image[*, *, i]/bin^2.0
					ENDFOR

					IF ~((self._Instrument EQ !COR2) AND (self._matchedPolarity EQ !TRUE) AND (self._polarityType EQ !TB)) THEN BEGIN
						IF (all[self._probe, self._instrument]._on EQ !TRUE) AND $
							(all[self._probe, self._instrument]._calibrationModel NE !OFF) THEN BEGIN
							FOR i=0, N_ELEMENTS(hdr)-1 DO BEGIN
								background = SCC_GETBKGIMG(hdr[i], TOTALB = totalb, DAILY = daily, $
                                                                                           SILENT = (!DEBUG EQ !FALSE), /INTERPOLATE)
								IF N_ELEMENTS(background) GT 1 THEN BEGIN
									IF self._Instrument EQ !COR2 OR self._Instrument EQ !HI1 OR self._Instrument EQ !HI2 THEN $
										image[*, *, i] = image[*, *, i] / background $
									ELSE $
										image[*, *, i] = image[*, *, i] - background
								ENDIF
							ENDFOR
						ENDIF
					ENDIF

					IF self._matchedPolarity EQ !TRUE THEN BEGIN

						CASE self._polarityType OF
							!PB: image = COR_POLARIZ(image, hdr, /PB, CALIBRATE_OFF = calibrate_off, CALFAC_OFF = calfac_off, CALIMG_OFF = calimg_off, SILENT = (!DEBUG EQ !FALSE))
							!TB: image = COR_POLARIZ(image, hdr, CALIBRATE_OFF = calibrate_off, CALFAC_OFF = calfac_off, CALIMG_OFF = calimg_off, SILENT = (!DEBUG EQ !FALSE))
							!MU: image = COR_POLARIZ(image, hdr, /MU, CALIBRATE_OFF = calibrate_off, CALFAC_OFF = calfac_off, CALIMG_OFF = calimg_off, SILENT = (!DEBUG EQ !FALSE))
							!PERCENT: image = COR_POLARIZ(image, hdr, /PERCENT, CALIBRATE_OFF = calibrate_off, CALFAC_OFF = calfac_off, CALIMG_OFF = calimg_off, SILENT = (!DEBUG EQ !FALSE))
							ELSE: MESSAGE, 'Unknown polarisation'
						END

						hdr = hdr[1]

					ENDIF

					IF ((self._Instrument EQ !COR2) AND (self._matchedPolarity EQ !TRUE) AND (self._polarityType EQ !TB)) THEN BEGIN
						IF (all[self._probe, self._instrument]._on EQ !TRUE) AND $
							(all[self._probe, self._instrument]._calibrationModel NE !OFF) THEN BEGIN
							background = SCC_GETBKGIMG(hdr, TOTALB = totalb, DAILY = daily, $
                                                                                   SILENT = (!DEBUG EQ !FALSE), /INTERPOLATE)
							IF N_ELEMENTS(background) GT 1 THEN image = image - background
						ENDIF
					ENDIF

					; Perform error checking :
					IF N_ELEMENTS(image) EQ 0 OR N_ELEMENTS(hdr) EQ 0 THEN $
						MESSAGE, "Calibration could not be performed for : " + self._fileSpecification

           			; Update _calibrationModel property :
					self._calibrationArray = all[self._probe, self._instrument]

       	    		self._CRPIX1 = hdr.CRPIX1 - 1
					self._CRPIX2 = hdr.CRPIX2 - 1

					self._CDELT1 = hdr.CDELT1
					self._CDELT2 = hdr.CDELT2

					self._CRVAL1 = hdr.CRVAL1
					self._CRVAL2 = hdr.CRVAL2

					; convert self._CRVAL1/2 to degrees (FESTIVAL convention)
					; convert self._CDELT1/2 to arcseonds  (FESTIVAL convention)

					CASE hdr.CUNIT1 OF
						'deg': self._CDELT1 = self._CDELT1*3600.0d
						'arcsec': self._CRVAL1 = self._CRVAL1/3600.0d
						ELSE:
					ENDCASE
					CASE hdr.CUNIT2 OF
						'deg': self._CDELT2 = self._CDELT2*3600.0d
						'arcsec': self._CRVAL2 = self._CRVAL2/3600.0d
						ELSE:
					ENDCASE

					; for HIs, fixes CRVAL1/2 if 0 in header based on nominal values
					IF self._Instrument EQ !HI1 OR self._Instrument EQ !HI2 THEN BEGIN

						IF (self._CRVAL1 EQ 0.0) AND (self._CRVAL2 EQ 0) THEN BEGIN

							IF self._probe EQ !STEREOA THEN BEGIN
								IF self._Instrument EQ !HI1 THEN dq = -13.98*!DDTOR $
								ELSE dq = -53.68 * !DDTOR
							ENDIF ELSE BEGIN
								IF self._Instrument EQ !HI1 THEN dq = 13.98*!DDTOR $
								ELSE dq = 53.68 * !DDTOR
							ENDELSE

							dk = hdr.CROTA*!DDTOR
							zp = COS(dq)
							xp = SIN(dq)*SIN(dk)
							yp = SIN(dq)*COS(dk)

							self._CRVAL2 = ASIN(xp)*!DRADEG
							self._CRVAL1 = ATAN(yp, zp)*!DRADEG

						ENDIF

					ENDIF

					; Extract Geometry information from header

					self._HAEX = hdr.HAEX_OBS
					self._HAEY = hdr.HAEY_OBS
					self._HAEZ = hdr.HAEZ_OBS
					self._CarringtonLongitude = (hdr.CRLN_OBS + 360) MOD 360
					self._CarringtonLatitude = hdr.CRLT_OBS
					self._SunDist = hdr.DSUN_OBS
					self._SunDistNormalization = 1.0d
					self._SolarNorthAngle = hdr.CROTA
					self._EclipticSolarNorthAngle = FESTIVAL_ECL_SOLAR_NORTH(self._HAEX, self._HAEY, self._HAEZ)

					BREAK

				END

				; Undefined :
				ELSE: MESSAGE, "Unknown STEREO instruments"

			END

			BREAK

		END

		ELSE: MESSAGE, "Unknown probe"


            END


	; Exclude !VALUES.F_NAN values :
	vec = WHERE(~FINITE(image), count)

	IF count GT 0 THEN BEGIN

		IF count EQ N_ELEMENTS(image) THEN $
			MESSAGE, "Image could not be calibrated"

		image[vec] = 0

	END

	; Perform image rotation :
	; Store image :
	IF PTR_VALID(self._pCalibratedImage) THEN $
		*self._pCalibratedImage = TEMPORARY(FLOAT(image)) ELSE $
		self._pCalibratedImage = PTR_NEW(FLOAT(image), /NO_COPY)

	; Store FITS header :
	; Change it eventually to structure :
	IF SIZE(hdr, /TYPE) NE 8 THEN $
		hdr = FITSHead2Struct(hdr)

	IF PTR_VALID(self._pFITSHeader) THEN $
		*self._pFITSHeader = TEMPORARY(hdr) ELSE $
		self._pFITSHeader = PTR_NEW(hdr, /NO_COPY)

	; Update size in memory :
	self->UPDATESIZEINMEMORY

	RETURN, out

END

;+
; NAME:
;   FESTIVAL_DATA::FILTER
;
; PURPOSE:
;   Perform filtering on an image using a user defined filtering sequence.
;
; INPUTS:
;   oFestivalFilterGUI: an object reference to the FESTIVAL_FILTER_GUI wizard.
;
; KEYWORD PARAMETERS:
;   None.
;
; OUTPUTS:
;   1 if filtering could be performed OK, 0 otherwise.
;
; MODIFICATION HISTORY:
;   29/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::FILTER, oFestivalFilterGUI

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL

		; Reinitialize some object properties :
		IF PTR_VALID(self._pFilteredImage) THEN $
			PTR_FREE, self._pFilteredImage

		IF PTR_VALID(self._pFilteringSequence) THEN $
			PTR_FREE, self._pFilteringSequence

		; Update size in memory :
		self->UPDATESIZEINMEMORY

		RETURN, !FALSE

	END

	IF !DEBUG EQ !TRUE THEN $
		PRINT, "-------------------------------------------------------------------> FILTER"

	; Return value :
	out = !TRUE

	; A scientific image must be available :
	IF ~PTR_VALID(self._pCalibratedImage) THEN $
		MESSAGE, "No calibrated image available for : " + self._fileSpecification

	; An invalid object was passed :
	IF ~OBJ_VALID(oFestivalFilterGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_FILTER_GUI object was passed"

	; A wrong object was passed :
	IF ~OBJ_ISA(oFestivalFilterGUI, "FESTIVAL_FILTER_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_FILTER_GUI object was passed"

	; Get object properties :
	oFestivalFilterGUI->GETPROPERTY, ALL = all
	filterStruct = all[self._probe, self._instrument]

	; Default is : _pFilteredImage = _pCalibratedImage
	IF PTR_VALID(self._pFilteredImage) THEN $
		*self._pFilteredImage = *self._pCalibratedImage ELSE $
		self._pFilteredImage = PTR_NEW(*self._pCalibratedImage)

	; Filtering required :
   	IF filterStruct._filterON THEN BEGIN

		; Filtering sequence is valid :
		IF PTR_VALID(filterStruct._pFilteringSequence) THEN BEGIN

			; Perform filtering
			filteringSequence = *filterStruct._pFilteringSequence

			; Is a non-null filtering sequence available ?
			IF filteringSequence[0] NE '' THEN BEGIN

				; Define initial image & header:
				image = *self._pCalibratedImage

				header = *self._pFITSHeader
				saved_header = header

				FOR i = 0, N_ELEMENTS(filteringSequence)-1 DO BEGIN

					filterSring = "image = " + filteringSequence[i]
					res = EXECUTE(filterSring)

					IF ~res THEN $
						MESSAGE, filteringString + " failed for : " + self._fileSpecification

					header = saved_header  ;prevents side effects if filter modifies header

				END

				; Check filtered image validity :
				info = SIZE(image)
				IF info[0] EQ 0 THEN $
					MESSAGE, "Filtered image is NOT a 2D array"

				; Exclude !VALUES.F_NAN values :
				vec = WHERE(~FINITE(image), count)
				IF count GT 0 THEN BEGIN

					IF count EQ N_ELEMENTS(image) THEN $
						MESSAGE, "Image could not be filtered"

					image[vec] = 0

				END

				; Store filtered image :
				IF PTR_VALID(self._pFilteredImage) THEN $
					*self._pFilteredImage = TEMPORARY(FLOAT(image)) ELSE $
					self._pFilteredImage = PTR_NEW(FLOAT(image), /NO_COPY)

				; Update size in memory :
				self->UPDATESIZEINMEMORY

				; Filtering is ON but empty filtering sequence :
			END

			; Store filtering sequence :
			IF PTR_VALID(self._pFilteringSequence) THEN $
				*self._pFilteringSequence = TEMPORARY(filteringSequence) ELSE $
				self._pFilteringSequence = PTR_NEW(filteringSequence, /NO_COPY)

		END

	END ELSE $
		IF !DEBUG EQ !TRUE THEN $
			PRINT, "_pFilteredImage = _pCalibratedImage because no filtering sequence is available"

	RETURN, out

END

;+
; NAME:
;   FESTIVAL_DATA::COMPUTEMINMAX
;
; PURPOSE:
;   Compute min and max values of an image prior to enhancement.
;
; INPUTS:
;   None
;
; KEYWORD PARAMETERS:
;   MINI : Set this keyword to named variable that will contain the minimum image value.
;
;	MAXI : Set this keyword to named variable that will contain the maximum image value.
;
; OUTPUTS:
;   None
;
; MODIFICATION HISTORY:
;   04/09/06 - IAS ORSAY
;-
PRO FESTIVAL_DATA::COMPUTEMINMAX, MINI = mini, MAXI = maxi

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL
		RETURN

	END

	IF ~PTR_VALID(self._pFilteredImage) THEN $
		MESSAGE, "No valid filtered image available"

	sz = SIZE(*self._pFilteredImage)
	nx = 128>(sz[1]/8.0)<1024
	ny = 128>(sz[2]/8.0)<1024
	im = CONGRID(*self._pFilteredImage, nx, ny)
	x2 = ((FINDGEN(nx)/nx - 0.5)^2.0) # REPLICATE(1.0, ny)
	y2 = REPLICATE(1.0, nx) # ((FINDGEN(ny)/ny - 0.5)^2.0)
	r2 = x2 + y2
	valid = WHERE((FINITE(im) EQ 1) AND (im NE !MISSING_VALUE) AND (r2 LT 0.25), count)

    IF count GT 0 THEN BEGIN

		im = im[valid]

		pos = WHERE(im GT 0, poscount)
		neg = WHERE(im LT 0, negcount)
		IF poscount GT 0 THEN im[pos] = SQRT(im[pos])
		IF negcount GT 0 THEN im[neg] = -SQRT(-im[neg])

		minval = 0.01
		maxval = 0.99

		minim = MIN(im, MAX = maxim)
    	histo = HISTOGRAM(im, MIN = minim, MAX = maxim, NBINS=2000, LOCATIONS = x)
    	histo = MEDIAN(histo, 3)

		tot = TOTAL(ABS(histo*x), /DOUBLE, /CUMULATIVE)
		tot = tot/tot[N_ELEMENTS(tot)-1]

 		good = WHERE((tot GE minval) AND (tot LE maxval), count)
    	IF count GT 0 THEN BEGIN
    		mini = MIN(x[good], MAX = maxi)
    		IF mini GT 0 THEN mini = mini^2.0 ELSE mini = -mini^2.0
    		IF maxi GT 0 THEN maxi = maxi^2.0 ELSE maxi = -maxi^2.0
    	ENDIF ELSE mini = MIN(im, MAX = maxi)

		IF mini EQ maxi THEN mini = MIN(im, MAX = maxi)

    ENDIF ELSE BEGIN

    	maxi = 255.0
    	mini = 0.0

    ENDELSE

END

PRO FESTIVAL_DATA::CHECK_MIN_MAX, enhancementAlgo, mini, maxi

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL

	END

	IF ~PTR_VALID(self._pFilteredImage) THEN $
		MESSAGE, "No filtered image available"

	CASE enhancementAlgo OF

		!LOG: BEGIN

			pos = WHERE(*self._pFilteredImage GT 0, count)
			IF count GT 0 THEN minLogValue = MIN((*self._pFilteredImage)[pos],MAX=maxLogValue) $
			ELSE BEGIN

            	minLogValue = 1e-20
                maxLogValue = 1e-20

         	END

			IF mini LE 0 THEN $
				mini = minLogValue

			IF maxi LE 0 THEN $
				maxi = maxLogValue

		END

		!SQUARE_ROOT: BEGIN

			IF mini LT 0.0 THEN $
				mini = 0

			IF maxi LT 0.0 THEN $
            	maxi = MAX(*self._pFilteredImage) > 0.0

		END

		ELSE: ; Do nothing :

	END

END

;+
; NAME:
;   FESTIVAL_DATA::ENAHNCE
;
; PURPOSE:
;   Perform filtering on an image using a user defined filtering sequence.
;
; INPUTS:
;   oFestivalFilterGUI: an object reference to the FESTIVAL_FILTER_GUI wizard.
;
; KEYWORD PARAMETERS:
;   None.
;
; OUTPUTS:
;   1 if filtering could be performed OK, 0 otherwise.
;
; MODIFICATION HISTORY:
;   29/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::ENHANCE, oFestivalEnhancementGUI

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL

		; Reinitialize some object properties :
		IF PTR_VALID(self._pEnhancedImage) THEN $
			PTR_FREE, self._pEnhancedImage

		; Reinitialize enhancement algorithm :
		self._enhancement = {FESTIVAL_ENHANCEMENT}

		; Update size in memory :
		self->UPDATESIZEINMEMORY

		RETURN, !FALSE

	END

	IF !DEBUG EQ !TRUE THEN $
		PRINT, "-------------------------------------------------------------------> ENHANCE"

	; Check for input object validity :
	IF ~OBJ_VALID(oFestivalEnhancementGUI) THEN $
		MESSAGE, "An invalid FESTIVAL_ENHANCEMENT_GUI object was passed"

	IF ~OBJ_ISA(oFestivalEnhancementGUI, "FESTIVAL_ENHANCEMENT_GUI") THEN $
		MESSAGE, "An invalid FESTIVAL_ENHANCEMENT_GUI object was passed"

	; A scientific image must be available :
	IF ~PTR_VALID(self._pFilteredImage) THEN $
		MESSAGE, "No filtered image available for : " + self._fileSpecification

	; Get all object properties :
	oFestivalEnhancementGUI->GETPROPERTY, ALL = all

	wavesList = *!WAVES_ARRAY[self._probe, self._instrument]
	filtersList = *!FILTERS_ARRAY[self._probe, self._instrument]

	IF N_ELEMENTS(wavesList) EQ 1 THEN $	;Only one or only all
		wave = 0 $
	ELSE BEGIN
		wave = WHERE(STRLOWCASE(wavesList) EQ STRLOWCASE(self._userWave), cwave)
		IF cwave EQ 0 THEN MESSAGE, "Image could not be enhanced"
	ENDELSE

	IF N_ELEMENTS(filtersList) EQ 1 THEN $
		filter = 0 $
	ELSE BEGIN
		filter = WHERE(STRLOWCASE(filtersList) EQ STRLOWCASE(self._userFilter), cfilter)
		IF cfilter EQ 0 THEN MESSAGE, "Image could not be enhanced"
	ENDELSE

	mode = ABS(self._deltaOfImageDiff) GT !epsilon

	; Get target enhancement structure :
	enhanceStruct = all[self._probe, self._instrument, wave, filter, mode]

	; Auto-scale is ON :
	IF enhanceStruct._autoScale THEN BEGIN

		self->COMPUTEMINMAX, MINI = mini, MAXI = maxi
		self->CHECK_MIN_MAX, enhanceStruct._algorithm, mini, maxi

		; Update target structure :
		enhanceStruct._min = mini
		enhanceStruct._max = maxi

		; Get target enhancement structure :
		all[self._probe, self._instrument, wave, filter, mode] = enhanceStruct
		oFestivalEnhancementGUI->SETPROPERTY, ALL = all

	END

	CASE enhanceStruct._algorithm OF

		!LINEAR: image = BYTSCL(*self._pFilteredImage, MIN = enhanceStruct._min, MAX = enhanceStruct._max)
		!SQUARE_ROOT: image = BYTSCL(SQRT(*self._pFilteredImage > enhanceStruct._min), MIN = SQRT(enhanceStruct._min), MAX = SQRT(enhanceStruct._max))
		!LOG: image = BYTSCL(ALOG10(*self._pFilteredImage > enhanceStruct._min), MIN = ALOG10(enhanceStruct._min), MAX = ALOG10(enhanceStruct._max))

		ELSE: MESSAGE, "Unknown enhancement algorithm"

	END

	; Exclude !VALUES.F_NAN values :
	vec = WHERE(~FINITE(image), count)
	IF count GT 0 THEN BEGIN

		IF count EQ N_ELEMENTS(image) THEN $
			MESSAGE, "Image could not be enhanced"

		image[vec] = 0

	END

	; Store enhanced image :
	IF PTR_VALID(self._pEnhancedImage) THEN $
		*self._pEnhancedImage = TEMPORARY(image) ELSE $
		self._pEnhancedImage = PTR_NEW(image, /NO_COPY)

	; Update _enhancement object property :
	self._enhancement = enhanceStruct

	; Update object size in memory :
	self->UPDATESIZEINMEMORY

	RETURN, !TRUE

END

PRO FESTIVAL_DATA::CLEANUP

	; Free all pointers/objects :
	IF PTR_VALID(self._pFITSHeader) THEN $
		PTR_FREE, self._pFITSHeader

	IF PTR_VALID(self._pCalibratedImage) THEN $
		PTR_FREE, self._pCalibratedImage

	IF PTR_VALID(self._pFilteringSequence) THEN $
		PTR_FREE, self._pFilteringSequence

	IF PTR_VALID(self._pFilteredImage) THEN $
		PTR_FREE, self._pFilteredImage

	IF OBJ_VALID(self._oProjectedImage) THEN $
		OBJ_DESTROY, self._oProjectedImage

	IF PTR_VALID(self._pContourOfNonMissing) THEN $
		PTR_FREE, self._pContourOfNonMissing

	IF PTR_VALID(self._pProjectedUserDefinedMaskCoord) THEN $
		PTR_FREE, self._pProjectedUserDefinedMaskCoord

	IF PTR_VALID(self._pEnhancedImage) THEN $
		PTR_FREE, self._pEnhancedImage

	IF PTR_VALID(self._pProjCoord) THEN $
		PTR_FREE, self._pProjCoord

END

;+
; NAME:
;   FESTIVAL_DATA::INIT
;
; PURPOSE:
;   Object constructor.
;
; INPUTS:
;   None
;
; KEYWORD PARAMETERS:
;   FESTIVAL_SEARCH_RESULT : A reference to a valid FESTIVAL_SEARCH_RESULT object.
;
; OUTPUTS:
;   0: Init OK.
;	1: Init Failed
;
; MODIFICATION HISTORY:
;   29/08/06 - IAS ORSAY
;-
FUNCTION FESTIVAL_DATA::INIT, FESTIVAL_SEARCH_RESULT = oFestSearchResult, $
        BATCH_MODE_IS_ON = BatchModeIsON, $
        DATA_ENGINE = oDataEngine, $
		_EXTRA = e

	; Error handler :
	err = 0
	CATCH, err
	IF err NE 0 THEN BEGIN

		; Update FESTIVAL error log :
		FESTIVAL_UPDATEJOURNAL

		HELP, /MESSAGE, /LAST_MESSAGE
		IF !DEBUG EQ !TRUE THEN BEGIN

			PRINT, !error_state.msg

		END

		CATCH, /CANCEL
		RETURN, !FALSE

	END

	FESTIVAL_DATA_TYPES

	; Return value :
	out = !TRUE

	IF OBJ_VALID(oDataEngine) THEN BEGIN
		IF ~OBJ_ISA(oDataEngine, "FESTIVAL_DATA_ENGINE") THEN $
			MESSAGE,  "An invalid FESTIVAL_DATA_ENGINE object was found" ELSE $
			self._oDataEngine = oDataEngine
	END ELSE $
		MESSAGE,  "An invalid FESTIVAL_DATA_ENGINE object was found"

	; First way of initialising a FESTIVAL_DATA object :
	IF N_ELEMENTS(oFestSearchResult) GT 0 THEN BEGIN

	; It is an object :
	IF OBJ_VALID(oFestSearchResult) THEN BEGIN

 		IF OBJ_VALID(oFestSearchResult) THEN BEGIN

			; Defines some object properties :
			oFestSearchResult->GETPROPERTY, $

			; Returns image acquisition date :
			UTCDATE = utcdate, $

			; Returns probe name :
			F_PROBE = probe, $

			; Returns instrument name :
			F_INSTRUMENT = instrument, $

			; Returns file specification :
			FILESPECIFICATION = fileSpec, $

			; Returns fullfilepath
			FULLFILEPATH = fullFilepath, $

			; User wave :
			F_WAVE = wave, $

			; Filter :
			F_FILTER = filter, $

            FITSFILENAME= fitsfilename, $

            GETMETHOD = getmethod

            ; is it a stereo triplet ?
           	IF (probe NE !SOHO) THEN BEGIN

            	oFestSearchResult->GETPROPERTY, TRIPLET_REF = triplet_ref, POLAR=polar

                IF OBJ_VALID(triplet_ref[0]) THEN BEGIN

                   	; this is a triplet
                    triplet_ref[0]->getproperty, FULLFILEPATH = fullfilepath0,$
                    FITSFILE = fitsfilename0
                    triplet_ref[1]->getproperty, FULLFILEPATH = fullfilepath1, $
                    FITSFILE = fitsfilename1
                    triplet_ref[2]->getproperty, FULLFILEPATH = fullfilepath2, $
                    FITSFILE = fitsfilename2

                    self._fullFilePath = [ fullfilepath0, fullfilepath1, fullfilepath2 ]
                    self._matchedPolarity = !TRUE

	                case polar of
	                    'TB':self._polarityType = !TB
	                    'PB':self._polarityType = !PB
	                    'Mu':self._polarityType = !MU
	                    '%' :self._polarityType = !PERCENT
	                endcase

         		END ELSE BEGIN

                	self._matchedPolarity = !FALSE
					self._fullFilePath = fullFilePath

             	END

         	END ELSE BEGIN

            	self._matchedPolarity = !FALSE
				self._fullFilePath = fullFilePath

          	END

       		IF (1 EQ 1) THEN BEGIN

				; Store file specification :
				self._fileSpecification = fileSpec

	            IF (getmethod EQ 'vso') THEN BEGIN

	               	show_progress = ~keyword_set(BatchModeIsON)
	                VSO_DOWNLOAD_DIRECTORY = !FESTIVAL_SAVE_DIRECTORY+PATH_SEP()+'downloaded_data'+$
	                                          PATH_SEP()

	                http = obj_new('http')

                 	IF (self._matchedPolarity EQ !TRUE) THEN BEGIN

                        IF !DEBUG EQ !TRUE THEN BEGIN
                            print, "Files will be put in "+VSO_DOWNLOAD_DIRECTORY
                            print, "Downloading "+self._fullFilePath[0]
                        ENDIF

                        http->copy,fullfilepath0, OUT_DIR = VSO_DOWNLOAD_DIRECTORY, $
                                   PROGRESS = show_progress

                        self._fullFilePath[0] = VSO_DOWNLOAD_DIRECTORY+fitsfilename0

                        IF !DEBUG EQ !TRUE THEN BEGIN
                            print, "Downloading "+self._fullFilePath[1]
                        ENDIF

                        http->copy,fullfilepath1, OUT_DIR = VSO_DOWNLOAD_DIRECTORY, $
                                   PROGRESS = show_progress

                        self._fullFilePath[1] = VSO_DOWNLOAD_DIRECTORY+fitsfilename1


                        IF !DEBUG EQ !TRUE THEN BEGIN
                            print, "Downloading "+self._fullFilePath[2]
                        ENDIF

	                    http->copy,fullfilepath2, OUT_DIR = VSO_DOWNLOAD_DIRECTORY, $
	                               PROGRESS = show_progress

	                    self._fullFilePath[2] = VSO_DOWNLOAD_DIRECTORY+fitsfilename2

	                    IF !DEBUG EQ !TRUE THEN BEGIN
	                        print, "Triplet downloaded"
	                    ENDIF

           			END ELSE BEGIN

                        IF !DEBUG EQ !TRUE THEN BEGIN
                            print, "Files will be put in "+VSO_DOWNLOAD_DIRECTORY
                            print, "Downloading "+self._fullFilePath[0]
                        ENDIF

                        http->copy,fullFilePath, OUT_DIR = VSO_DOWNLOAD_DIRECTORY, $
                                   PROGRESS = show_progress

                        self._fullFilePath = VSO_DOWNLOAD_DIRECTORY+fitsfilename

                        IF !DEBUG EQ !TRUE THEN BEGIN
                            print, "File downloaded"
                        ENDIF

               		END

               		obj_destroy, http

				END

				; Store UTC date :
				self._UTCDate = utcdate

				; Store probe ID :
				self._probe = probe

				; Store wave :
				self._userWave = wave

				; Store filter :
				self._userFilter = filter

				; Initialise coordinate system :
				SWITCH self._probe OF

					!SOHO: BEGIN

						IF ~PTR_VALID(self._pProjCoord) THEN $
							self._pProjCoord = PTR_NEW({FESTIVAL_PROJCOORD})

						(*self._pProjCoord)._coordSystem = !HELIOCENTRIC_SOLAR_NORTH_UP

						BREAK

					END

					!STEREOA:
					!STEREOB: BEGIN

						IF ~PTR_VALID(self._pProjCoord) THEN $
							self._pProjCoord = PTR_NEW({FESTIVAL_PROJCOORD})

						(*self._pProjCoord)._coordSystem = !HELIOCENTRIC_ECLIPTIC
						BREAK

					END

					ELSE: MESSAGE, "Unknown probe"

				END

				; Store instrument ID :
				self._instrument = instrument

				; Store corresponding FESTIVAL_SEARCH_RESULT object :
				self._oRefSearchResult = oFestSearchResult

				; Initialise projection to "Mercator" :
				(*self._pProjCoord)._sMap.up_name = !PROJECTIONS[0]

				; Default filtering sequence is an empty string :
				self._pFilteringSequence = PTR_NEW('')

			END ELSE $
				out = !FALSE

		; An invalid FESTIVAL_SEARCH_RESULT object was passed :
		END ELSE $
			out = !FALSE

	; An invalid FESTIVAL_SEARCH_RESULT object was passed :
	END ELSE $
		out = !FALSE

	; Second way of initialising a FESTIVAL_DATA object :
	; This way allows to build a FESTIVAL_DATA difference object from 2 FESTIVAL_DATA objects :
	END ELSE IF N_ELEMENTS(e) GT 0 THEN BEGIN

		; Check extra-parameters validity :

		; Must contain 5 tags :
		IF N_TAGS(e) EQ 5 THEN BEGIN

			; Get tag names :
			tagNames = TAG_NAMES(e)

			; Check for tags availability :
			vec = WHERE(tagNames EQ 'FESTIVALDATA1', count0)
			vec = WHERE(tagNames EQ 'FESTIVALDATA2', count1)
			vec = WHERE(tagNames EQ 'FESTIVAL_FILTER_GUI', count2)
			vec = WHERE(tagNames EQ 'FESTIVAL_CALIBRATION_GUI', count3)
			vec = WHERE(tagNames EQ 'FESTIVAL_PROJCOORD_GUI', count4)

			; All required keywords are present :
			IF count0 GT 0 AND count1 GT 0 AND count2 GT 0 AND count3 GT 0 AND count4 GT 0 THEN BEGIN

				; Check for object validity :
				IF OBJ_VALID(e.festivalData1) AND OBJ_VALID(e.festivalData2) AND OBJ_VALID(e.festival_filter_gui) AND $
				OBJ_VALID(e.festival_calibration_gui) AND OBJ_VALID(e.festival_projcoord_gui) THEN BEGIN

					; Check for object classes :
					IF OBJ_ISA(e.festivalData1, "FESTIVAL_DATA") AND OBJ_ISA(e.festivalData2, "FESTIVAL_DATA") AND $
					OBJ_ISA(e.festival_filter_gui, "FESTIVAL_FILTER_GUI") AND OBJ_ISA(e.festival_calibration_gui, "FESTIVAL_CALIBRATION_GUI") AND $
					OBJ_ISA(e.festival_projcoord_gui, "FESTIVAL_PROJCOORD_GUI") THEN BEGIN

						; Everything is fine, build difference object :
						out = self->MAKE_DIFF(e.festivalData1, e.festivalData2, $
						e.festival_filter_gui, e.festival_calibration_gui, e.festival_projcoord_gui)

					END ELSE $
						out = !FALSE

				END ELSE $
					out = !FALSE

			END ELSE $
				out = !FALSE

		END ELSE $
			out = !FALSE
	END

	RETURN, out

END

; Define object properties :
PRO FESTIVAL_DATA__DEFINE

	tmp = {FESTIVAL_DATA, $

		; ******************** GENERAL OBJECT PROPERTIES ************************

		; Object file specifiation :
		_fileSpecification: '', $
		_fullFilePath: STRARR(3), $
		_matchedPolarity: !FALSE, $

		; UTC image acquisition date :
		_UTCDate: 0.0d, $

		; Probe identifier associated to taht image :
		_probe: 0, $

		; Instrument identifier associated to that image :
		_instrument: 0, $

		; Reference to the corresponding FESITVAL_SEARCH_RESULT object :
		_oRefSearchResult: OBJ_NEW(), $

		; Pointer to the FITS header structure associated to that image :
		_pFITSHeader: PTR_NEW(), $

		; Approximative memory used by that image :
		_sizeInMemory: 0L, $

		; Delay between the 2 images used for a difference image.
		; 0.0 indicates that the filtered image does not contain a difference :
		_deltaOfImageDiff: 0.0, $

		; **********************************************************************



		; ********************** OBJECT CALIBRATION PROPERTIES *****************

		; Calibration model used for that image :
		_calibrationArray: {FESTIVAL_CALIBRATION}, $

		; Pointer to the 32 bits calibrated image. Temporary buffer not saved in
		; history stack :
		_pCalibratedImage: PTR_NEW(), $

		; **********************************************************************



		; ********************** OBJECT FILTER PROPERTIES **********************

		; Pointer to the filtering sequence used for that image.
		; Pointer to an empty string means no filtering sequence :
		_pFilteringSequence: PTR_NEW(), $

		; Pointer to the filtered image :
		_pFilteredImage: PTR_NEW(), $

		; **********************************************************************



		; ***************** OBJECT PROJECTION/COORDINATES PROPERTIES ***********

		; Structure containing all information concerning the projection used to
		; compute the projected image and mask (projection structure mask).
		; TO BE MODIFIED. THIS STRUCTURE CONTAINS TOO MUCH INFORMATION :
		_pProjCoord: PTR_NEW(), $

		; 4 elements vector givng the projection limits lonMin, latMin, lonMax, latMax :
		_imageCoordinates: FLTARR(4), $

		; IDLGRIMAGE object created with the 8bit projected image :
		_oProjectedImage: OBJ_NEW(), $

		; UV coordinates of the 4 corners of the projected image :
                ;
                ; C0 = UVRange(*,0) = lower left corner
                ; C1 = UVRange(*,1) = lower right corner
                ; C2 = UVRange(*,2) = upper right corner
                ; C3 = UVRange(*,3) = upper right corner
                ;
                ;    C3 ---- C2
                ;     |      |
                ;     |      |
                ;    C0 ---- C1
                ;
                ; In general, it is not a rectangle (it depends on the projection used)
		_UVRange: FLTARR(2,4), $

		; Pointer to a byte array containing the contour of "good" values
		; after a projection was performed.
		_pContourOfNonMissing: PTR_NEW(), $

		; Pointer to an array of coordinates of the projected mask defined by the user :
		_pProjectedUserDefinedMaskCoord: PTR_NEW(), $

		; FESTIVAL_MASKING structure associated to that image :
		_userMaskParameters: {FESTIVAL_MASKING}, $

		; **********************************************************************



		; ********************** OBJECT ENHANCEMENT PROPERTIES *****************

		_enhancement: {FESTIVAL_ENHANCEMENT}, $

		; Pointer to the 8 bits enhances image. Temporary buffer not saved in
		; history stack :
		_pEnhancedImage: PTR_NEW(), $

		; **********************************************************************



		; ************************* OBJECT GEOMETRY PRPERTIES ******************

		_HAEX: 0.0d, $	;X Ares Ecliptic (m)
		_HAEY: 0.0d, $	;Y Ares Ecliptic (m)
		_HAEZ: 0.0d, $	;Z Ares Ecliptic (m)
		_SunDist: 0.0d, $	;Distance of the Sun (m)
		_SunDistNormalization: 1.0d, $	;Distance normalization factor
		_CarringtonLongitude:0.0d, $ ;degrees
		_CarringtonLatitude:0.0d, $  ;degrees
		_SolarNorthAngle:0.0d, $	;Angle between top of image and solar north (degrees)
		_EclipticSolarNorthAngle:0.0d, $	;Angle between Ecliptic north and solar north (degrees)


		; **********************************************************************



		; ************************* OBJECT ORDER PROPERTIES ********************

		_displayOrder: 0, $

		; **********************************************************************


		; **************************** OTHER PROPERTIES ************************
		_CRPIX1: 0.0d, $
		_CRPIX2: 0.0d, $
		_CDELT1: 0.0d, $
		_CDELT2: 0.0d, $
		_CRVAL1: 0.0d, $
		_CRVAL2: 0.0d, $
		_UVPerPixelResolution: DBLARR(2), $
		_oVisuElt: OBJ_NEW(), $

		_polarityType: 0, $
		_userFilter: '', $
		_userWave: '', $

		; Reference to the data engine object :
		_oDataEngine: OBJ_NEW()}

END