subroutine map_polar(line,comm,error)
  use gkernel_interfaces
  use image_def
  use gbl_message
  use imager_interfaces, only : map_message
  !---------------------------------------------------------------------
  ! @ private
  !   IMAGER
  !     Support for command
  !   MAP_POLAR NameBegin NameEnd /COMPUTE Threshold
  !     [/BACKGROUND Type Min Max] [/STEP Step Xfirst Yfirst]
  !
  ! Withe /COMPUTE, derive 
  !   1) Polarized fraction
  !   2) Polarization angle
  ! from I,Q,U (and V ?) polarization images
  !
  ! Without, display the polarization maps as controlled by the options
  !   /BACKGROUND and /STEP
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line, comm
  logical, intent(inout) :: error  
  !
  ! Global or remanent
  real, parameter :: pi=acos(-1.0)
  integer, parameter :: o_back=1
  integer, parameter :: o_compute=2
  integer, parameter :: o_step=3
  character(len=*), parameter :: rname='MAP_POLAR'
  !
  character(len=filename_length) :: query
  character(len=filename_length), save :: stokes=' '  ! Begin of name
  character(len=32), save :: ctype='.lmv-clean'       ! End of name
  real, save :: threshold=10              ! 10 sigma detection level
  integer, save :: step=0, ibeg=1, jbeg=1 ! /STEP option
  real, save :: scale(2)=[0.,0.]          ! /BACK scale values 
  character(len=1) :: cback='I' !         ! /BACK Image
  logical, save :: defined=.false.
  character(len=24), save :: chead
  !
  ! Local
  character(len=128) :: chain
  integer :: n, nx, ny, ic, ier
  type(gildas) :: hi, hu, hq, hf, ha, htmp
  real(8), allocatable :: ap(:,:)    ! 4 columns array for ARROWS
  real :: seuil, pnoise, p2, pa
  integer :: ip, ix, iy, jy, nk, kstep
  real(8) :: rstep
  integer(kind=index_length) :: dim(2)
  logical :: err
  !---------------------------------------------------------------------
  err = .false.
  !
  ic = 1 ! So far, only 1st channel
  ! Code:
  query =  ' '
  call sic_ch(line,0,1,query,n,.false.,error)
  if (error) return
  call sic_ch(line,0,2,ctype,n,.false.,error)
  if (error) return  
  !
  if (defined) then
    call sic_delvariable('POLAR_BACK',.false.,error)
    call sic_delvariable('POLAR_VECTOR',.false.,error)
    call sic_delvariable('POLAR_HEAD',.false.,error)
    error = .false.
    defined = .false.
  endif
  !
  if (sic_present(o_compute,0)) then
    if (sic_present(0,1)) stokes = query
    !
    ! Compute the Polarization Fraction and Angle
    call sic_r4(line,o_compute,2,threshold,.false.,error)
    if (error) return
    !
    call gildas_null(hi)
    call gildas_null(hu)
    call gildas_null(hq)
    call gildas_null(hf)
    call gildas_null(ha)
    !
    hi%file = trim(stokes)//'-I'//ctype
    hu%file = trim(stokes)//'-U'//ctype
    hq%file = trim(stokes)//'-Q'//ctype
    !
    call gdf_read_header(hi,error)
    if (error) return
    call gdf_read_header(hu,error)
    if (error) return
    call gdf_read_header(hq,error)
    if (error) return
    if (hi%gil%ndim.ne.3) then
      call gdf_trim_header(hi,3,error)
      if (error) return
      call gdf_trim_header(hu,3,error)
      if (error) return
      call gdf_trim_header(hq,3,error)
      if (error) return
    endif
    !
    call gdf_copy_header(hi,hf,error)
    if (error) return
    call gdf_copy_header(hi,ha,error)
    if (error) return
    hf%file = trim(stokes)//'-P'//ctype
    ha%file = trim(stokes)//'-A'//ctype
    !
    call gdf_allocate(hi,error)
    if (error) return
    call gdf_allocate(hu,error)
    if (error) return
    call gdf_allocate(hq,error)
    if (error) return
    call gdf_allocate(hf,error)
    if (error) return
    call gdf_allocate(ha,error)
    if (error) return
    !
    call gdf_read_data(hi,hi%r3d,error)
    if (error) err = .true.
    call gdf_read_data(hu,hu%r3d,error)
    if (error) err = .true.
    call gdf_read_data(hq,hq%r3d,error)
    if (error) err = .true.
    !
    ! The images are loaded now
    call gdf_close_image(hi,error)
    call gdf_close_image(hu,error)
    call gdf_close_image(hq,error)
    !
    ! But some may have been missed
    if (err) then
      error = .true.
      return
    endif
    !
    seuil = threshold*max(hi%gil%rms,hi%gil%noise)
    !
    pnoise = max(hu%gil%noise,hu%gil%rms) * max(hq%gil%noise,hq%gil%rms) 
    pnoise = 2.0*pnoise  ! Sum of sigma^2 
    !
    do ip=1,hi%gil%dim(3)
      do iy=1,hi%gil%dim(2)    
        do ix=1,hi%gil%dim(1)
          if (hi%r3d(ix,iy,ip).gt.seuil) then
            ! Get the fractional polarization
            !
            ! Low S/N involves finding the maximum of the Rice distribution, which includes
            ! a Gaussian and a 1st order Bessel function (See Vaillancourt 2006, Eq.6,
            !   https://arxiv.org/pdf/astro-ph/0603110.pdf)
            !
            ! Lower confidence intervals are bound to 0 at the 68, 95 and 99 % levels (1,2,3 sigma) for 
            ! p2/pnoise < 1.7, 2.4 and 3.0 respectively. 
            ! 
            ! Upper confidence interval at 95 to 99 % is obtained by simply using p2+2*pnoise
            ! 
            p2 = hu%r3d(ix,iy,ip)**2 + hq%r3d(ix,iy,ip)**2
            if (p2.lt.pnoise) then
              ! At low S/N, the polarisation is Zero. The maximum likelihood method
              ! yields this for p2/pnoise < sqrt(2)
              ! We use this only below 1 sigma, because it provides continuity with the de-biased limit.
              hf%r3d(ix,iy,ip) = 0
              ha%r3d(ix,iy,ip) = -2.*pi
            else
              ! At higher S/N (above 3 sigma), a simple de-biasing is acceptable. 
              ! Above 4 sigma, it yields an almost Gaussian noise.
              !
              ! We use this debiasing above 1 sigma, because it provides continuity with the previous case.
              p2 = sqrt(p2-pnoise)          ! De-bias the polarized intensity, high S/N approximation
              hf%r3d(ix,iy,ip) = p2/hi%r3d(ix,iy,ip)  ! Go to fraction
              !
              ! Polarization angle is atan(U/Q)/2 in the [0,pi[ domain
              ! see Hull & Plambeck 2015, DOI:10.1142/S2251171715500051
              pa = atan2(hu%r3d(ix,iy,ip),hq%r3d(ix,iy,ip)) 
              if (pa.lt.0) pa = pa+2*pi
              ha%r3d(ix,iy,ip) = 0.5*pa 
              !
              ! The error term is pnoise/(2*p2) on this angle, and could be stored elsewhere...
            endif
          else
            hf%r3d(ix,iy,ip) = 0
            ha%r3d(ix,iy,ip) = -2.*pi
          endif
        enddo
      enddo
    enddo
    !
    hf%r3d = 100.0*hf%r3d
    hf%gil%bval = 0.
    hf%gil%eval = 0.
    hf%char%unit= 'Percent'
    call cube_minmax('FRACTION',hf,error)
    !
    ha%gil%bval = -2.*pi
    ha%gil%eval = 0.1*pi
    call cube_minmax('ANGLE',ha,error)
    !
    call gdf_write_image(hf,hf%r3d,error)
    if (error) return
    ha%char%unit= 'ANGLE'
    call gdf_write_image(ha,ha%r3d,error)
    !
    step = 0
    ibeg = 1
    jbeg = 1 ! /STEP option
    cback ='I' !         ! /BACK Image
    scale = 0
    call map_message(seve%i,rname,"Display control parameters reset to Default")
  else if (query(1:2).eq.'? ') then
    !
    write(*,*) "  --- Current MAP_POLAR behaviour control values ---"
    write(*,'(A)')   "  Data files      "//trim(stokes)//'-?'//trim(ctype)
    write(*,'(A,I0,A,I0,1X,I0)')   "  Pixel step ",step," Start ",ibeg,jbeg
    if (cback.eq.'I') then
      write(*,'(A)') "  Displayed image : Total Intensity       (I)"
    else if (cback.eq.'P') then
      write(*,'(A)') "  Displayed image : Polarized Intensity   (P)"
    else if (cback.eq.'F') then
      write(*,'(A)') "  Displayed image : Polarization Fraction (F) in %"
    endif
    write(*,*)  " Color scale range ",scale
  else
    if (sic_present(0,1)) stokes = query
    !
    ! Display  the results
    call gildas_null(hf)
    call gildas_null(ha)
    hi%file = trim(stokes)//'-I'//ctype
    hf%file = trim(stokes)//'-P'//ctype
    ha%file = trim(stokes)//'-A'//ctype
    !
    call gdf_read_header(hi,error)
    if (error) return
    call gdf_read_header(hf,error)
    if (error) return
    call gdf_read_header(ha,error)
    if (error) return
    if (hi%gil%ndim.ne.3) then
      call gdf_trim_header(hi,3,error)
      if (error) return
      call gdf_trim_header(hf,3,error)
      if (error) return
      call gdf_trim_header(ha,3,error)
      if (error) return
    endif
    !
    ! /STEP option
    if (sic_present(o_step,0)) then
      call sic_i4(line,o_step,1,step,.false.,error)
      if (error) return
      call sic_i4(line,o_step,2,ibeg,.false.,error)
      if (error) return
      call sic_i4(line,o_step,3,jbeg,.false.,error)
      if (error) return
    endif
    !
    if (step.eq.0) then
      ! A bit less than 1 beam size 
      rstep = sqrt(abs(hi%gil%majo*hi%gil%mino/hi%gil%inc(1)/hi%gil%inc(2)))/1.4 
      kstep = nint(rstep)
    else
      kstep = step
    endif
    write(chain,'(A,I0)') "@ p_show_polar ",kstep
    !
    ! /BACK option
    if (sic_present(o_back,0)) then
      !  [/BACKGROUND Type Min Max]
      call sic_ke(line,o_back,1,cback,n,.false.,error)
      if (error) return
      call sic_r4(line,o_back,2,scale(1),.false.,error)
      if (error) return
      call sic_r4(line,o_back,3,scale(2),.false.,error)
      if (error) return
    endif
    n = len_trim(chain)+1
    write(chain(n:),'(1X,1PG15.7,1X,1PG15.7)') scale(1),scale(2)
    !
    ! Data and intermediate arrays
    call gdf_allocate(hi,error)
    if (error) return
    call gdf_allocate(hf,error)
    if (error) return
    call gdf_allocate(ha,error)
    if (error) return
    !
    call gdf_read_data(hi,hi%r3d,error)
    if (error) err = .true.
    call gdf_read_data(hf,hf%r3d,error)
    if (error) err = .true.
    call gdf_read_data(ha,ha%r3d,error)
    if (error) err = .true.
    call gdf_close_image(hi,error)
    call gdf_close_image(hf,error)
    call gdf_close_image(ha,error)
    if (err) then
      error = .true.
      return
    endif
    !
    ic = 1 ! Channel to be specified later
    nx = ha%gil%dim(1)
    ny = ha%gil%dim(2)
    nk = 0
    do jy = jbeg,ny,kstep
      do ix = ibeg,nx,kstep
        if (hf%r3d(ix,jy,ic).ne.0) then
          nk = nk+1
        endif
      enddo
    enddo
    allocate(ap(nk,4),stat=ier)
    !
    nk = 0
    do jy = jbeg,ny,kstep
      do ix = ibeg,nx,kstep
        if (hf%r3d(ix,jy,ic).ne.0) then
          nk = nk+1
          ap(nk,1) = (ix-hi%gil%ref(1))*hi%gil%inc(1)+hi%gil%val(1)
          ap(nk,2) = (jy-hi%gil%ref(2))*hi%gil%inc(2)+hi%gil%val(2)
          ap(nk,3) = hf%r3d(ix,jy,ic)*hi%r3d(ix,jy,ic)
          ap(nk,4) = ha%r3d(ix,jy,ic)
        endif
      enddo
    enddo
    !
    call gildas_null(htmp)
    select case (cback)
    case('I') 
      call gdf_copy_header(hi,htmp,error)
      htmp%r2d => hi%r3d(:,:,ic)
      chead = "Total Intensity"
    case('F') 
      call gdf_copy_header(hf,htmp,error)
      htmp%r2d => hf%r3d(:,:,ic)
      chead = "Polar Fraction %"
    case('P')
      call gdf_copy_header(hi,htmp,error)
      allocate(htmp%r2d(nx,ny),stat=ier)
      htmp%r2d = hi%r3d(:,:,ic)*hf%r3d(:,:,ic)/100
      chead = "Polar Intensity"
    case default
      error = .true.
      return
    end select
    htmp%gil%ndim = 2
    htmp%loca%addr = locwrd(htmp%r2d)
    call gdf_get_extrema (htmp,error)
    call sic_mapgildas('POLAR_BACK',htmp,error,htmp%r2d)
    !
    dim = [nk,4]
    call sic_def_dble('POLAR_VECTOR',ap,2,dim,.true.,error)
    defined = .true.
    call sic_def_char('POLAR_HEAD',chead,.true.,error)
    !
    call exec_program(chain)
    !
  endif
  !
  error = .false.
end subroutine map_polar
