;+
; NAME: anyobs__define
;
;
; PURPOSE: Definiton of the anyobs ("any observation") superclass; the
;          subclasses hinobs and irisobs inherit this superclass. You will NOT
;          work with an anyobs, hinobs or irisobs object, the only purpose of
;          these classes is to pass on common methods to their
;          subclasses. I.e. you will work with an object of one of the
;          subclasses (or sub-subclasses) of hinobs (xrtobs, eisobs, sotobs,
;          sot1dobs or sotl2obs), or one of the sub classes of irisobs
;          (TBD). The main purpose of the objects of the sub-subclasses is to
;          plot an image of the data or a context image of the FOV.
;
;
;                                           (sdc_util)
;                                              |
;                                            ANYOBS    spectobs
;                                          /       \ /       |
;                                   hinobs         /\       /
;                                  /   |   \     /   \    /
;                            xrtobs sotobs eisobs   irisobs 
;                                     /  \
;                              sp1dobs    spl2obs
;
;
;
; METHODS: Public methods that may be applied to an instance of a subclass of
;          hinobs or irisobs (the latter being subclasses of anyobs).
;          
;
;          o->plot_image,i
;             Plots image i to the current device. 
;             Keywords: 
;                      /thumb: plot thumbnail image
;                      /yszthumb: y size of thumbnail
;                      /fit_device: fit window or z buffer to the image
;                      /coltab: call ::load_color_table, i.e. load the color
;                               table that the SDC team recommends. This keyword
;                               is never set in any XXXobs__define methods, but may
;                               be timesaving to set when working with objects 
;                               outside of the class definition.
;                      _extra: keywords to SSWIDL plot_image
;
;          o->plot_fov 
;             Plots the FOV of the object superimposed on the XRT 
;             fulldisk image closest in time to the data image.
;
;          col = load_color_tabel(i) 
;             Load a reccomended color table for image i. 
;
;          o->read_header
;             Reads header information, sets the structure tag header and 
;             all structure tags deriving from the header.
;
;          o->read_data
;             Reads both header and data, sets the structure tags header and
;             data, sets all structure tags deriving from the header.
;
;          o->is_set(/TAG) 
;             Checks if an int/foat/string or pointer tag is set,
;             i.e. if the int/float tag is NE -1, if the string NE 'MISSING'
;             and that the pointer is valid.  Returns 1 if set, 0 if not set.
;
;          tag = o->hget(/TAG)          
;              Returns the value of fits header tag TAG.
;
;
;          Most of the methods in this file are help methods (and help methods' 
;          help methods!) of the methods listed above. They are all defined as 
;          private, and may only be applied on an object by other methods of this
;          object. All private method names start with an underscore (apart from
;          ::get_XXX and ::set_XXX methods, which are called when the sdc_util 
;          methods ::get and ::set are called)
;       
;          Help methods named _ensure_TAG_NAME are setting the structure tags. 
;          These may call help methods' help methods named _TAG_NAME_HELP1, 
;          _TAG_NAME_HELP2 etc.
;
;          Help methods returning information that are not taken directly from a
;          single structure tag, but are extracting information based on a
;          structure tag and e.g. image number or the size of the image, are
;          named _extract_TAG_NAME/_extract_INFO. These may call help methods'
;          help methods named _TAG_NAME_HELP1/_INFO_HELP1, _TAG_NAME_HELP2/
;          _INFO_HELP2. 
;          Examples: im=o->_extract_image(i), orig=o->_extract_image_origin(i), 
;          pos=_extract_image_position(sz)
;
;
; SPECIAL CALLS: sdc_util__define.pro, prune_filename.pro,
;                gt_closest_index.pro, gt_fkw.pro
;
; WRITTEN: Terje Fredvik (terje.fredvik@astro.uio.no), UiO, April 2013
;
; MODIFICATION HISTORY:
;
;    15 Apr 2013 Terje Fredvik - First draft
;    May, Jun, Sep, Oct 2013 Terje Fredvik - Lots of improvements and additions
;    07 Nov 2013 Terje Fredvik - ::plot_image - when keyword compassneedle is
;    set ensure that the N is always drawn ahead of the arrow.
;    14 Nov 2013 Terje Fredvik - ::plot_image: fixed bug when COMPASSNEEDLE is set, the
;                                SAT_ROT is the rotation of the instrument,
;                                not the image. Shortened the headlength.
;    28 Feb 2014 Terje Fredvik -  ::plot_fov - Plot a rotated FOV if needed. 
;                                 The crosshair is now plotted with a dashed
;                                 line in the x direction of the instrument,the 
;                                 y direction of the instrument is marked with 
;                                 solid lines. An arrow head is added to mark
;                                 positive instrument y.
;    12. Mar 2014 Terje Fredvik   Minor changes in plot titles
;    28. Jan 2015 Terje Fredvik   Call ensure_mission_specific_properties (was
;                                 commented out) to set ver_rf0 for XRT files
;                                 (needed for limb fitting)
;    02. Feb 2015 Terje Fredvik   ::_plot_helio calls ::extract_rsun_arcsec()
;                                 also when fit_limb hasn't been set. Free all
;                                 structure tags pointers that need to be
;                                 recalculated if the header is reset.
;    05. Feb 2015 Terje Fredvik   Deleted ::_get_xycen_delta_and_rsun_arcsecs
;                                 and moved code to ::extract_rsun_arcsec and 
;                                ::extract_xycen_delta.
;    15. Aug 2017 Terje Fredvik  ::_ensure_file is now a function that returns
;                                0 if the file neither found locally nor on
;                                the SDC server. ::init now calls
;                                ::_ensure_file, and thus returns 0 when
;                                trying to initialize object with non-existent
;                                file (also object will be null). read_header
;                                and read_data no longer calls ::_ensure_file. 
;
;


FUNCTION anyobs::init, file, destdir=destdir, maxplotsz=maxplotsz, noprep=noprep
  o = self
  IF o->sdc_util::init(/quiet) EQ 0 THEN return,0
  
  default, destdir, '' ;; If not set, store files in current working directory

  o.file = file
  
  default, maxplotsz, 1024
  o.maxplotsz = maxplotsz
  
  o.thumbsmoothsz = 3
  
  default, noprep, 0
  o.prep = ~noprep
  
  o.nicons = -1
  
  o.xtitle = 'MISSING'
  o.ytitle = 'MISSING'
  o.rightytitle = '' ; Not 'MISSING'; this tag shall be set for EIS I/v/w maps!
  
  o.destdir = destdir
  

  return,o->_ensure_file()
END



PRO anyobs::cleanup
  IF ptr_valid(self.header) THEN ptr_free,self.header
  IF ptr_valid(self.data) THEN ptr_free,self.data  
  IF ptr_valid(self.image_origin) THEN ptr_free,self.image_origin  
  IF ptr_valid(self.image_scale) THEN ptr_free,self.image_scale  
  IF ptr_valid(self.title) THEN ptr_free,self.title  
  IF ptr_valid(self.ARn) THEN ptr_free,self.ARn
  IF ptr_valid(self.ARxy) THEN ptr_free,self.ARxy
  IF ptr_valid(self.xycen_delta) THEN ptr_free,self.xycen_delta
  IF ptr_valid(self.rsun_arcsec) THEN ptr_free,self.rsun_arcsec
  
  self->sdc_util::cleanup
  
  print,"Cleaning up (anyobs)"
END

FUNCTION anyobs::_gt_extra_title, dummy=dummy
  ;; dummy function
  return,''
END

;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++
;;              <plot_image and help methods>
;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++

PRO anyobs::plot_image, i, ii, thumb=thumb, yszthumb=yszthumb, $
                        fit_device=fit_device, coltab=coltab, limb=limb, $
                        fit_limb=fit_limb, $
                        charsize=charsize, background=background, $
                        color=color, invert=invert, statusok=statusok, $
                        xrange=xrange, yrange=yrange, $
                        notforcexticks=notforcexticks, medianvalue=medianvalue, $
                        AR=AR, clock=clock, compassneedle=compassneedle, _ref_extra=extra
  ;;+
  ;; Make a plot of image i, with axes, using the color table preferred by the
  ;; SDC science data specialist. If i is not specified, i is set to 0 (i.e. the
  ;; first image is plotted)
  ;;
  ;; KEYWORDS: 
  ;;          thumb:      if set, plot thumbnail image
  ;;          yszthumb:   y size in pixels of thumbnail image
  ;;          fit_device: if set, call _fit_device
  ;;          coltab:     load user provided color table
  ;;          limb:       plot solar grid and mark limb (if within FOV)
  ;;          fit_limb:   Correct the x/ycen of the fits header, may be set to 1 or 2:
  ;;                      1: use xycen_delta structure tag if set. If not set, check if the 
  ;;                      x/ycencorrections are present in the SDC archive. If so
  ;;                      download the values, if not calculte them and set structure tags
  ;;                      2: Force limb fitting, even if the xycen_delta structure tag is
  ;;                      already set or if the values are present in the archive
  ;;          charsize:   sent to SSW plot_image
  ;;          background: -"-
  ;;          color:      -"-
  ;;          invert: invert color table of image. NOTE- EIS and IRIS intensity images are
  ;;                   inverted by default (i.e. dark means high
  ;;                   intensity). Setting the invert keyword for intensity
  ;;                   images will do a double invert, i.e. dark means low
  ;;                   intensity.
  ;;          statusok: The value of keyword statusok is set to 0 if
  ;;                    extract_image returns -1, i.e. if this image is a
  ;;                    slot or wide window slit observation and keyword
  ;;                    velocity or width are set (by the _ref_extra keyword).
  ;;                    If statusok is 0 no image will be plotted, return to caller.
  ;;          x/yrange: Array[2] giving the start and end (physical) coordinates of 
  ;;                    a sub FOV to plot.
  ;;          notforcexticks: If set, let IDL decide the number of xticks
  ;;          medianvalue: Return median value of the image if set to a namedd 
  ;;                       variable
  ;;          AR: If set, plot NOAA AR numbers on the image
  ;;          clock: If set, display an analog and a digital clock on the image
  ;;          _ref_extra: Keywords not accepted by plot_image is passed to the
  ;;                      subclasses methods by reference. See the subclass
  ;;                      methods to see which keywords they accept!
  ;;-
  
  
  o = self
  
  default, charsize, 1
  default, background, 255
  default, color, 255-background
  default, fit_limb, 0 ;; Use x/ycen values from the fits header as default
  
  find_coltab = (n_elements(coltab) EQ 1) ? 0 : 1 
  
  IF n_params() EQ 0 THEN i = 0 ; If image no. is not specified, plot first one
  
  IF keyword_set(AR) THEN BEGIN 
     IF ptr_valid(o->get(/ARn)) THEN BEGIN
        ARn = *(o->get(/ARn))
        ARxy = *(o->get(/ARxy))
     ENDIF ELSE BEGIN 
        date_obs = o->hget(/date_obs)
        noaa = obj_new('noaa_sdc', date_obs)
        o->extract_pointinginfo, xcen, ycen, fovx, fovy
        
        noaa->get_srs_info, xcen, ycen, fovx, fovy, date_obs,$
                            ARn=ARn, srsxy=ARn, ARdistance=ARdistance, $
                            area=area, $
                            zurich=zurich, nspots=nspots, magtype=magtype, $
                            offlimb=offlimb
        
        obj_destroy,noaa
        
        o->set, ARn=ptr_new(ARn)
        o->set, ARxy=ptr_new(ARxy)
     ENDELSE
       
  ENDIF
  
  
  im = o->extract_image(i, ii = ii, medianvalue = medianvalue, _extra=extra)
  
  sz = size(im)
  IF sz[0] EQ 0 THEN BEGIN 
     statusOK = 0
     return
  ENDIF ELSE statusok = 1
  
  ;; Parameters that (may) have different values for different data windows
  ;; Note that o->_description() is empty for all instruments except IRIS/SPEC
  ;; for the time being
  extratitle = o->_gt_extra_title(_extra = extra)
  
  title = o->_extract_title(i) + ' ' + o->_description() + ' ' + $
          trim(sz[1]) + 'x' + trim(sz[2]) + ' pix' + extratitle 
  
  origin = o->_extract_image_origin(i)
  scale = o->_extract_image_scale(i)
  
  ; Check for NaNs (temp. fix for SOT/FG files)
  ;
  IF origin[0] NE origin[0] THEN BEGIN
     o->read_header,header_out=orig_fits_header
     xcen = fxpar(orig_fits_header,'XCEN')
     ycen = fxpar(orig_fits_header,'YCEN')
     fovx = fxpar(orig_fits_header,'FOVX')
     fovy = fxpar(orig_fits_header,'FOVY')
     origin = [xcen-fovx/2.0,ycen-fovy/2.0]
  END
  
  o->_image_trim, im, origin, scale, xrange=xrange, yrange=yrange
  
  IF keyword_set(invert) THEN im = 255-bytscl(im)
  
  IF keyword_set(fit_device) THEN o->_fit_device, sz
  IF find_coltab THEN dummy = o->load_color_table(i, _extra=extra) ELSE $
     loadct,coltab
  
  position = o->_extract_image_position(sz)
  IF position[0] EQ -1 THEN delvarx, position
  
  IF NOT keyword_set(thumb) THEN BEGIN
     ;; If sz[1] [50,150> xticks=2, if [0,50> xticks = 1, if [120,->> let IDL
     ;; decide:
     IF NOT keyword_set(notforcexticks) THEN $
        IF sz[1] LT 150 THEN xticks = (sz[1] LT 50) ? 1 : 2 ELSE xticks = 0
        
     plot_image, im, $
                 origin=origin, scale=scale, position=position, $
                 xtitle=o->get(/xtitle), ytitle=o->get(/ytitle), $
                 title=title, subtitle=o->get(/subtitle), $
                 xticks=xticks,charsize=charsize, /noscale, $
                 background=background,color=color
     
     
     IF keyword_set(AR) THEN FOR i=0,n_elements(ARn)-1 DO BEGIN 
        xyouts,arxy[i,0],arxy[i,1],trim(arn[i]),charsize=1.5,charthick=3,color=0;255
        xyouts,arxy[i,0],arxy[i,1],trim(arn[i]),charsize=1.5,charthick=1,color=255;0
     ENDFOR
     
     ;; Overplot a solar grid + solar limb (if in the FOV) for Solar X VS Solar
     ;; Y images
     IF keyword_set(limb) THEN IF o->get_xtitle() EQ 'Solar X (arc sec)' THEN o->_plot_helio, fit_limb = fit_limb
     
     rightytitle = o->get(/rightytitle)
     axis, /yaxis, ytitle=rightytitle, yticks=1, ytickname=['.', '.'], /yst, $
           charsize=charsize, color=color
     
     IF keyword_set(clock) THEN BEGIN
        ;; Plot an analog clock in the bottom left corner of the window, 2
        ;; pixels from the window edges. Print date_obs (without ms) to the
        ;; right of the clock
        xvsz = !d.x_vsize
        yvsz = !d.y_vsize
        xleftplot_dev = xvsz*!x.window[0]
        ybottomplot_dev = yvsz*!y.window[0]
        minplot_norm = min([xleftplot_dev,ybottomplot_dev])/xvsz
        
        date_obs = o->extract_date_obs(i)
        
        charsz = (!d.name EQ 'X') ? 1 : 0.8
        
        xyouts, minplot_norm+5./xvsz,2./yvsz, strmid(date_obs,0,10)+' '+$
                strmid(date_obs,11,8),color=100,charsize=charsz, /norm
        
        sdc_clock, strmid(date_obs,11,8), size=minplot_norm, $
                   pos=[2./xvsz,2./yvsz], clockcol=100
     ENDIF
     
     IF keyword_set(compassneedle) THEN BEGIN
        ;; Plot a compass needle pointing towords north in the left lower
        ;; corner above the clock. Useful for instruments that are rotated,
        ;; which often is the case for IRIS observations.
        sat_rot = o->hget(/sat_rot)
        IF typename(sat_rot) NE 'STRING' THEN BEGIN 
           ;; The y centre of the arrow should be placed halfway between the
           ;; lower window boundary and the lower plot axis
           y0 =yvsz*!y.window[0]/2.
           
           ;; The arrow should be a fraction of the window boundary /plot axis distance
           arrowlen = y0*0.55
           
           ;; The x centre should be close to the right window border
           x0 = !d.x_vsize-arrowlen*2.
           
           arrowcol = 80
           
           ;; Make the sat_rot into radians, calculate the start and end
           ;; positions of the arrow
           sat_rot_rad = 2*!pi/360*(360-sat_rot)
           dx = sin(sat_rot_rad)*arrowlen
           dy = cos(sat_rot_rad)*arrowlen
           plots,[x0-dx,x0+dx],[y0-dy,y0+dy],color=arrowcol,/dev
           
           ;; No arrow without a head! The two head lines should be headlen
           ;; pixels big and the angle between the head lines and the arrow
           ;; line should be headangle. 
           headlen = 10 ;; Length of the head lines
           headangle = 25.*2*!pi/360 ;; Angle of head lines relative to arrow line
           
           ;; The right head line (relative to the direction of the arrow)
           dxr = sin(sat_rot_rad-headangle)*headlen
           dyr = cos(sat_rot_rad-headangle)*headlen
           plots,[x0+dx-dxr, x0+dx],[y0+dy-dyr, y0+dy],color=arrowcol,/dev
           
           ;; The left head line (relative to the direction of the arrow)
           dyl = sin(!pi/2-sat_rot_rad-headangle)*headlen
           dxl = cos(!pi/2-sat_rot_rad-headangle)*headlen
           plots,[x0+dx-dxl, x0+dx],[y0+dy-dyl, y0+dy],color=arrowcol,/dev
           
      
           ;; Draw an N that is always placed ahead of the arrow head.
           ;; The tip of the arrow is at (x0+dx, y0+dy), we want to place the
           ;; centre of the diagonal of the N a bit further in the direction
           ;; of the arrow, i.e. at (nx0, ny0) = (x0+dx + deltax, y0+dy +
           ;; deltay), where deltax and deltay is frac*dx and frac*dy. 
           frac = 0.4
           nx0 = x0 + dx + frac*dx
           ny0 = y0 + dy + frac*dy
           nx = 3 ; Width of the N
           ny = 5 ; Height of the N      
           ncol = 180 ; Color table index of the N
         
           plots,[nx0-nx,nx0-nx],[ny0+ny, ny0-ny],color=ncol,/dev ; Left vertical line
           plots,[nx0-nx,nx0+nx],[ny0+ny, ny0-ny],color=ncol,/dev ; Diagonal
           plots,[nx0+nx,nx0+nx],[ny0+ny, ny0-ny],color=ncol,/dev ; Right vertical line
        
        ENDIF
        
        
     ENDIF
     
     
  ENDIF ELSE BEGIN
     
     thumb = o->_extract_thumb(im, sz, position, scale, $
                               yszthumb = yszthumb, _extra = extra)
     
     tv,thumb
  ENDELSE
  
  o->_erase_temp_variables
  
END

FUNCTION anyobs::_description
  self->private
  return,''
END

FUNCTION anyobs::extract_date_obs, i
  ;; For all instruments with one and only one observation per file, the
  ;; date_obs is simply the date_obs of the header.
  return, self->hget(/date_obs)
END


;; <extract_image and help methods>


FUNCTION anyobs::extract_image, i, data, medianvalue=medianvalue, _extra=extra; Overridden in eis and sp1dobs
  ;; Extract image number i from data, return bytescaled and sigranged image
  
  default, i, 0
  returndata = (n_params() EQ 2) ? 1 : 0
  
  o = self
    
  data = o->get(/data)
  
  o->_image_number_exist,i
  
  naxis = gt_fkw(*(o->get(/header)), 'NAXIS')
  IF naxis EQ 4 THEN BEGIN
                                     ;  Find 2D indices of image number i:
     ind=array_indices(reform(temporary((*data)[0,0,*,*])),i)
                                ; Some datacubes are [x,y,1,n] instead of 
                                ; [x,y,2,n]. Must manipulate ind in this case:
     IF n_elements(ind) EQ 1 THEN ind = [0,ind]
     
     im = (*data)[*,*,ind[0], ind[1]] 
  ENDIF ELSE IF naxis EQ 3 THEN im = (*data)[*,*,i] ELSE im =  *data
  
  IF arg_present(medianvalue) THEN medianvalue = median(im)
  
  IF returndata THEN data = im ELSE im = o->_image_final_touch(im, _extra = extra) ; e.g. sigrange(im), check for wrap-around
  
  return, im
  
END

PRO anyobs::_image_number_exist,i
  self->private
  nicons = self->get(/nicons)
  IF i GE nicons THEN message,'Image number must be in the range [0,'+$
                              trim(nicons-1) + ']'
END

FUNCTION anyobs::_image_final_touch, im
  ;; The subclasses may override this help method with a code that actually does
  ;; something to the data, e.g. sigrange(im), sigrange(alog10(im)), check for
  ;; 16 bit wrap around effect, etc.
  self->private
  return,im
END



PRO anyobs::_fit_device, sz
  ;; Make a window/set the size of the Z buffer to a size that is close to,
  ;; but somewhat bigger the size of the image to display. 
  self->private
  
  maxplotsz = self->get(/maxplotsz)
  minxsz = 600
  minysz = 640
  
  maxadd = 70
  minadd = 5
  xadd = (sz[1] LT sz[2]) ? maxadd:minadd
  yadd = (sz[2] LT sz[1]) ? maxadd:minadd
  
  xs = sz[1]+xadd > minxsz
  ys = sz[2]+yadd > minysz 
  
  ;; Decrease xs and ys if xs or ys is greater than maxplotsz
  IF xs > ys GT maxplotsz THEN BEGIN
     xyratio = float(xs)/ys
     IF xs GT ys THEN BEGIN
        xs = maxplotsz
        ys = xs/xyratio
     ENDIF ELSE BEGIN
        ys = maxplotsz
        xs = ys*xyratio
     ENDELSE
  ENDIF
  
  IF !d.name EQ 'Z' THEN device, set_resolution=[xs,ys] ELSE BEGIN 
     ;; Device is X
     device,get_window_position=winpos
     menubaradd = (!version.os_name EQ 'Mac OS X') ? 21 : 0
     window,xs=xs, ys=ys, xp=winpos[0], yp=winpos[1]+menubaradd, retain=2
  ENDELSE
  

END


;; <_extract_image_position, _extract_image_scale, _extract_title and help methods>

FUNCTION anyobs::_extract_image_position, sz
  ;; Based on the size of the data cube, calculate the position of the image
  ;; in order to retain the aspect ratio. This method is only applied to
  ;; objects which images have different units on the axis; we assume the
  ;; scale of the axis to be equal.
  o = self
  o->private
  
  xtitle = o->get(/xtitle)
  ytitle = o->get(/ytitle) 
  IF strmid(xtitle,0,1) NE strmid(ytitle,0,1) THEN $
     return, o->_image_position(sz) ELSE return,-1
END

FUNCTION anyobs::_image_position,sz
  o = self
  o->private
  
  data_xs = float(sz[1])
  data_ys = float(sz[2])
  
  IF o.xtitle EQ  'Time (min)' THEN $
     IF data_ys/data_xs GT 2 THEN data_xs = data_ys/2.

  window_xs = float(!d.x_size)
  window_ys = float(!d.y_size)
  
  mxpos = 0.95
  
  IF data_xs/data_ys LT window_xs/window_ys THEN BEGIN
     ;PRINT,'Wide plot window'
     pos =  [0.10, 0.15, -1, 0.90]
     pos = o->_image_position_x1(pos, data_xs, data_ys, window_xs, window_ys) 
     IF pos[2] GT mxpos THEN BEGIN
        ;print,'Wide: GT mxpos'
        pos[2] = mxpos
        pos = o->_image_position_y1(pos, data_xs, data_ys, window_xs, window_ys) 
     ENDIF 
     offset = (1-pos[2])/2.
     pos[0] += offset
     pos[2] += offset
  ENDIF ELSE BEGIN
     ;print,'Narrow plot window'
     pos = [0.15, 0.1, 0.95, -1]
     pos = o->_image_position_y1(pos, data_xs, data_ys, window_xs, window_ys) 
     IF pos[3] GT mxpos THEN BEGIN
        ;print,'Narrow: GT mxpos'
        pos[3] = mxpos 
        pos = o->_image_position_x1(pos, data_xs, data_ys, window_xs, window_ys) 
     ENDIF
     offset = (1-pos[3])/2.
     pos[1] += offset
     pos[3] += offset
  ENDELSE
  
  RETURN, pos
END

FUNCTION anyobs::_image_position_x1,  pos, data_xs, data_ys, window_xs, window_ys 
  self->private
  pos[2] =  ((pos[3]*window_ys-pos[1]*window_ys)/ $
             (data_ys/data_xs) + pos[0]*window_xs)/window_xs
  return, pos
END

FUNCTION anyobs::_image_position_y1,  pos, data_xs, data_ys, window_xs, window_ys 
  self->private
  pos[3] = ((window_xs*pos[2] - pos[0]*window_xs)*(data_ys/data_xs) $
            + pos[1]*window_ys)/window_ys 
  return, pos
END


FUNCTION anyobs::_extract_image_origin, i
  ;; Return the image origin (coordinate of bottom left pixel) of image i
  self->private
  image_origin = self->get(/image_origin)
  IF (size(image_origin))[0] EQ 2 THEN return, reform(image_origin[i,*])
  return, image_origin
END


FUNCTION anyobs::_extract_image_scale, i ; Overridden in eisos
  ;; Return the x and y image scale of image i
  self->private
  image_scale = self->get(/image_scale)
  IF (size(image_scale))[0] EQ 2 THEN return, reform(image_scale[i,*])
  return, image_scale
END

PRO anyobs::_image_trim, im, origin, scale, xrange=xrange, yrange=yrange
  ;; Trim image if xrange and/or yrange is set; extract the part of the image
  ;; described by the x/y ranges. The ranges are given in arc secs, secs or
  ;; Angstrom, depending on the image type
  o = self
  o->private
  IF keyword_set(xrange) THEN BEGIN
     x = findgen((size(im))[1])
     xcoord = scale[0]*x+origin[0]
     xind = [gt_closest_index(xrange[0], xcoord,/noprint), $
             gt_closest_index(xrange[1], xcoord,/noprint)]
     
     origin[0] = xrange[0] > origin[0]
     im = im[xind[0]:xind[1],*]
  ENDIF 
  IF keyword_set(yrange) THEN BEGIN
     y = findgen((size(im))[2])
     ycoord = scale[1]*y+origin[1]
     yind = [gt_closest_index(yrange[0], ycoord,/noprint), $
             gt_closest_index(yrange[1], ycoord,/noprint)]
     
     origin[1] = yrange[0] > origin[1]
     im = im[*,yind[0]:yind[1]]
  ENDIF
END

FUNCTION anyobs::_plot_image_frame_missing_data, im
  self->private
  sz = size(im)
  framecolor = 2
  im[*,0] = framecolor
  im[0 > sz[1]-1,*] = framecolor  ;; im (size(im))[1] EQ 2 color im[1,*],not [0,*]
  im[*,sz[2]-1] = framecolor
  im[sz[1]-1,*] = framecolor
  return,im
END
PRO anyobs::_plot_helio, fit_limb=fit_limb
  self->private
  
  default, fit_limb, 0  ;; If not set do not fit limb, use x/ycen from header
  
  ;; fit_limb may be set to 1 or 2:
  ;; 1: use xycen_delta structure tag if set. If not set, check if the 
  ;;    x/ycencorrections are present in the SDC archive. If so
  ;;    download the values, if not calculte them and set structure tags
  ;; 2: Force limb fitting, even if the xycen_delta structure tag is
  ;;    already set or if the values are present in the archive
  
  rsun_arcsec = self->extract_rsun_arcsec() 
  
  xycen_delta = self->extract_xycen_delta(fit_limb=fit_limb)
  
  ;; Plot solar grid.
  sdc_plot_helio, date_obs, xycen_delta[0], xycen_delta[1], $
                  radius=rsun_arcsec, /over,/limb, lmcolor=0,  gstyle=2, $
                  gcolor=0, grid=10
  sdc_plot_helio, date_obs, xycen_delta[0], xycen_delta[1], $
                  radius=rsun_arcsec, /over, gstyle=1, gcolor=255, grid=10
END

FUNCTION anyobs::extract_xycen_delta, fit_limb=fit_limb
  IF ~ptr_valid(self.xycen_delta) THEN $
     self->_get_xycen_delta, fit_limb=fit_limb
 
  return, *self.xycen_delta
END

PRO anyobs::_get_xycen_delta, fit_limb=fit_limb
  ;; Overridden in xrtobs__define
  self->private
  IF fit_limb GT 0 THEN $
     box_message,'Pointing correction by limb fitting not implemented for '+$
                 self->hget(/instrume)
  
  self->set, xycen_delta=ptr_new([0,0])  
END



FUNCTION anyobs::extract_rsun_arcsec
  IF ~ptr_valid(self.rsun_arcsec) THEN $
     self->_get_rsun_arcsec
  return, *self.rsun_arcsec
END
PRO anyobs::_get_rsun_arcsec, fit_limb=fit_limb
  ;; Overridden in xrtobs__define
  self->private
   
  dummy = get_sun(self->hget(/DATE_OBS), sd=rsun_arcsec) 

  self->set, rsun_arcsec=ptr_new(rsun_arcsec)
END

  
FUNCTION anyobs::_extract_title, i
  self->private
  return, (*(self->get(/title)))[i]  
END

;; <_extract_image_position, _extract_image_scale, _extract_title and help methods>


FUNCTION anyobs::_extract_thumb, im, sz, position, scale, $
                                 yszthumb=yszthumb, _extra=extra
  o = self
  o->private
  
  default, yszthumb, 64
  scl = (n_elements(position) EQ 0) ? scale:[1,1]
  xsz = sz[1]*scl[0]*yszthumb/(sz[2]*scl[1])
  sm =  o->get(/thumbsmoothsz)
  thumb =  congrid(smooth(im, sm < sz[1]-1 < yszthumb,/nan,/edge_truncate), xsz > 1, yszthumb)
  ;; If all pixels are bad, draw a frame around the empty image 
  IF max(thumb) EQ min(thumb) THEN $
     thumb = o->_plot_image_frame_missing_data(thumb)
  erase 
  
  ;; In some rare cases the x size of the Z buffer may be set incorrectly,
  ;; e.g. for EIS observations with very few raster positions but covering
  ;; a large area in x (by using large step sizes). anyobs::_fit_device
  ;; uses the pixel size of the data to fit the device, not the
  ;; fovx... This should be fixed sometime, so I don't have to use this hack: 
  IF !d.name EQ 'Z' THEN IF xsz GT !d.x_size THEN $
     device,set_resolution=[xsz,!d.y_size]
  
  thumb = o->_thumb_final_touch(thumb, _extra = extra)
  return, thumb
  
END

FUNCTION anyobs::_thumb_final_touch, thumb, _extra=extra
  self->private
  return, bytscl(thumb,/nan)
END


;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++
;;              <plot_fov and help methods>
;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++


PRO anyobs::plot_fov,thumb=thumb, quiet=quiet
  ;; Plot the FOV of the observation (assuming the same FOV of all images)
    
  self->_cenAndfov, xcen, ycen, fovx, fovy
  
  ;; For some instruments there's a theoretical possibility for several values
  ;; of the pointing parameters. We need one of each:
  xcen = max(xcen)
  ycen = max(ycen)
  fovx = max(fovx)
  fovy = max(fovy)
 
  sat_rot = self->hget(/sat_rot)
  sat_rot_rad = (360-sat_rot)*!pi/180  

  chcolor  = 221                ; color of crosshair

  
  ; Plot a rotated box, get the coordinates of the vertices
  sdc_tvbox,[fovx, fovy], xcen, ycen, angle=sat_rot, color=chcolor, $
            xvertices=xvertices, yvertices=yvertices
  
  ; Fill the box
  polyfill, [xvertices[0], xvertices[1], xvertices[2], xvertices[3]], $
            [yvertices[0], yvertices[1], yvertices[2], yvertices[3]],color=254
  
  ;; Now we need to replot the box, since the box needs to have the same
  ;; colour as the crosshair, if not it gets erased in the composite XRT/FOV image.
  sdc_tvbox,[fovx, fovy], xcen, ycen, angle=sat_rot, color=chcolor
  
  ;; Now the crosshair. 
  ;; The four lines of the crosshair should each be chlenght arcsecs long and 
  ;; should start fovdist arcecs from the outer edges of the FOV.
  fovdist = 20    ;; Distance in arcsecs between FOV centre and start of crosshair  
  chlength = 750   ;; length of crosshair lines, arcsecs
  
  ;; We split the crosshair into the two lines that are along the instrument x 
  ;; direction and the two lines that are in the instrument y direction.
  ;;
  ;; First the lines in instrument y. If sat_rot_rad is the angle between the
  ;; coordinate system y and the instrument y direction, then beta is the angle
  ;; between the instrument y axis and the coordinate x axis.
  beta = !pi/2-sat_rot_rad
  
  ;; dx_y and dy_y are the x and y components of the distance between the FOV
  ;; centre and the END of the y crosshair lines.
  ;; dxx_y and dyy_y are the x and y components of the distance between the
  ;; FOV centre and the BEGINNING of the y crosshair lines.
  dx_y  = cos(beta)*(fovy/2. + fovdist + chlength)
  dxx_y = cos(beta)*(fovy/2. + fovdist)
  dy_y  = sin(beta)*(fovy/2. + fovdist + chlength)
  dyy_y = sin(beta)*(fovy/2. + fovdist)
  
  ;; Plot the two crosshair lines that are paralell to instrument y
  plots, [xcen-dxx_y, xcen-dx_y],[ycen+dyy_y, ycen+dy_y],color=chcolor
  plots, [xcen+dx_y, xcen+dxx_y],[ycen-dy_y, ycen-dyy_y],color=chcolor

  
  ;; The same thing for the crosshair lines in the instrument x direction. 
  ;; dx_x and dy_x are the x and y components of the distance between the FOV 
  ;; centre and the END of the x crosshair lines. 
  ;; dxx_x and dyy_x are the x and y components of the distance between FOV 
  ;; centre and the BEGINNING of the x crosshair lines.
  dx_x  = cos(sat_rot_rad)*(fovx/2. + fovdist + chlength)
  dxx_x = cos(sat_rot_rad)*(fovx/2. + fovdist)
  dy_x  = sin(sat_rot_rad)*(fovx/2. + fovdist + chlength)
  dyy_x = sin(sat_rot_rad)*(fovx/2. + fovdist)
  
  ;; Plot the two crosshair lines that are paralell to instrument x
  plots, [xcen-dx_x, xcen-dxx_x],[ycen-dy_x, ycen-dyy_x],color=chcolor,line=2
  plots, [xcen+dxx_x, xcen+dx_x],[ycen+dyy_x, ycen+dy_x],color=chcolor,line=2
  
  ;; Mark the instrument up direction by placing an arrow head at the end of
  ;; upper crosshair in instrument y direction. The lines of the head of
  ;; the arrow should be headlen pixels big and the angle between the head
  ;; lines and the arrow line should be headangle. 
  headlen = 50                       ;; Length of the head lines
  headangle = (90-25)*2*!pi/360      ;; Angle of head lines relative to arrow line
  
  ;; The left head line (relative to the direction of the arrow)
  dxl = sin(beta-headangle)*headlen
  dyl = cos(beta-headangle)*headlen
  plots,[xcen-dx_y, xcen-dx_y-dxl],[ycen+dy_y, ycen+dy_y-dyl],color=chcolor
  
  ;; The right head line (relative to the direction of the arrow) 
  dxr = cos(sat_rot_rad-headangle)*headlen
  dyr = sin(sat_rot_rad-headangle)*headlen
  plots,[xcen-dx_y, xcen-dx_y+dxr],[ycen+dy_y, ycen+dy_y+dyr],color=chcolor        
   
  IF NOT keyword_set(thumb) THEN $
     xyouts, 0.5, 0.95, prune_filename(self->get(/file)), align=0.5,/normal
  
  IF NOT keyword_set(quiet) THEN $
     xyouts, 0.02, 0.01, self->get(/version), color=255,/normal,charsize=0.9
  
END

PRO anyobs::_cenAndfov, xcen, ycen, fovx, fovy ; Overridden in sp15/eisobs
  self->private
  header = *(self->get(/header))
  
  xcen = gt_fkw(header,'XCEN')
  ycen = gt_fkw(header,'YCEN')
  fovx = gt_fkw(header,'FOVX')
  fovy = gt_fkw(header,'FOVY')
  
END


;; -----------------------------------------------------
;;              </plot_fov and help methods>  
;; -----------------------------------------------------



;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++
;              <load_color_table and help methods>
;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++

FUNCTION anyobs::load_color_table, i, _ref_extra=extra
  ;; Load the color table recommended by the SDC team. Extra keywords not
  ;; accepted by load_color_table is passed to the subclasses _color_table_txt
  ;; and _load_color_table_special by reference.
  
  wanted_ct_txt = self->_color_table_txt(i, _extra=extra)
  
  IF wanted_ct_txt EQ 'VELOCITY' OR wanted_ct_txt EQ 'WIDTH' OR wanted_ct_txt EQ 'RADFILT' THEN $
     self->_load_color_table_special, wanted_ct_txt ELSE BEGIN 
     stc_coltabs = self->get(/coltabs)
     wanted_ct = stc_coltabs.(where(tag_names(stc_coltabs) EQ wanted_ct_txt))
     loadct, wanted_ct,/silent
  ENDELSE
  
  tvlct, r, g, b, /get
  
  return, {r:r, g:g, b:b}
  
END

FUNCTION anyobs::_color_table_txt,i, _extra=extra ; Overridden in sot/sp1d/eisobs
  self->private
  return,'INTENSITY'
END

PRO anyobs::_load_color_table_special, wanted_ct_txt
  self->private
 
  velocity = wanted_ct_txt EQ 'VELOCITY'
  width = wanted_ct_txt EQ 'WIDTH'
  radialfilter = wanted_ct_txt EQ 'RADFILT'
  
  IF radialfilter THEN BEGIN 
     ssw_colors,0,r,g,b,_extra='EIT'
     tvlct,r,g,b
  ENDIF ELSE BEGIN 
     eis_colors, velocity=velocity, width=width
     
     tvlct,r,g,b,/get
     
     IF velocity THEN BEGIN 
        r[0] = 0
        g[0] = 0
        b[0] = 0
        tvlct,r,g,b
     ENDIF
     
  ENDELSE 
END

PRO anyobs::_erase_temp_variables
  self->private
END

;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++
;;               <Misc checks on file name>
;; ++++++++++++++++++++++++++++++++++++++++++++++++++++++

FUNCTION anyobs::_ensure_file
  file_ok = 1 
  IF ~file_test(self.file,/read) THEN BEGIN
     message,'File not found, fetching from default file server',/info
     o=obj_new('osdc')
     o->show,'FILE,INSTRUME,DATE_OBS'
     o->limit,0
     o->condition,'anon: y'
     o->condition,'FILE: '+self.file
     o->order,''
     o->search,out
     IF o->nmatch() NE 0 THEN BEGIN
        paths = o->paths(out,/fetch)
        self.file = paths[0]
     ENDIF ELSE BEGIN 
        message,'File not found on file server either!',/info
        file_ok = 0 
     ENDELSE 
  ENDIF
  
  return, file_ok
END


PRO anyobs::read_header,header_out=header_out
  ;; Read the header. In the case of fits files wiht a single image extension
  ;; (XRT, some SOT files) the complete header will be read by fxhread (called
  ;; by anyobs::_read_header_fxmethod). Known bug- for SOT files with multiple
  ;; images the header isn't read, instead read_data must be called! For files 
  ;; with binary extensisions (e.g. EIS) fxbopen will be called instead, and the headers 
  ;; for the extrensions will be added to the header. For files with multiple
  ;; image extensions and ascii extensions (e.g. IRIS) only the main header,
  ;; i.e. the header of extension 0, will be put in the anyobs header. The
  ;; headers for the individual windows should be accessed by the data
  ;; object's gethdr method, see spectobs::extract_header
  ;;
  ;; Make sure we *have* the file, then call the
  ;; instrument-specific read function. Later versions may also choose to get
  ;; the header directly from the archive.
  o = self
  
  openr,lun,o.file,/get_lun,/compress
  hdr = o->_read_header_fxmethod(lun)
  free_lun,lun
  
  IF arg_present(header_out) THEN header_out = hdr
  
  IF ptr_valid(o.header) THEN ptr_free,o.header
  o->set, header=ptr_new(hdr,/no_copy)
END

FUNCTION anyobs::_read_header_fxmethod, lun  ; overridden in eisobs__define
  self->private
  fxhread, lun, hdr
  return, hdr
END

PRO anyobs::set_header, header
  o = self 
  o->private
  
  ;; If the header is reset we need to recalcualte all structure tags that are
  ;; calculated based on header information. We therefore free all pointers,
  ;; and the pointers will be set to the correct values in _ensure_properties.
  IF ptr_valid(o.image_origin) THEN ptr_free,o.image_origin  
  IF ptr_valid(o.image_scale) THEN ptr_free,o.image_scale 
  IF ptr_valid(o.title) THEN ptr_free,o.title  
  IF ptr_valid(o.ARn) THEN ptr_free,o.ARn
  IF ptr_valid(o.ARxy) THEN ptr_free,o.ARxy
  IF ptr_valid(o.xycen_delta) THEN ptr_free,o.xycen_delta
  IF ptr_valid(o.rsun_arcsec) THEN ptr_free,o.rsun_arcsec
  
  o.header = header
  o->_ensure_properties
END

PRO anyobs::_ensure_properties
  ;; Ensure that all properties (structure tags) are set. If they are not set, 
  ;; read the header and set them.
  o = self
  o->private
  
  o->_ensure_mission_specific_properties 
  o->_ensure_instrument_specific_properties 
  o->_ensure_instrume
  o->_ensure_nicons                         
  o->_ensure_plot_titles ;; calls _ensure_xtitle, _ensure_ytitle and _ensure_title
  o->_ensure_subtitle                       
  o->_ensure_image_scale                    
  o->_ensure_image_origin                   
END

PRO anyobs::_ensure_mission_specific_properties 
  self->private 
  ;; Not much going on here...
END

PRO anyobs::_ensure_instrument_specific_properties  ;; Overridden in eisobs
  self->private 
  ;; Not much going on here...
END

PRO anyobs::_ensure_instrume ;; Overridden in sp15obs
  self->private
  self.instrume = trim(gt_fkw(*(self->get(/header)),'INSTRUME'))
END

PRO anyobs::_ensure_nicons                  ;; Overridden in eisobs__define
  o = self
  o->private
  IF ~o->is_set(/nicons) THEN BEGIN
     header = o->get(/header)
     naxis = gt_fkw(*header, 'NAXIS')
     CASE naxis OF 
        2: nicons = 1
        3: nicons = gt_fkw(*header, 'NAXIS3')
        4: nicons = gt_fkw(*header, 'NAXIS3')*gt_fkw(*header, 'NAXIS4')
        else: nicons = -1
     ENDCASE
     o.nicons = nicons
  ENDIF
END

;; <_ensure_plot_titles and help methods>
PRO anyobs::_ensure_plot_titles
  o = self
  o->private
  IF ~o->is_set(/xtitle) THEN o->_ensure_xtitle
  IF ~o->is_set(/ytitle) THEN o->_ensure_ytitle
  IF ~o->is_set(/title) THEN o->_ensure_title
END

PRO anyobs::_ensure_xtitle     ;; Overridden in eisobs__define, sotobs__define, irissji
  self->private
  self.xtitle = 'Solar X (arc sec)'
END

PRO anyobs::_ensure_ytitle    ;; Overridden in irissjiobs__define
  self->private
  self.ytitle = 'Solar Y (arc sec)'
END
;; </_ensure_plot_titles>


PRO anyobs::_ensure_subtitle    ; Overridden in eisobs
  self->private
  self.subtitle = ''
END

;;  <_ensure_image_scale and help methods>  
PRO anyobs::_ensure_image_scale  ; Overridden in eisobs__define, sotobs__define
  o = self
  o->private
  
  IF ~o->is_set(/image_scale) THEN o.image_scale = ptr_new(o->_image_scale_x_y())
END

FUNCTION anyobs::_image_scale_x_y
  self->private
  return,[self->_image_scale_x(), self->_image_scale_y()]
END

FUNCTION anyobs::_image_scale_x
  self->private
  header =  *(self->get(/header))
  return, gt_fkw(header,'CDELT1')
END
FUNCTION anyobs::_image_scale_y
  self->private
  header =  *(self->get(/header))
  return, gt_fkw(header,'CDELT2')
END
;;   </_ensure_image_scale>   

;;   <_ensure_image_origin and help methods>    
PRO anyobs::_ensure_image_origin ; Overridden in eisobs__define, sotobs__define
  o = self
  o->private

  IF ~o->is_set(/image_origin) THEN $
     o.image_origin = ptr_new(o->_image_origin_x_y())
END

FUNCTION anyobs::_image_origin_x_y ; Overridden in eisobs__define
  self->private
  return, [self->_image_origin_x(), self->_image_origin_y()]
END

FUNCTION anyobs::_image_origin_x ; Overridden in eisobs__define
  self->private
  header = *(self->get(/header))
  fovx = gt_fkw(header,'FOVX')
  xcen = gt_fkw(header,'XCEN')
  return, xcen-fovx/2.
END

FUNCTION anyobs::_image_origin_y  ; Overridden in eisobs__define
  self->private
  header = *(self->get(/header))
  fovy = gt_fkw(header,'FOVY')
  ycen = gt_fkw(header,'YCEN')
  return, ycen-fovy/2.
END
;;  </_ensure_image_origin>   

;; ------------------------------------------------------------------------
;; </read_header with ensure_properties & help methods
;; ------------------------------------------------------------------------

;; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;; get_XXX functions for all structure tags that are set based on the fits header.
;; Each function checks if header is a valid pointer, if not, call read_header which
;; again ensures the tag is set. 
;; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FUNCTION anyobs::get_header
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.header
END

FUNCTION anyobs::get_data 
  self->private
  IF ~ptr_valid(self.data) THEN self->read_data
  return, self.data
END

FUNCTION anyobs::get_instrume
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.instrume
END

FUNCTION anyobs::get_nicons
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.nicons
END

FUNCTION anyobs::get_image_origin
  ;; For files with more than one image with different x-origin, image_origin
  ;; will be a 2D array. If you want the image_origin for image i, use
  ;; orig = o->_extract_image_origin,i
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, *self.image_origin
END
FUNCTION anyobs::get_image_scale
  ;; For files with more than one image with different x-scale, image_scale
  ;; will be a 2D array. If you want the image_scale for image i, use
  ;; scl = o->_extract_image_scale,i
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, *self.image_scale
END

FUNCTION anyobs::get_title
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.title
END

FUNCTION anyobs::get_xtitle
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.xtitle
END

FUNCTION anyobs::get_ytitle
  self->private
  IF ~ptr_valid(self.header) THEN self->read_header
  return, self.ytitle
END



 
;; ------------------------------------------------------------------------
;;                          </get_XXX methods>
;; ------------------------------------------------------------------------


PRO anyobs::read_data           ; Overridden in eisobs__define and irisobs__define
  ;; Free the header and data pointers, read the full fits file, set both the
  ;; header and data.
  o = self
       
  ptr_free, o.data, o.header
  
  o->_instrument_specific_read_method, data, header
  
  o->set, header=ptr_new(header,/no_copy)
  o->set, data=ptr_new(data,/no_copy)
END



FUNCTION anyobs::hget, _extra=extra
  ;; Return fits keyword value from header. 'MISSING' is returned if the
  ;; keyword doesn't exist.
  header = self->extract_header();*(self->get(/header))
  t = (tag_names(extra))[0]
  return,gt_fkw(header,t)
END


FUNCTION anyobs::extract_data, i, ii, all=all, medianvalue=medianvalue, _extra=extra
  ;; Returns the full n dimensional data cube or data object, or the 2D data
  ;; cube of image number i if set. If the file is an EIS file the 2D datacube
  ;; of gaussian ii may be extracted (/intensity, /velocity or /width should
  ;; also be set
  
  o = self
  
  ;; if ALL is set, return the complete data cube (or data object)
  IF keyword_set(all) THEN return, *(o->get(/data))
  
  ;; If called without any input parameters, i=0, ii is undefined
  IF n_params() EQ 0 THEN i = 0 ; If image no. is not specified, extract first one
  
  dummy = o->extract_image(i, ii = ii, data, _extra=extra)
  
  return, data
END

FUNCTION anyobs::extract_header, structure=structure
  header =  *(self->get_header())
  IF size(header,/tname) EQ 'STRING' THEN header = fitshead2struct(header)
  return, header
END

FUNCTION anyobs::extract_directory
  file = self->get(/file)
  return, strmid(file,0,strpos(file,'/',/reverse_search)+1)
END


PRO anyobs::extract_pointinginfo, xcen, ycen, fovx, fovy
  self->_cenAndfov,xcen,ycen,fovx,fovy
END


    
FUNCTION anyobs::is_set, _extra=extra
  ;; Check if int/float/string/pointer tag is set. ie. if the int/float has a
  ;; value different from -1, if string has a value different from 'MISSING' and
  ;; if the pointer is valid
  self->private
  o = self
  myt = *o.sdc_util_tags
  t = tag_names(extra)
  FOR i=0,n_elements(t)-1 DO BEGIN
     ix = where(strmid(myt,0,strlen(t[i])) EQ t[i],c)
     IF c GT 1 THEN message,"Several properties match "+t[i]+": "+ $
                            strjoin(myt[ix],', ')
     IF ~c THEN message,"No property matches "+t[i]
     
  END 
  tagval = o.(ix)
  
  CASE size(tagval,/tname) OF 
     'POINTER': IF  ~ptr_valid(tagval) THEN return,0
     'STRING' : IF tagval EQ 'MISSING' THEN return,0
     'INT': FOR i=0,n_elements(tagval)-1 DO $
        IF trim(tagval[i]) EQ '-1' THEN return,0
     'FLOAT':FOR i=0,n_elements(tagval)-1 DO $
        IF trim(tagval[i]) EQ '-1' THEN return,0
     
     else: message,'Ooooops!'
  ENDCASE
  
  return,1

END 

FUNCTION anyobs::nmissing ;; Overridden in eisobs
  return,0
END



PRO anyobs::help
  help,self,/obj
  return
END

PRO anyobs__define
  INT = 0
  PTR = ptr_new()
  STR = ''
  FLT = 0.0
  
  d = { anyobs, $
        inherits sdc_util, $
        version: STR, $
        header:PTR, $
        data:PTR, $
        instrume:STR, $
        file:STR, $
        destdir:STR, $
        nicons:INT, $
        image_origin:PTR, $
        image_scale:PTR, $
        xtitle:STR, $
        ytitle:STR, $
        title:PTR, $
        subtitle:STR, $
        rightytitle:STR, $
        maxplotsz:INT, $
        thumbsmoothsz:INT,$
        prep:INT, $
        ARn:PTR, $
        ARxy:PTR, $
        xycen_delta: PTR, $
        rsun_arcsec: PTR, $
        coltabs:{d, intensity:INT, altintensity:INT, stokesquv:INT, rufaz:INT}}
END

