subroutine limits(line,error)
  use gildas_def
  use gbl_format
  use gbl_message
  use sic_types
  use greg_dependencies_interfaces
  use greg_interfaces, except_this=>limits
  use greg_kernel
  use greg_rg
  use greg_wcs
  use greg_xyz
  !---------------------------------------------------------------------
  ! @ private
  ! GREG Support routine for command
  !   LIMITS [X1 X2 Y1 Y2 [TYPE]]
  ! 1        [/XLOG]
  ! 2        [/YLOG]
  ! 3        [/RGDATA [Var_name]]
  ! 4        [/BLANKING]
  ! 5        [/REVERSE [X] [Y]]
  ! 6        [/VARIABLES Xv Yv]
  !
  ! With:
  !   Automatic computation if Xi     *
  !   Last value if                   =
  !   Union with last values          >
  !   Intersection with last values   <
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line   ! Command line
  logical,          intent(inout) :: error  ! Logical error flag
  ! Global
  include 'gbl_pi.inc'
  include 'gbl_memory.inc'
  ! Local
  character(len=*), parameter :: rname='LIMITS'
  integer(kind=4), parameter :: optxlog=1
  integer(kind=4), parameter :: optylog=2
  integer(kind=4), parameter :: optrgda=3
  integer(kind=4), parameter :: optblan=4
  integer(kind=4), parameter :: optreve=5
  integer(kind=4), parameter :: optvari=6
  integer(kind=4), parameter :: optmarg=7
  !
  real(kind=8), parameter :: defmargin=5d0  ! [%]
  character(len=20) :: argum
  character(len=argument_length) :: code(4)
  character(len=12) :: ctype(5),name
  real(kind=8) :: arg(4),garg(4),bval,eval,tmp,absarg(4),abstmp,marg(4),kconv
  logical :: flag(4),optrg,vari,blan,incarnate,domargin
  integer(kind=4) :: i,kboxd,kangle,n,length,iarg
  type(sic_descriptor_t) :: xinca,yinca
  integer(kind=address_length) :: ipx,ipy
  integer(kind=4) :: form,nc
  integer(kind=size_length) :: ixy
  save xinca,yinca
  ! Data
  data ctype /'ABSOLUTE','SECONDS','MINUTES','DEGREES','RADIANS'/
  data arg /0.0,1.0,0.0,1.0/
  !
  ! Some syntax analysis
  axis_xlog = sic_present(optxlog,0)
  axis_ylog = sic_present(optylog,0)
  optrg = sic_present(optrgda,0)
  blan  = sic_present(optblan,0)
  vari  = sic_present(optvari,0)
  if (optrg) then
    if (axis_xlog .or. axis_ylog) then
      call greg_message(seve%e,rname,'No logarithmic axis for regular maps')
      error = .true.
    endif
    if (vari.or.blan) then
      call greg_message(seve%e,rname,'Conflicting options')
      error = .true.
    endif
    if (error) return
  endif
  garg(1) = gux1
  garg(2) = gux2
  garg(3) = guy1
  garg(4) = guy2
  !
  ! UNIT
  if (sic_present(0,5)) then
    call sic_ke (line,0,5,argum,nc,.true.,error)
    call sic_ambigs('LIMITS',argum,name,n,ctype,5,error)
    if (error) return
    if (n.eq.1) then
      kboxd = -2               ! Absolute
      kangle = u_radian        ! Must be defined ...
    else
      kboxd = 0
      kangle = n-1             ! Angular unit
    endif
  else
    kboxd = iboxd
    kangle = u_radian
    if (kboxd.ne.-2) kangle = u_angle
  endif
  select case (kangle)
  case (u_second)
    kconv = pi/180.d0/3600.d0
  case (u_minute)
    kconv = pi/180.d0/60.d0
  case (u_degree)
    kconv = pi/180.d0
  case default
    kconv = 1.d0
  end select
  !
  ! /MARGIN
  domargin = sic_present(optmarg,0)
  if (domargin) then
    marg(:) = defmargin
    ! 0, 1, 2 or 4 arguments
    call sic_r8(line,optmarg,1,marg(1),.false.,error)
    if (error)  return
    if (sic_present(optmarg,4)) then
      ! 4 arguments: all different margins
      call sic_r8(line,optmarg,2,marg(2),.true.,error)
      if (error)  return
      call sic_r8(line,optmarg,3,marg(3),.true.,error)
      if (error)  return
      call sic_r8(line,optmarg,4,marg(4),.true.,error)
      if (error)  return
    elseif (sic_present(optmarg,2)) then
      ! 2 arguments: X margins of same amount, Y margins of another amount
      call sic_r8(line,optmarg,2,marg(3),.true.,error)
      if (error)  return
      marg(2) = marg(1)
      marg(4) = marg(3)
    else
      ! 1 argument to option: all margins of same amount
      marg(2) = marg(1)
      marg(3) = marg(1)
      marg(4) = marg(1)
    endif
  else
    marg(:) = 0.d0
  endif
  ! Convert from % to fraction
  marg(:) = marg(:)*1.d-2
  !
  ! Find automatic settings. FLAG(i) is .true. if i-th limit has to be
  ! computed from data (as opposed to explicit limit on command line).
  if (.not.sic_present(0,1)) then
    flag(:) = .true.  ! All limits computed from data
    code(:) = '*'
  elseif (.not.sic_present(0,4)) then
    call greg_message(seve%e,rname,'4 arguments required or none')
    error = .true.
    return
  else
    do i=1,4
      call sic_ch(line,0,i,code(i),length,.true.,error)
      if (error)  return
      flag(i) = (code(i).eq.'*' .or. code(i).eq.'>' .or. code(i).eq.'<')
      if (code(i).eq.'=') then
        ! Previous value is reused. NB: if ABSOLUTE limits, this means that
        ! the previous margin is reused. Is this a desired feature? (e.g.
        ! overplotting maps with different centers?)
        arg(i) = garg(i)
        !
      elseif (flag(i)) then
        ! Automatic limit involved (see later)
        if (kboxd.eq.-2) then
          ! ABSOLUTE limits. Set an absolute value from previous limit, but
          ! not perfect because the formula used in the abs_to_rel conversion
          ! below is not reversible.
          if (i.le.2) then
            call rel_to_abs(gproj,garg(i),garg(i+2),absarg(i),tmp,1)
          else
            call rel_to_abs(gproj,garg(i-2),garg(i),tmp,absarg(i),1)
          endif
        else
          ! Nothing to be precomputed
        endif
        !
      else
        ! Explicit limit
        if (kboxd.eq.-2) then
          ! ABSOLUTE SEXAGESIMAL value
          call sic_sexa(code(i),length,absarg(i),error)
          if (i.le.2 .and. (i_system.eq.type_eq .or. i_system.eq.type_ic)) then
            absarg(i) = absarg(i)*pi/12.0d0
          else
            absarg(i) = absarg(i)*pi/180.0d0
          endif
        else
          ! Floating point value
          call sic_math_dble(code(i),length,arg(i),error)
          if (error)  return
        endif
      endif
    enddo
    if (kboxd.eq.-2) then
      ! Now absarg(:) contains absolute coordinates in radians. Convert to
      ! relative (offsets) coordinates, exactly symetrical to what is done
      ! in BOX or AXIS, e.g. for relative point [X1,tmp], convert from
      ! absolute point [AX1,mean(AY1,AY2)]
      abstmp = (absarg(3)+absarg(4))/2.d0
      call abs_to_rel(gproj,absarg(1),abstmp,arg(1),tmp,1)
      call abs_to_rel(gproj,absarg(2),abstmp,arg(2),tmp,1)
      abstmp = (absarg(1)+absarg(2))/2.d0
      call abs_to_rel(gproj,abstmp,absarg(3),tmp,arg(3),1)
      call abs_to_rel(gproj,abstmp,absarg(4),tmp,arg(4),1)
    endif
  endif
  !
  ! Limits must be computed if any FLAG is set
  ! Otherwise Limits are explicitely mentionned
  incarnate = .false.
  if (flag(1).or.flag(2).or.flag(3).or.flag(4)) then
    !
    ! Compute limits from X and Y data
    if (.not.optrg) then
      ! We activate implicit margin if not already explictly used, but
      ! not where explicit limits were passed
      if (.not.domargin) then
        domargin = .true.
        do i=1,4
          if (flag(i))  marg(i) = defmargin*1.d-2
        enddo
      endif
      !
      ! Check for explicit variables
      if (vari) then
        if (sic_narg(optvari).ne.2) then
          call greg_message(seve%e,rname,  &
            '/VARIABLES option requires two arguments')
          error = .true.
          return
        endif
        ixy = 0
        form = fmt_r8
        call get_same_inca ('LIMITS',line,optvari,1,form,ixy,xinca,error)
        if (error) return
        call get_same_inca ('LIMITS',line,optvari,2,form,ixy,yinca,error)
        if (error) then
          call sic_volatile(xinca)
          return
        endif
        !
        ! Use X Y default
      else
        form = fmt_r8
        ixy = 0
        call get_greg_inca ('LIMITS','X',form,ixy,xinca,error)
        if (error) return
        call get_greg_inca ('LIMITS','Y',form,ixy,yinca,error)
        if (error) then
          call sic_volatile(xinca)
          return
        endif
      endif
      incarnate = .true.
      !
      ! /BLANKING ?
      bval = cblank
      call sic_r8 (line,optblan,1,bval,.false.,error)
      if (error) goto 97
      eval = eblank
      call sic_r8 (line,optblan,2,eval,.false.,error)
      if (error) goto 97
      !
      ! Compute limits
      ipx = gag_pointer(xinca%addr,memory)
      ipy = gag_pointer(yinca%addr,memory)
      call limaut('X',axis_xlog,arg(1),arg(2),flag(1:2),memory(ipx),ixy,  &
        memory(ipy),bval,eval,error)
      if (error) goto 97
      call limaut('Y',axis_ylog,arg(3),arg(4),flag(3:4),memory(ipy),ixy,  &
        memory(ipy),bval,eval,error)
      if (error) goto 97
      !
    else
      ! /RGDATA Var_Name: compute limits from Regular Grid array
      if (sic_present(optrgda,1)) then
        ! Load Variable as RGDATA
        call rgimag (line,optrgda,.false.,0,error)
        if (error) then
          call greg_message(seve%e,rname,'Variable does not exist')
          return
        endif
      endif
      ! Check RGDATA is loaded
      if (rg%status.eq.code_pointer_null) then
        call greg_message(seve%e,rname,'No map loaded')
        error =.true.
        return
      endif
      ! NB: limits are converted from radians to current unit for a coherent
      ! flow in this subroutine
      if (flag(1)) arg(1) = ( (0.5-rg%xref)*rg%xinc+rg%xval       )/kconv
      if (flag(2)) arg(2) = ( (rg%nx+0.5-rg%xref)*rg%xinc+rg%xval )/kconv
      if (flag(3)) arg(3) = ( (0.5-rg%yref)*rg%yinc+rg%yval       )/kconv
      if (flag(4)) arg(4) = ( (rg%ny+0.5-rg%yref)*rg%yinc+rg%yval )/kconv
    endif
  endif
  !
  ! Apply margins if any
  if (domargin) then
    call limits_margin('X',axis_xlog,arg(1),arg(2),marg(1),marg(2),error)
    if (error)  return
    call limits_margin('Y',axis_ylog,arg(3),arg(4),marg(3),marg(4),error)
    if (error)  return
  endif
  !
  ! Convert from current angle unit to internal radians unit
  do i=1,4
    arg(i) = arg(i)*kconv
  enddo
  !
  ! Set new limits according to code
  do i=1,4
    if (code(i).eq.'<') then
      arg(i)=min(arg(i),garg(i))
    elseif (code(i).eq.'=') then
      arg(i)=garg(i)
    elseif (code(i).eq.'>') then
      arg(i)=max(arg(i),garg(i))
    endif
  enddo
  !
  ! Check if valid limits
  if (arg(1).eq.arg(2)) then
    call greg_message(seve%e,rname,'No range in X')
    error = .true.
    goto 97
  endif
  if (arg(3).eq.arg(4)) then
    call greg_message(seve%e,rname,'No range in Y')
    error = .true.
    goto 97
  endif
  !
  ! Reverse if requested
  if (sic_present(optreve,0)) then
    do iarg=1,max(1,sic_narg(optreve))  ! At least 1 argument
      call sic_ke (line,optreve,iarg,argum,nc,.true.,error)
      if (error) goto 97
      if (argum.eq.'X') then
        tmp = arg(2)
        arg(2) = arg(1)
        arg(1) = tmp
      elseif (argum.eq.'Y') then
        tmp = arg(3)
        arg(3) = arg(4)
        arg(4) = tmp
      endif
    enddo
  endif
  !
  call setlim(arg(1),arg(2),arg(3),arg(4))
  call setrem  ! Set the "remarkable" points
  !
97 continue
  if (incarnate) then
    call sic_volatile(xinca)
    call sic_volatile(yinca)
  endif
  !
end subroutine limits
!
subroutine setlim(ax1,ax2,ay1,ay2)
  use greg_interfaces, except_this=>setlim
  use greg_kernel
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  ! GREG  Internal routine
  !   Setup new limits and conversion formulae
  !---------------------------------------------------------------------
  real(kind=8), intent(in) :: ax1  ! Left limit of X axis
  real(kind=8), intent(in) :: ax2  ! Right limit of X axis
  real(kind=8), intent(in) :: ay1  ! Low limit of Y axis
  real(kind=8), intent(in) :: ay2  ! High limit of Y axis
  ! Local
  character(len=*), parameter :: rname='SETLIM'
  !
  gux1 = ax1
  gux2 = ax2
  guy1 = ay1
  guy2 = ay2
  !
  ! Make a stupid protection against Invalid LOG axis
  if (axis_xlog) then
    if (gux1.le.0 .or. gux2.le.0) then
      call greg_message(seve%w,rname,'Invalid X LOG axis, ignored')
      gux = (gx2-gx1)/(gux2-gux1)
    else
      lux = log(gux1)
      gux = (gx2-gx1)/(log(gux2)-lux)
    endif
  else
    gux = (gx2-gx1)/(gux2-gux1)
  endif
  !
  if (axis_ylog) then
    if (guy1.le.0 .or. guy2.le.0)  then
      call greg_message(seve%w,rname,'Invalid Y LOG axis, ignored')
      guy = (gy2-gy1)/(guy2-guy1)
    else
      luy = log(guy1)
      guy = (gy2-gy1)/(log(guy2)-luy)
    endif
  else
    guy = (gy2-gy1)/(guy2-guy1)
  endif
end subroutine setlim
!
subroutine limaut(name,logf,xmin,xmax,flag,x,n,y,bval,eval,error)
  use gildas_def
  use gbl_message
  use greg_dependencies_interfaces
  use greg_interfaces
  !---------------------------------------------------------------------
  ! @ no-interface (because of argument type mismatch)
  !    Find maxima and minima of X array and set range to be plotted
  ! according to the logical into FLAG
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: name     ! Axis name
  logical,                   intent(in)    :: logf     ! Log or linear axis?
  real(kind=8),              intent(inout) :: xmin     ! Minimum value
  real(kind=8),              intent(inout) :: xmax     ! Maximum value
  logical,                   intent(in)    :: flag(2)  ! Logicals for computing extrema
  real(kind=8),              intent(in)    :: x(*)     ! Array to search in
  integer(kind=size_length), intent(in)    :: n        ! Number of data points
  real(kind=8),              intent(in)    :: y(*)     ! Array of blank/non-blank values (used as mask for X array)
  real(kind=8),              intent(in)    :: bval     ! Blanking value
  real(kind=8),              intent(in)    :: eval     ! Blanking tolerance
  logical,                   intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='LIMITS'
  real(kind=8) :: xmi,xma
  integer(kind=size_length) :: j,is,ne,in,nmi,nma,ntot
  character(len=message_length) :: mess
  !
  if (.not.flag(1) .and. .not.flag(2)) return
  if (n.lt.1)  return
  !
  if (eval.lt.0.0d0) then
    ! is protected against NaNs
    call gr8_minmax (n,x,bval,eval,xmi,xma,nmi,nma)
    ntot = n
  else
    in = 1
    ! FIND_BLANK8 is protected against NaNs when Blanking enabled
    call find_blank8(y,bval,eval,n,is,ne,in)
    if (ne.lt.1)  return
    ntot = ne
    !
    if (x(is).eq.x(is))then
      xmi = x(is)
      xma = x(is)
    else
      call gr8_minmax (n,x,bval,eval,xmi,xma,nmi,nma)
    endif
    do j=is,is+ne-1
      if (x(j).eq.x(j).and.x(j).lt.xmi) then
        xmi = x(j)
      elseif (x(j).eq.x(j).and.x(j).gt.xma) then
        xma = x(j)
      endif
    enddo
    do while (in.ne.0)
      call find_blank8(y,bval,eval,n,is,ne,in)
      ntot = ntot+ne
      do j=is,is+ne-1
        if (x(j).eq.x(j).and.x(j).lt.xmi) then
          xmi = x(j)
        elseif (x(j).eq.x(j).and.x(j).gt.xma) then
          xma = x(j)
        endif
      enddo
    enddo
  endif
  !
  if (flag(1))  xmin = xmi
  if (flag(2))  xmax = xma
  !
  if (logf) then
    if (xmin.le.0.d0 .or. xmax.le.0.d0) then
      call greg_message(seve%e,rname,'Invalid logarithmic limits in '//name)
      error = .true.
      return
    endif
  endif
  if (xmin.eq.xmax) then
    if (ntot.eq.1) then
      write(mess,'(A,A1,A)')  'No range in ',name,' (only 1 valid value in Y array)'
    else
      write(mess,'(A,A1)')  'No range in ',name
    endif
    call greg_message(seve%w,rname,mess)
  endif
  !
end subroutine limaut
!
subroutine limits_margin(name,logf,xl,xr,lmarg,rmarg,error)
  use gbl_message
  use greg_interfaces, except_this=>limits_margin
  !---------------------------------------------------------------------
  ! @ private
  !  Apply margins on the provided range
  !  NB: margin can be negative, i.e. truncate the [xl-xr] range
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: name         ! Axis name
  logical,          intent(in)    :: logf         ! Log or linear axis?
  real(kind=8),     intent(inout) :: xl,xr        ! Current (in) and new (out) values
  real(kind=8),     intent(in)    :: lmarg,rmarg  ! Left and right margins (fraction of [xl-max])
  logical,          intent(inout) :: error        !
  ! Local
  character(len=*), parameter :: rname='LIMITS'
  real(kind=8) :: xdiff
  !
  if (logf) then
    ! Log axis
    xdiff = log(xr/xl)
    if (xdiff.eq.0.d0)  xdiff = 1.  ! One decade
    xl = xl / exp(lmarg * xdiff)
    xr = xr * exp(rmarg * xdiff)
    !
  else
    ! Linear axis. NB: this is valid even if xl>xr
    xdiff = xr - xl
    if (xdiff.eq.0.d0) then
      xdiff = abs(xr)
      if (xdiff.eq.0.d0) xdiff = 1.d0
    endif
    !
    xl = xl - lmarg*xdiff
    xr = xr + rmarg*xdiff
  endif
  !
end subroutine limits_margin
