/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

// This is a "copy" of the file ~magics/c/proj_braz.c .
// The reason for this duplicity is to avoid to call
// Magics routines within libUtil.
// For the new Magics version, both files should be
// replaced by a single C++ version.

#include "proj_braz.hpp"

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <cstring>

static Datum* D;
static SProjection *Sp1, *Sp2;
static GeneralProjection *Gp1, *Gp2;
// static CylindEquid		*Ce1,*Ce2;
// static PolarStereo		*Ps1,*Ps2;
static Satellite *Sat1, *Sat2;
static SImage *Imi, *Imo;
static BBox *B1, *B2;
static CPoint *P1, *P2, *P3, *P4;
static int XXX = 0;

static char *tx1, *tx2, *tx3, *tx4;

Real pWidth(BBox* b)
{
    Real a;
    a = ABS((b->Bur).X - (b->Bll).X);

    return a;
}


Real pHeight(BBox* b)
{
    Real a;

    a = (b->Bur).Y - (b->Bll).Y;
    a = (a >= 0) ? a : -a;
    return a;
}

short pContains(BBox* b, CPoint* p)
{
    if ((b->Bll).X <= p->X && (b->Bll).Y <= p->Y &&
        p->X <= (b->Bur).X && p->Y <= (b->Bur).Y)
        return TRUE;

    return FALSE;
}

short pIntersects(BBox* b1, BBox* b2)
{
    CPoint p1, p2;

    p1 = pMaxPoint(&b1->Bll, &b2->Bll);
    p2 = pMinPoint(&b1->Bur, &b2->Bur);
    if (p1.X < p2.X && p1.Y < p2.Y)
        return TRUE;
    return FALSE;
}

void pInitBBox(BBox* b, Real BllX, Real BllY, Real BurX, Real BurY)
{
    (b->Bll).X = BllX;
    (b->Bll).Y = BllY;
    (b->Bur).X = BurX;
    (b->Bur).Y = BurY;
}

void pBBoxRemapInOut(SProjection* In, SProjection* Out, BBox* B)
{
    Real aux,
        step,
        x1 = B->Bll.X,
        x2 = B->Bur.X,
        y1 = B->Bll.Y,
        y2 = B->Bur.Y,
        xmin = MAXFLOAT,
        xmax = -MAXFLOAT,
        ymin = MAXFLOAT,
        ymax = -MAXFLOAT;
    CPoint p;
    step = (x2 - x1) / NSTEPS;
    for (aux = x1; aux < x2; aux += step) {
        /* lower edge */
        p.X = aux;
        p.Y = y1;
        p = pPC2LL(In, p);
        if (p.X < (MAXFLOAT / 10.)) {
            p = pLL2PC(Out, p);
            if (p.X < (MAXFLOAT / 10.)) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.X < xmin)
                    xmin = p.X;
                if (p.X > xmax)
                    xmax = p.X;
                if (p.Y < ymin)
                    ymin = p.Y;
                if (p.Y > ymax)
                    ymax = p.Y;
            }
        }

        /* upper edge */
        p.X = aux;
        p.Y = y2;
        p = pPC2LL(In, p);
        if (p.X < (MAXFLOAT / 10.)) {
            p = pLL2PC(Out, p);
            if (p.X < (MAXFLOAT / 10.)) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.X < xmin)
                    xmin = p.X;
                if (p.X > xmax)
                    xmax = p.X;
                if (p.Y < ymin)
                    ymin = p.Y;
                if (p.Y > ymax)
                    ymax = p.Y;
            }
        }
    }

    step = (y2 - y1) / NSTEPS;
    for (aux = y1; aux < y2; aux += step) {
        /* left edge */
        p.X = x1;
        p.Y = aux;
        p = pPC2LL(In, p);
        if (p.X < (MAXFLOAT / 10.)) {
            p = pLL2PC(Out, p);
            if (p.X < (MAXFLOAT / 10.)) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.X < xmin)
                    xmin = p.X;
                if (p.X > xmax)
                    xmax = p.X;
                if (p.Y < ymin)
                    ymin = p.Y;
                if (p.Y > ymax)
                    ymax = p.Y;
            }
        }


        /* right edge */
        p.X = x2;
        p.Y = aux;
        p = pPC2LL(In, p);
        if (p.X < (MAXFLOAT / 10.)) {
            p = pLL2PC(Out, p);
            if (p.X < (MAXFLOAT / 10.)) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.X < xmin)
                    xmin = p.X;
                if (p.X > xmax)
                    xmax = p.X;
                if (p.Y < ymin)
                    ymin = p.Y;
                if (p.Y > ymax)
                    ymax = p.Y;
            }
        }
    }

    B->Bll.X = xmin;
    B->Bll.Y = ymin;
    if (pTooBig(&B->Bll)) {
        B->Bll.X = -MAXFLOAT;
        B->Bll.Y = -MAXFLOAT;
    }
    B->Bur.X = xmax;
    B->Bur.Y = ymax;
    if (pTooBig(&B->Bll)) {
        B->Bur.X = MAXFLOAT;
        B->Bur.Y = MAXFLOAT;
    }

    return;
}

void pBBoxRemap(SProjection* Out, BBox* B)
{
    Real aux,
        step,
        x1 = B->Bll.X,
        x2 = B->Bur.X,
        y1 = B->Bll.Y,
        y2 = B->Bur.Y,
        xmin = MAXFLOAT,
        xmax = -MAXFLOAT,
        ymin = MAXFLOAT,
        ymax = -MAXFLOAT;
    CPoint p;
    step = (x2 - x1) / NSTEPS;
    for (aux = x1; aux < x2; aux += step) {
        /* lower edge */
        p.X = aux;
        p.Y = y1;
        p = pLL2PC(Out, p);
        if (p.X < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.X < xmin)
                xmin = p.X;
            if (p.X > xmax)
                xmax = p.X;
            if (p.Y < ymin)
                ymin = p.Y;
            if (p.Y > ymax)
                ymax = p.Y;
        }

        /* upper edge */
        p.X = aux;
        p.Y = y2;
        p = pLL2PC(Out, p);
        if (p.X < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.X < xmin)
                xmin = p.X;
            if (p.X > xmax)
                xmax = p.X;
            if (p.Y < ymin)
                ymin = p.Y;
            if (p.Y > ymax)
                ymax = p.Y;
        }
    }

    step = (y2 - y1) / NSTEPS;
    for (aux = y1; aux < y2; aux += step) {
        /* left edge */
        p.X = x1;
        p.Y = aux;
        p = pLL2PC(Out, p);
        if (p.X < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.X < xmin)
                xmin = p.X;
            if (p.X > xmax)
                xmax = p.X;
            if (p.Y < ymin)
                ymin = p.Y;
            if (p.Y > ymax)
                ymax = p.Y;
        }


        /* right edge */
        p.X = x2;
        p.Y = aux;
        p = pLL2PC(Out, p);
        if (p.X < (MAXFLOAT / 10.)) {
            if (p.X < xmin)
                xmin = p.X;
            if (p.X > xmax)
                xmax = p.X;
            if (p.Y < ymin)
                ymin = p.Y;
            if (p.Y > ymax)
                ymax = p.Y;
        }
    }

    B->Bll.X = xmin;
    B->Bll.Y = ymin;
    B->Bur.X = xmax;
    B->Bur.Y = ymax;

    return;
}

void pimii1_(Real BllX, Real BllY, Real BurX, Real BurY, int lin, int col, Real Irx, Real Iry, int pixel, int outside, unsigned char* tbuf)
{
    unsigned long pix, outsidevalue;
    //	short				lin,col;

    pix = (unsigned long)pixel;
    outsidevalue = (unsigned long)outside;

    //	lin = (short) *Inx;
    //	col = (short) *Iny;

    pInitBBox(B1, BllX, BllY, BurX, BurY);

    pInitImage(Imi, B1, (short)lin, (short)col, Irx, Iry, pix, outsidevalue, tbuf, Sp1);

    return;
}

void pimii2_(Real BllX, Real BllY, Real BurX, Real BurY, int lin, int col, Real Irx, Real Iry, int pixel, int outside, unsigned char* tbuf)
{
    unsigned long pix, outsidevalue;
    //	short				lin,col;
    int i;
    unsigned char* buf;

    pix = (unsigned long)pixel;
    outsidevalue = (unsigned long)outside;

    //	lin = (short) *Inx;
    //	col = (short) *Iny;

    pInitBBox(B2, BllX, BllY, BurX, BurY);

    buf = tbuf;
    for (i = 0; i < lin * col; i++)
        *buf++ = (unsigned char)outsidevalue;

    pInitImage(Imo, B2, (short)lin, (short)col, Irx, Iry, pix, outsidevalue, tbuf, Sp2);

    return;
}

void pll2ic(Real& x, Real& y)
{
    // Convert lat/lon to projection coordinates
    pInitPoint(P1, x, y);
    *P1 = pLL2PC(Sp1, *P1);

    // Compute image coordinates. This routine always returns
    // valid coordinates. If the computed coordinates are outside
    // the image area, the returned values are the min/max coordinates.
    *P1 = pCoord2Index(Imi, P1);
    x = P1->X;
    y = P1->Y;

    return;
}

void pl2p1_(Real* x, Real* y)
{
    pInitPoint(P1, *x, *y);

    *P1 = pLL2PC(Sp2, *P1);

    *x = P1->X;
    *y = P1->Y;

    return;
}

void pl2p2_(Real* x, Real* y)
{
    pInitPoint(P2, *x, *y);

    *P2 = pLL2PC(Sp2, *P2);

    *x = P2->X;
    *y = P2->Y;

    return;
}

void pp2l1_(Real* x, Real* y)
{
    pInitPoint(P1, *x, *y);

    *P1 = pPC2LL(Sp2, *P1);

    *x = P1->X;
    *y = P1->Y;

    return;
}

void pp2l2_(Real* x, Real* y)
{
    pInitPoint(P2, *x, *y);

    *P2 = pPC2LL(Sp2, *P2);

    *x = P2->X;
    *y = P2->Y;

    return;
}

void premap_()
{
    pRemapI(Imi, Imo);

    return;
}

void pdeal_()
{
    free(tx1);
    free(tx2);
    free(tx3);
    free(tx4);
    free(D);
    free(Imi);
    free(Imo);
    free(Sp1);
    free(Sp2);
    free(Sat1);
    free(Sat2);
    free(Gp1);
    free(Gp2);
    free(P1);
    free(P2);
    free(P3);
    free(P4);
    free(B1);
    free(B2);

    return;
}

void pimin1_()
{
    /* Define and Initialize Datum and Projection */

    D = (Datum*)malloc(sizeof(Datum));
    Imi = (SImage*)malloc(sizeof(SImage));
    Imo = (SImage*)malloc(sizeof(SImage));

    Sp1 = (SProjection*)malloc(sizeof(SProjection));
    Sp2 = (SProjection*)malloc(sizeof(SProjection));

    Sat1 = (Satellite*)malloc(sizeof(Satellite));
    Sat2 = (Satellite*)malloc(sizeof(Satellite));

    Gp1 = (GeneralProjection*)malloc(sizeof(GeneralProjection));
    Gp2 = (GeneralProjection*)malloc(sizeof(GeneralProjection));

    P1 = (CPoint*)malloc(sizeof(CPoint));
    P2 = (CPoint*)malloc(sizeof(CPoint));
    P3 = (CPoint*)malloc(sizeof(CPoint));
    P4 = (CPoint*)malloc(sizeof(CPoint));

    B1 = (BBox*)malloc(sizeof(BBox));
    B2 = (BBox*)malloc(sizeof(BBox));

    return;
}

void pimind_(int i1, char* tx, Real Pdx, Real Pdy, Real Pdz, double Prd, double Pflt)
{
    short Pcode;

    /* Define and Initialize Datum and Projection */

    Pcode = (short)i1;

    /*
    i = str_length;
    while( *(tx+i-1) == ' ' && i>0 ) i--;

    txd = (char *) malloc(sizeof(char)*i+1);

    strncpy(txd,tx,i);
    *(txd+i) = '\0';
*/

    pInitDatum(D, Pcode, tx, Pdx, Pdy, Pdz, Prd, Pflt);

    return;
}

void pimp1_(char* txo, char* txs, int Pid, int Pcode, int Pdatum, int /*i3*/, Real Pdx, Real Pdy, Real Pdz, double Prd, double Pflt, double Poffx, double Poffy)
{
    //	int			i;
    //	short		Pcode,Pdatum;

    /* Define and Initialize Datum and Projection */

    //	Pcode = (short)i1;
    //	Pdatum = (short)i2;

    /*
    i = str_length1;
    while( *(txo+i-1) == ' ' && i>0 ) i--;

    tx1 = (char *) malloc(sizeof(char)*i+1);

    strncpy(tx1,txo,i);
    *(tx1+i) = '\0';

    i = str_length2;
    while( *(txs+i-1) == ' ' && i>0 ) i--;

    tx2 = (char *) malloc(sizeof(char)*i+1);

    strncpy(tx2,txs,i);
    *(tx2+i) = '\0';
*/

    pInitProj(Sp1, txs, txo, Pid, (short)Pcode, (short)Pdatum, Pdx, Pdy, Pdz, Prd, Pflt, Poffx, Poffy);

    return;
}

void pimp2_(char* txo, char* txs, int Pid, int Pcode, int Pdatum, int /*i3*/, Real Pdx, Real Pdy, Real Pdz, double Prd, double Pflt, double Poffx, double Poffy)
{
    //	int		i;
    //	short		Pcode,Pdatum;

    /* Define and Initialize Datum and Projection */

    //	Pcode = (short)i1;
    //	Pdatum = (short)i2;

    /*
    i = str_length1;
    while( *(txo+i-1) == ' ' && i>0 ) i--;

    tx3 = (char *) malloc(sizeof(char)*i+1);

    strncpy(tx3,txo,i);
    *(tx3+i) = '\0';

    i = str_length2;
    while( *(txs+i-1) == ' ' && i>0 ) i--;

    tx4 = (char *) malloc(sizeof(char)*i+1);

    strncpy(tx4,txs,i);
    *(tx4+i) = '\0';
*/
    pInitProj(Sp2, txs, txo, Pid, (short)Pcode, (short)Pdatum, Pdx, Pdy, Pdz, Prd, Pflt, Poffx, Poffy);

    return;
}

void pimg1_(int GPhemis, double GPlat0, double GPlon0, double GPstlat1, double GPstlat2)
{
    //	short GPhemis;

    //	GPhemis = (short)i1;

    pInitGen(Gp1, (short)GPhemis, GPlat0, GPlon0, GPstlat1, GPstlat2);

    Sp1->Proj.Gp = Gp1;

    return;
}

void pimg2_(int GPhemis, double* GPlat0, double* GPlon0, double* GPstlat1, double* GPstlat2)
{
    //	short GPhemis;

    //	GPhemis = (short)i1;

    pInitGen(Gp2, (short)GPhemis, *GPlat0, *GPlon0, *GPstlat1, *GPstlat2);

    Sp2->Proj.Gp = Gp2;

    return;
}

void pims1_(double SPri, double SPrj, double SPis, double SPjs, double SPla0, double SPlo0, double SPrs, double SPscn, double SPyaw)
{
    pInitSat(Sat1, SPri, SPrj, SPis, SPjs, SPla0, SPlo0, SPrs, SPscn, SPyaw);
    Sp1->Proj.Sat = Sat1;

    return;
}

void pims2_(double SPri, double SPrj, double SPis, double SPjs, double SPla0, double SPlo0, double SPrs, double SPscn, double SPyaw)
{
    pInitSat(Sat2, SPri, SPrj, SPis, SPjs, SPla0, SPlo0, SPrs, SPscn, SPyaw);
    Sp2->Proj.Sat = Sat2;

    return;
}


// F--------------
#if 0
#define SIZE 128

void pmvimg_(int* nx, int* ny, unsigned char* buff)
{
#define table_size 128
	int table[table_size], ntable, itab;
	int shift;

	char mvflag[10], printer[10];
	long i, total;
	int k;

	MAGEnqc("METVIEW", mvflag);
	MAGEnqc("METVIEW_PRINTER", printer);
	if (strncmp(mvflag,"ON",2)) return;
	if (!strncmp(printer,"ON",2)) return;

	MAGEnq1i("METVIEW_IMAGE_TABLE_INDEX", table, table_size, &ntable);

	k = SIZE/ntable;
	total = ((long) *nx) * ((long) *ny);
	if (k == 1) shift = 0;
	if (k == 2) shift = 1;
	if (k == 4) shift = 2;
	if (k == 8) shift = 3;
	if (k == 16) shift = 4;

	for (i = 0; i < total; i++)
	{
		/* original values are between 128 and 255.
		   first mask the 8th bit (subtract 128) and
		   then shift (divide by 2 or 4 or 8...)
		*/
		if (buff[i] >= 128)
		{
			itab = table[( (((int)buff[i]) & 0x007F) >> shift)];
			buff[i] = (unsigned char) itab;
		}
	}
}

#endif

short pTooBig(CPoint* p)
{
    /* old version: p->* > BIGFLOAT and p->X < -BIGFLOAT, works for SGI
   but not for Linux */

    return (p->X >= BIGFLOAT || p->Y >= BIGFLOAT || p->X <= -BIGFLOAT || p->Y <= -BIGFLOAT);
}

short pPointOnLine(SImage* Im, CPoint* p, CPoint* q, CPoint* w)
{
    int px, py, qx, qy, wx, wy;

    if (!pContains(Im->IBBox, p))
        return FALSE;
    if (!pContains(Im->IBBox, q))
        return FALSE;

    px = (int)(p->X / Im->Irx);
    py = (int)(p->Y / Im->Iry);
    qx = (int)(q->X / Im->Irx);
    qy = (int)(q->Y / Im->Iry);
    wx = (int)(w->X / Im->Irx);
    wy = (int)(w->Y / Im->Iry);

    if (ABS((qy - py) * (wx - px) - (wy - py) * (qx - px)) >=
        MAX(ABS(qx - px), ABS(qy - py)))
        return FALSE;

    return TRUE;
}

CPoint pMaxPoint(CPoint* p1, CPoint* p2)
{
    CPoint p;

    p.X = MAX(p1->X, p2->X);
    p.Y = MAX(p1->Y, p2->Y);
    return p;
}

CPoint pMinPoint(CPoint* p1, CPoint* p2)
{
    CPoint p;

    p.X = MIN(p1->X, p2->X);
    p.Y = MIN(p1->Y, p2->Y);
    return p;
}

void pInitPoint(CPoint* p, Real x, Real y)
{
    p->X = x;
    p->Y = y;
}

void pInitDatum(Datum* D, short Pcode, char* tx2, Real Ddx, Real Ddy, Real Ddz, double Drd, double Dflt)
{
    D->Dproj = Pcode;
    D->Dname = tx2;
    D->Ddx = Ddx;
    D->Ddy = Ddy;
    D->Ddz = Ddz;
    D->Drd = Drd;
    D->Dflt = Dflt;
}

void pInitProj(SProjection* Sp, char* tx1, char* tx2, int Pid, short Pcode, short Pdatum, Real Pdx, Real Pdy, Real Pdz, double Prd, double Pflt, double Poffx, double Poffy)
{
    Sp->Pname = tx2;
    Sp->Pdname = tx1;
    Sp->Pdatum = Pdatum;
    Sp->Pcode = Pcode;
    Sp->Pgbcode = 0;
    Sp->Pgbcode1 = 0;
    Sp->Pid = Pid;
    Sp->Pdx = Pdx;
    Sp->Pdy = Pdy;
    Sp->Pdz = Pdz;
    Sp->Prd = Prd;
    Sp->Pflt = Pflt;
    Sp->Poffx = Poffx;
    Sp->Poffy = Poffy;
    Sp->Pout = Sp;
}

short pProjEq(SProjection* In, SProjection* Out)
{
    if (In->Pcode != Out->Pcode)
        return FALSE;
    if (In->Pdatum != Out->Pdatum)
        return FALSE;
    if (In->Pdx != Out->Pdx)
        return FALSE;
    if (In->Pdy != Out->Pdy)
        return FALSE;
    if (In->Pdz != Out->Pdz)
        return FALSE;
    if (In->Prd != Out->Prd)
        return FALSE;
    if (In->Pflt != Out->Pflt)
        return FALSE;
    if (In->Poffx != Out->Poffx)
        return FALSE;
    if (In->Poffy != Out->Poffy)
        return FALSE;

    switch (In->Pcode) {
        case PROJCYLINDRICAL:
        case PROJMERCATOR:
        case PROJPOLAR:
        default:

            if (In->Proj.Gp->GPhemis != Out->Proj.Gp->GPhemis)
                return FALSE;
            if (In->Proj.Gp->GPlat0 != Out->Proj.Gp->GPlat0)
                return FALSE;
            if (In->Proj.Gp->GPstlat1 != Out->Proj.Gp->GPstlat1)
                return FALSE;
            if (In->Proj.Gp->GPstlat2 != Out->Proj.Gp->GPstlat2)
                return FALSE;
            break;


        case PROJSATELLITE:

            if (In->Proj.Sat->SPri != Out->Proj.Sat->SPri)
                return FALSE;
            if (In->Proj.Sat->SPrj != Out->Proj.Sat->SPrj)
                return FALSE;
            if (In->Proj.Sat->SPis != Out->Proj.Sat->SPis)
                return FALSE;
            if (In->Proj.Sat->SPjs != Out->Proj.Sat->SPjs)
                return FALSE;
            if (In->Proj.Sat->SPla0 != Out->Proj.Sat->SPla0)
                return FALSE;
            if (In->Proj.Sat->SPlo0 != Out->Proj.Sat->SPlo0)
                return FALSE;
            if (In->Proj.Sat->SPrs != Out->Proj.Sat->SPrs)
                return FALSE;
            if (In->Proj.Sat->SPscn != Out->Proj.Sat->SPscn)
                return FALSE;
            if (In->Proj.Sat->SPyaw != Out->Proj.Sat->SPyaw)
                return FALSE;
            break;
    }

    return TRUE;
}

void pInitGen(GeneralProjection* Gp, short GPhemis, double GPlat0, double GPlon0, double GPstlat1, double GPstlat2)
{
    Gp->GPhemis = GPhemis;
    Gp->GPlat0 = GPlat0;
    Gp->GPlon0 = GPlon0;
    Gp->GPstlat1 = GPstlat1;
    Gp->GPstlat2 = GPstlat2;
}

void pInitSat(Satellite* Sat, double SPri, double SPrj, double SPis, double SPjs, double SPla0, double SPlo0, double SPrs, double SPscn, double SPyaw)
{
    Sat->SPri = SPri;
    Sat->SPrj = SPrj;
    Sat->SPis = SPis;
    Sat->SPjs = SPjs;
    Sat->SPla0 = SPla0;
    Sat->SPlo0 = SPlo0;
    Sat->SPrs = SPrs;
    Sat->SPscn = SPscn;
    Sat->SPyaw = SPyaw;
}

CPoint pLL2PC(SProjection* Sp, CPoint ptll)
{
    CPoint p;

    switch (Sp->Pcode) {
        case PROJCYLINDRICAL:
            p = pCylLL2PC(Sp, ptll);
            break;

        case PROJSATELLITE:
            p = pSatLL2PC(Sp, ptll);
            break;

        case PROJMERCATOR:
            p = pMerLL2PC(Sp, ptll);
            break;

        case PROJPOLAR:
            p = pPolLL2PC(Sp, ptll);
            break;

        default:
            p.X = 0.;
            p.Y = 0.;
            break;
    }

    return p;
}

CPoint pPC2LL(SProjection* Sp, CPoint ptll)
{
    CPoint p;

    switch (Sp->Pcode) {
        case PROJCYLINDRICAL:

            p = pCylPC2LL(Sp, ptll);
            break;

        case PROJSATELLITE:

            p = pSatPC2LL(Sp, ptll);
            break;

        case PROJMERCATOR:

            p = pMerPC2LL(Sp, ptll);
            break;

        case PROJPOLAR:

            p = pPolPC2LL(Sp, ptll);
            break;

        default:

            printf(" Undefined projection!\n");
            p.X = 0.;
            p.Y = 0.;
            break;
    }

    return p;
}

/********************************************************************
        Planimetric datum transformation
********************************************************************/
void ChangeLL(SProjection* Sp, double* lon1, double* lat1)
{
    double equad1, /* Squared eccentricity - datum 1 */
        equad2,    /* Squared eccentricity - datum 2 */
        n1,        /* Great normal of ellipsoid - datum 1 */
        n2,        /* Great normal od ellipsoid - datum 2 */
        x1,        /* Geocentric cartesian coordinates - datum 1  */
        y1,
        z1,
        x2, /* Geocentric cartesian coordinates - datum 2 */
        y2,
        z2,
        d, lat2, /* Ancillary variable */
        lon2;
    Real Pdx, Pdy, Pdz;
    double Prd, Pflt;

    Pdx = Sp->Pdx;
    Pdy = Sp->Pdy;
    Pdz = Sp->Pdz;
    Prd = Sp->Prd;
    Pflt = Sp->Pflt;

    /* Geocentric cartesian coordinates calculation - datum 1 */
    equad1 = 2. * Pflt - pow(Pflt, (double)2);
    n1 = Prd / sqrt((double)1 - equad1 * pow(sin(*lat1), (double)2));
    x1 = n1 * cos(*lat1) * cos(*lon1);
    y1 = n1 * cos(*lat1) * sin(*lon1);
    z1 = (n1 * (1 - equad1)) * sin(*lat1);

    /* Geocentric cartesian coordinates calculation - datum 2 */
    if (Pdx == MAXFLOAT || Sp->Pout->Pdx == MAXFLOAT) {
        x2 = x1;
        y2 = y1;
        z2 = z1;
    }
    else {
        x2 = x1 + (Pdx - Sp->Pout->Pdx);
        y2 = y1 + (Pdy - Sp->Pout->Pdy);
        z2 = z1 + (Pdz - Sp->Pout->Pdz);
    }

    /* Geodetic coordinates calculation - datum 2 */
    equad2 = 2. * Sp->Pout->Pflt - pow(Sp->Pout->Pflt, (double)2);
    lat2 = *lat1;
    do {
        n2 = Sp->Pout->Prd / sqrt((double)1 - equad2 * pow(sin(lat2), (double)2));
        lat2 = atan((z2 + n2 * equad2 * sin(lat2)) / sqrt(x2 * x2 + y2 * y2));
        d = Sp->Pout->Prd / sqrt((double)1 - equad2 * pow(sin(lat2), (double)2)) - n2;
    } while (fabs(d) > 0.0000001);
    lon2 = atan(y2 / x2);
    *lat1 = lat2;
    *lon1 = lon2;
}

/***********************************************************************
        GEODETIC COORDINATES TO SATELLITE PROJECTION
***********************************************************************/
CPoint pSatLL2PC(SProjection* Sp, CPoint ptll)
{
    double equad, n, x, y, z, sn, dp, dl, xlo, yla, lin, col, resx, resy;
    double x1, x2, a, b, c, Rd2, Rm, Rm2, Rs2, v;
    double SPri, SPrj, SPis, SPjs, SPla0, SPlo0, SPrs, SPscn, SPyaw;
    double Pflt, Prd;

    CPoint ppc;
    /* Check if ptll contains MAXFLOAT */

    if (pTooBig(&ptll)) {
        ppc.X = MAXFLOAT;
        ppc.Y = MAXFLOAT;
        return ppc;
    }

    SPri = Sp->Proj.Sat->SPri;
    SPrj = Sp->Proj.Sat->SPrj;
    SPis = Sp->Proj.Sat->SPis;
    SPjs = Sp->Proj.Sat->SPjs;
    SPla0 = Sp->Proj.Sat->SPla0;
    SPlo0 = Sp->Proj.Sat->SPlo0;
    SPrs = Sp->Proj.Sat->SPrs;
    SPscn = Sp->Proj.Sat->SPscn;
    SPyaw = Sp->Proj.Sat->SPyaw;


    Pflt = Sp->Pflt;
    Prd = Sp->Prd;
    xlo = (double)ptll.X;
    yla = (double)ptll.Y;

    /* Cartesian geocentric coordinates */
    xlo = xlo - SPlo0;
    yla = yla - SPla0;
    equad = 2. * Pflt - pow(Pflt, (double)2);
    n = Prd / sqrt((double)1 - equad * pow(sin(yla), (double)2));
    x = n * cos(yla) * cos(xlo);
    y = n * cos(yla) * sin(xlo);
    z = (n * (1 - equad)) * sin(yla);

    /* Field of view angles */
    dp = atan(y / (SPrs - x));
    dl = atan(z * cos(dp) / (SPrs - x));

    /* Visibility test */
    if (x < 0.0) {
        ppc.X = MAXFLOAT;
        ppc.Y = MAXFLOAT;
        return ppc;
    }
    else {
        Rd2 = Prd * Prd;
        Rm = Prd * (1. - Pflt);
        Rm2 = Rm * Rm;
        Rs2 = SPrs * SPrs;

        v = (tan(dp) * tan(dp) * cos(dp) * cos(dp) * Rm2) + (tan(dl) * tan(dl) * Rd2);
        a = cos(dp) * cos(dp) * Rm2 + v;
        b = -2. * SPrs * v;
        c = Rs2 * v - Rd2 * Rm2 * cos(dp) * cos(dp);

        /* Equation Resolution a*x**2+b*x+c=0 */
        v = (b * b) - (4. * a * c);
        if (v < 0.0)
            v = 0.0;
        x1 = (-b + sqrt(v)) / (2. * a);
        x2 = (-b - sqrt(v)) / (2. * a);
        if (x1 < x2)
            x1 = x2;

        if (fabs(x - x1) > 1.0) {
            ppc.X = MAXFLOAT;
            ppc.Y = MAXFLOAT;
            return ppc;
        }
    }
    /* Line and column of image*/
    if (SPscn == 0.)
        sn = 1.0;
    else
        sn = -1.0;
    col = (sn * dp / SPrj + SPjs);
    lin = (-sn * dl / SPri + SPis);

    /* Axis rotation correction due to yaw angle */
    col = col * cos(SPyaw) - lin * sin(SPyaw);
    lin = lin * cos(SPyaw) + col * sin(SPyaw);
    resx = tan(SPrj) * (SPrs - Prd);
    resy = tan(SPri) * (SPrs - Prd);

    ppc.X = (Real)(col * resx);
    ppc.Y = -(Real)(lin * resy);
    return ppc;
}

/*********************************************************************
        SATELLITE PROJECTION TO GEODETIC COORDINATES
**********************************************************************/
CPoint pSatPC2LL(SProjection* Sp, CPoint ptpc)
{
    double dl, dp, x, y, z, x1, x2, equad, n, d, sn,
        a, b, c, Rd2, Rm, Rm2, Rs2, v, ptpcx, ptpcy,
        yla, xlo, resx, resy;
    double SPri, SPrj, SPis, SPjs, SPla0, SPlo0, SPrs, SPscn, SPyaw;
    double Pflt, Prd;

    CPoint ppc;


    SPri = Sp->Proj.Sat->SPri;
    SPrj = Sp->Proj.Sat->SPrj;
    SPis = Sp->Proj.Sat->SPis;
    SPjs = Sp->Proj.Sat->SPjs;
    SPla0 = Sp->Proj.Sat->SPla0;
    SPlo0 = Sp->Proj.Sat->SPlo0;
    SPrs = Sp->Proj.Sat->SPrs;
    SPscn = Sp->Proj.Sat->SPscn;
    SPyaw = Sp->Proj.Sat->SPyaw;

    Pflt = Sp->Pflt;
    Prd = Sp->Prd;

    resx = tan(SPrj) * (SPrs - Prd);
    resy = tan(SPri) * (SPrs - Prd);
    ptpcx = (double)ptpc.X / resx;
    ptpcy = -(double)ptpc.Y / resy;

    /* Axis rotation correction due yaw angle */
    ptpcx = ptpcx * cos(SPyaw) + ptpcy * sin(SPyaw);
    ptpcy = ptpcy * cos(SPyaw) - ptpcx * sin(SPyaw);

    /* Field of view angles */
    if (SPscn == 0.)
        sn = 1.0;
    else
        sn = -1.0;
    dl = -sn * ((ptpcy - SPis) * SPri);
    dp = sn * ((ptpcx - SPjs) * SPrj);

    /* Cartesian coordinates */
    Rd2 = Prd * Prd;
    Rm = Prd * (1. - Pflt);
    Rm2 = Rm * Rm;
    Rs2 = SPrs * SPrs;

    v = (tan(dp) * tan(dp) * cos(dp) * cos(dp) * Rm2) + (tan(dl) * tan(dl) * Rd2);
    a = cos(dp) * cos(dp) * Rm2 + v;
    b = -2. * SPrs * v;
    c = Rs2 * v - Rd2 * Rm2 * cos(dp) * cos(dp);

    /* Equation Resolution a*x**2+b*x+c=0  */
    v = (b * b) - (4. * a * c);
    if (v < 0) {
        ppc.X = MAXFLOAT;
        ppc.Y = MAXFLOAT;
        return ppc;
    }

    x1 = (-b + sqrt(v)) / (2. * a);
    x2 = (-b - sqrt(v)) / (2. * a);

    if (x1 >= x2)
        x = x1;
    else
        x = x2;

    z = (SPrs - x) * tan(dl) / cos(dp);
    y = (SPrs - x) * tan(dp);

    /* Geodetic coordinates */
    equad = 2. * Pflt - Pflt * Pflt;
    if (dl > Prd / SPrs)
        yla = PI / 2.;
    else if (dl < -Prd / SPrs)
        yla = -PI / 2.;
    else
        yla = asin(SPrs * dl / Prd);

    do {
        n = Prd / sqrt((double)1 - equad * pow(sin(yla), (double)2));
        yla = atan((z + n * equad * sin(yla)) / sqrt(x * x + y * y));
        d = Prd / sqrt((double)1 - equad * pow(sin(yla), (double)2)) - n;
    } while (fabs(d) > 0.001);

    xlo = atan(y / x) + SPlo0;
    yla = yla + SPla0;

    ppc.X = (Real)xlo;
    ppc.Y = (Real)yla;
    return ppc;
}

/********************************************************************
    GEODETIC TO EQUIDISTANT CYLINDRICAL COORDINATES
********************************************************************/
CPoint pCylLL2PC(SProjection* Sp, CPoint ptll)
{
    double GPstlat1, GPlon0, Prd;
    double ptllx, ptlly;
    CPoint ppc;

    ptllx = (double)ptll.X;
    ptlly = (double)ptll.Y;

    Prd = Sp->Prd;
    GPlon0 = Sp->Proj.Gp->GPlon0;
    GPstlat1 = Sp->Proj.Gp->GPstlat1;


    ptllx = Prd * (ptllx - GPlon0) * cos(GPstlat1);
    ptlly = Prd * ptlly;

    ppc.X = (Real)ptllx;
    ppc.Y = (Real)ptlly;

    return ppc;
}

/********************************************************************
    EQUIDISTANT CYLINDRICAL TO GEODETIC COORDINATES
********************************************************************/
CPoint pCylPC2LL(SProjection* Sp, CPoint ptpc)
{
    double ptpcx, ptpcy;
    double GPstlat1, GPlon0, Prd;

    CPoint ppc;

    ptpcx = (double)ptpc.X;
    ptpcy = (double)ptpc.Y;

    Prd = Sp->Prd;
    GPlon0 = Sp->Proj.Gp->GPlon0;
    GPstlat1 = Sp->Proj.Gp->GPstlat1;

    ptpcy = ptpcy / Prd;
    ptpcx = GPlon0 + ptpcx / (Prd * cos(GPstlat1));

    if (Sp->Pid != Sp->Pout->Pid)
        ChangeLL(Sp, &ptpcx, &ptpcy);

    ppc.X = (Real)ptpcx;
    ppc.Y = (Real)ptpcy;

    return ppc;
}

/********************************************************************
    GEODETIC TO POLAR STEREOGRAPHIC COORDINATES
********************************************************************/
CPoint pPolLL2PC(SProjection* Sp, CPoint ptll)
{
    double k0, /* Scale factor */
        e,     /* Eccentricity */
        lon0,  /* auxilliary origin longitude */
        t, ro, aux1, aux2, aux3, ptllx, ptlly;
    short GPhemis;
    double Pflt, Prd;
    double GPlon0;
    CPoint ppc;

    ptllx = (double)ptll.X;
    ptlly = (double)ptll.Y;

    GPhemis = Sp->Proj.Gp->GPhemis;
    // GPlat0 = Sp->Proj.Gp->GPlat0;
    GPlon0 = Sp->Proj.Gp->GPlon0;
    // GPstlat1 = Sp->Proj.Gp->GPstlat1;
    // GPstlat2 = Sp->Proj.Gp->GPstlat2;

    Pflt = Sp->Pflt;
    Prd = Sp->Prd;

    /*	k0 = 0.994;	 Standard parallel 80.1 degrees  */
    k0 = 0.933; /* Standard parallel 60 degrees */
    e = sqrt((double)2 * Pflt - pow(Pflt, (double)2));

    ptlly *= GPhemis;
    ptllx *= GPhemis;
    lon0 = (GPhemis == 1) ? GPlon0 : -GPlon0;

    aux1 = (1. - e * sin(ptlly)) / (1. + e * sin(ptlly));
    t = tan((PI / 4.) - (ptlly / 2.)) / pow(aux1, (e / (double)2));
    aux2 = pow(((double)1 + e), ((double)1 + e));
    aux3 = pow(((double)1 - e), ((double)1 - e));
    ro = 2. * Prd * k0 * t / sqrt(aux2 * aux3);

    aux1 = ro * sin(ptllx - lon0);
    ptlly = -ro * cos(ptllx - lon0);
    aux1 *= GPhemis;
    ptlly *= GPhemis;

    if (GPhemis == -1) {
        aux1 *= GPhemis;
        ptlly *= GPhemis;
    }
    ppc.X = (Real)aux1;
    ppc.Y = (Real)ptlly;

    return ppc;
}

/*******************************************************************
    POLAR STEREOGRAPHIC TO GEODETIC COORDINATES
********************************************************************/
CPoint pPolPC2LL(SProjection* Sp, CPoint ptpc)
{
    double k0, /* Scale factor */
        equad, /* Squared eccentricity */
        e,     /* Eccentricity */
        lon0,  /* auxilliary origin longitude */
        ro, t, xx, aux1, aux2, aux3, aux4, aux5, ptpcx = 0., ptpcy, px, py;
    short GPhemis;
    double GPlon0;
    double Pflt, Prd;
    CPoint ppc;

    px = (double)ptpc.X;
    py = (double)ptpc.Y;

    GPhemis = Sp->Proj.Gp->GPhemis;
    // GPlat0 = Sp->Proj.Gp->GPlat0;
    GPlon0 = Sp->Proj.Gp->GPlon0;
    // GPstlat1 = Sp->Proj.Gp->GPstlat1;
    // GPstlat2 = Sp->Proj.Gp->GPstlat2;

    Pflt = Sp->Pflt;
    Prd = Sp->Prd;
    /*	k0 = 0.994;	 Standard parallel 80.1 degrees  */
    k0 = 0.933; /* Standard parallel 60 degrees */
    equad = 2. * Pflt - pow(Pflt, (double)2);
    e = sqrt(equad);

    if (GPhemis == -1) {
        px *= GPhemis;
        py *= GPhemis;
    }

    lon0 = (GPhemis == 1) ? GPlon0 : -GPlon0;

    ro = sqrt(px * px + py * py);
    aux1 = pow(((double)1 + e), ((double)1 + e));
    aux2 = pow(((double)1 - e), ((double)1 - e));
    t = (ro * sqrt(aux1 * aux2)) / (2. * Prd * k0);
    xx = PI / 2. - 2. * atan(t);
    aux3 = equad / 2. + 5. * equad * equad / 24. + equad * equad * equad / 12.;
    aux4 = 7. * equad * equad / 48. + 29. * equad * equad * equad / 240.;
    aux5 = 7. * equad * equad * equad / 120.;

    ptpcy = xx + aux3 * sin((double)2 * xx) + aux4 * sin((double)4 * xx) + aux5 * sin((double)6 * xx);

    if (py != 0.)
        ptpcx = lon0 + atan(px / (-py));


    if (GPhemis == 1) {
        if (px > 0. && py > 0.)
            ptpcx = ptpcx + PI;
        else if (px < 0. && py > 0.)
            ptpcx = ptpcx - PI;
        else if (px > 0. && py == 0.)
            ptpcx = lon0 + PI / 2.;
        else if (px < 0. && py == 0.)
            ptpcx = lon0 - PI / 2.;
        else if (px == 0. && py == 0.)
            ptpcx = lon0;
    }
    else {
        ptpcy *= GPhemis;
        ptpcx *= GPhemis;

        if (px > 0. && py < 0.)
            ptpcx = ptpcx + PI;
        else if (px < 0. && py < 0.)
            ptpcx = ptpcx - PI;
        else if (px > 0. && py == 0.)
            ptpcx = lon0 + PI / 2.;
        else if (px < 0. && py == 0.)
            ptpcx = lon0 - PI / 2.;
        else if (px == 0. && py == 0.)
            ptpcx = lon0;
    }

    if (ptpcx < (-PI))
        ptpcx += 2. * PI;
    else if (ptpcx > PI)
        ptpcx -= 2. * PI;

    if (Sp->Pid != Sp->Pout->Pid)
        ChangeLL(Sp, &ptpcx, &ptpcy);

    ppc.X = (Real)ptpcx;
    ppc.Y = (Real)ptpcy;

    return ppc;
}

/********************************************************************
        GEODETIC TO MERCATOR COORDINATES
********************************************************************/
CPoint pMerLL2PC(SProjection* Sp, CPoint ptll)
{
    double equad, /*Squared eccentricity  */
        aux1,     /* Ancillary variables */
        aux2, aux3, aux4, aux5, aux6, ptllx, ptlly;
    // short	GPhemis;
    double Pflt, Prd;
    double GPlon0, GPstlat1;
    CPoint ppc;

    ptllx = (double)ptll.X;
    ptlly = (double)ptll.Y;

    // GPhemis = Sp->Proj.Gp->GPhemis;
    // GPlat0 = Sp->Proj.Gp->GPlat0;
    GPlon0 = Sp->Proj.Gp->GPlon0;
    GPstlat1 = Sp->Proj.Gp->GPstlat1;
    // GPstlat2 = Sp->Proj.Gp->GPstlat2;

    Pflt = Sp->Pflt;
    Prd = Sp->Prd;

    equad = 2. * Pflt - pow(Pflt, (double)2);
    aux1 = (1. + tan(ptlly / (double)2)) / (1. - tan(ptlly / (double)2));
    aux2 = (equad + equad * equad / 4. + equad * equad * equad / 8.) * sin(ptlly);
    aux3 = (equad * equad / 12. + equad * equad * equad / 16.) * sin((double)3 * ptlly);
    aux4 = (equad * equad * equad / 80.) * sin((double)5 * ptlly);
    aux5 = cos(GPstlat1);
    aux6 = 1. / sqrt((double)1 - equad * pow(sin(GPstlat1), (double)2));

    ptllx = Prd * (ptllx - GPlon0) * aux5 * aux6;
    ptlly = Prd * (log(aux1) - aux2 + aux3 - aux4) * aux5 * aux6;

    ppc.X = (Real)ptllx;
    ppc.Y = (Real)ptlly;

    return ppc;
}

/********************************************************************
        MERCATOR TO GEODETIC COORDINATES
********************************************************************/
CPoint pMerPC2LL(SProjection* Sp, CPoint ptpc)
{
    double equad, /*Squared eccentricity  */
        t,        /* Ancillary variables */
        xx, aux1, aux2, aux3, aux4, aux5, ptpcx, ptpcy;
    // short	GPhemis;
    double GPlon0, GPstlat1;
    double Pflt, Prd;
    CPoint ppc;

    ptpcx = (double)ptpc.X;
    ptpcy = (double)ptpc.Y;

    // GPhemis = Sp->Proj.Gp->GPhemis;
    // GPlat0 = Sp->Proj.Gp->GPlat0;
    GPlon0 = Sp->Proj.Gp->GPlon0;
    GPstlat1 = Sp->Proj.Gp->GPstlat1;
    // GPstlat2 = Sp->Proj.Gp->GPstlat2;

    Pflt = Sp->Pflt;
    Prd = Sp->Prd;

    equad = 2. * Pflt - pow(Pflt, (double)2);

    aux1 = cos(GPstlat1);
    aux2 = 1. / sqrt((double)1 - equad * pow(sin(GPstlat1), (double)2));
    ptpcx = ptpcx / (aux1 * aux2);
    ptpcy = ptpcy / (aux1 * aux2);
    t = exp(-ptpcy / Prd);
    xx = PI / 2. - 2. * atan(t);
    aux3 = (equad / 2. + 5. * equad * equad / 24. + equad * equad * equad / 12.) * sin(4. * atan(t));
    aux4 = -(7. * equad * equad / 48. + 29. * equad * equad * equad / 240.) * sin(8. * atan(t));
    aux5 = (7. * equad * equad * equad / 120.) * sin(12. * atan(t));

    ptpcy = xx + aux3 + aux4 + aux5;
    ptpcx = ptpcx / Prd + GPlon0;

    if (Sp->Pid != Sp->Pout->Pid)
        ChangeLL(Sp, &ptpcx, &ptpcy);

    ppc.X = (Real)ptpcx;
    ppc.Y = (Real)ptpcy;

    return ppc;
}

void pInitImage(SImage* Im, BBox* IBBox, short Iny, short Inx,
                Real Irx, Real Iry, unsigned long pixel,
                unsigned long outsidevalue, unsigned char* tbuf,
                SProjection* IProj)
{
    Im->IProj = IProj;
    Im->IBBox = IBBox;
    Im->Inx = Inx;
    Im->Iny = Iny;
    Im->Irx = Irx;
    Im->Iry = Iry;
    Im->pixel = pixel;
    Im->outsidevalue = outsidevalue;
    Im->tbuf = tbuf;
}

void pInterpolateIn(SImage* Imi, SImage* Imo, CPoint poll, CPoint pour, CPoint poul, CPoint polr, CPoint pill, CPoint piur, CPoint piul, CPoint pilr)
{
    short x1, y1, x2, y2, i, j;
    CPoint paux;
    Real x, y;
    Real xl, /* x at the beginning of the line */
        yl,  /* y at the beginning of the line */
        xr,  /* x at the end of the line */
        yr,  /* y at the end of the line */
        dx,  /* inner loop x increment  */
        dy,  /* inner loop y increment */
        dxl, /* x increment at the beginning of line */
        dyl, /* y increment at the beginning of line */
        dxr, /* x increment at the end of line */
        dyr; /* y increment at the end of line */

    SProjection* p = Imi->IProj;

    CPoint pou, pob, pol, por, pom;

    CPoint piu, pib, pil, pir, pim;

    CPoint pxill, pxiul, pxilr, pxiur;

    /*Check if input image contains desired area. If not, let's try another area. */

    BBox da;

    unsigned char c;

    pInitBBox(&da, poll.X, poll.Y, pour.X, pour.Y);
    pBBoxRemapInOut(Imo->IProj, p, &da);

    paux = pCoord2Index(Imo, &poul);
    x1 = (short)(paux.X + .5);
    y1 = (short)(paux.Y + .5);

    paux = pCoord2Index(Imo, &polr);
    x2 = (short)(paux.X + .5);
    y2 = (short)(paux.Y + .5);

    if (!pIntersects(&da, Imi->IBBox)) {
        /*printf (" Out Image x1=%4d y1=%4d x2=%4d y2=%4d",x1,y1,x2,y2);*/
        return;
    }


    /* If desired area on output image is too small,
let's perform the interpolation point by point. */

    pInitBBox(&da, poll.X, poll.Y, pour.X, pour.Y);
    if (pWidth(&da) < 3. * Imo->Irx || pHeight(&da) < 3. * Imo->Iry) {
        y = poul.Y;
        for (i = y1; i <= y2; i++, y -= Imo->Iry) {
            x = poul.X;
            for (j = x1; j <= x2; j++, x += Imo->Irx) {
                pInitPoint(&paux, x, y);
                paux = pPC2LL(Imo->IProj, paux);
                if (pTooBig(&paux))
                    continue;

                paux = pLL2PC(p, paux);
                if (pTooBig(&paux))
                    continue;

                paux = pCoord2Index(Imi, &paux);
                c = pInterAt(Imi, &paux);
                pPut(Imo, i, j, c);
            }
        }
        return;
    }

    /* Check if linear interpolation may be performed on input image
Evaluate point at middle of the edges and check if these points belong
to the edges in input image domain.If they belong,a linear
interpolation may be performed, else subdivide output image in four
quadrants and try interpolating again */

    pInitPoint(&pou, (pour.X - poul.X) / 2 + poul.X, poul.Y);
    pInitPoint(&pob, (polr.X - poll.X) / 2. + poll.X, poll.Y);
    pInitPoint(&pol, poll.X, (poul.Y - poll.Y) / 2. + poll.Y);
    pInitPoint(&por, polr.X, (pour.Y - polr.Y) / 2. + polr.Y);
    pInitPoint(&pom, (por.X - pol.X) / 2. + pol.X, (pou.Y - pob.Y) / 2. + pob.Y);

    if (pProjEq(p, (Imo->IProj))) {
        piu.X = pou.X;
        piu.Y = pou.Y;
        pib.X = pob.X;
        pib.Y = pob.Y;
        pil.X = pol.X;
        pil.Y = pol.Y;
        pir.X = por.X;
        pir.Y = por.Y;
        pim.X = pom.X;
        pim.Y = pom.Y;
    }
    else {
        piu = pPC2LL(Imo->IProj, pou);
        if (!pTooBig(&piu))
            piu = pLL2PC(p, piu);
        pib = pPC2LL(Imo->IProj, pob);
        if (!pTooBig(&pib))
            pib = pLL2PC(p, pib);
        pil = pPC2LL(Imo->IProj, pol);
        if (!pTooBig(&pil))
            pil = pLL2PC(p, pil);
        pir = pPC2LL(Imo->IProj, por);
        if (!pTooBig(&pir))
            pir = pLL2PC(p, pir);
        pim = pPC2LL(Imo->IProj, pom);
        if (!pTooBig(&pim))
            pim = pLL2PC(p, pim);
    }


    /* Check special case for Satellite projection, when an area outside the globe
    is being remapped, therefore none of these points will have valid values and
    we will give up. */

    if (pTooBig(&piu) &&
        pTooBig(&pib) &&
        pTooBig(&pil) &&
        pTooBig(&pir) &&
        pTooBig(&piul) &&
        pTooBig(&piur) &&
        pTooBig(&pilr) &&
        pTooBig(&pill) &&
        pTooBig(&pim)) {
        /*printf (" Out Globe x1=%4d y1=%4d x2=%4d y2=%4d",x1,y1,x2,y2);*/
        return;
    }

    /* Check if middle points belong to the edges */
    if (!pPointOnLine(Imi, &piul, &piur, &piu) ||
        !pPointOnLine(Imi, &pilr, &piur, &pir) ||
        !pPointOnLine(Imi, &pill, &pilr, &pib) ||
        !pPointOnLine(Imi, &pill, &piul, &pil)) {
        XXX++;
        if (pTooBig(&piu))
            pInitPoint(&piu, MAXFLOAT, MAXFLOAT);
        if (pTooBig(&pil))
            pInitPoint(&pil, -MAXFLOAT, -MAXFLOAT);
        if (pTooBig(&pim))
            pInitPoint(&pim, MAXFLOAT, -MAXFLOAT);
        pInterpolateIn(Imi, Imo, pol, pou, poul, pom, pil, piu, piul, pim);

        /*		printf ("Quadrant 2 - Level %d",XXX); */
        if (pTooBig(&piu))
            pInitPoint(&piu, -MAXFLOAT, MAXFLOAT);
        if (pTooBig(&pim))
            pInitPoint(&pim, -MAXFLOAT, -MAXFLOAT);
        if (pTooBig(&pir))
            pInitPoint(&pir, MAXFLOAT, -MAXFLOAT);
        pInterpolateIn(Imi, Imo, pom, pour, pou, por, pim, piur, piu, pir);

        /*		printf ("Quadrant 3 - Level %d",XXX); */
        if (pTooBig(&pil))
            pInitPoint(&pil, -MAXFLOAT, MAXFLOAT);
        if (pTooBig(&pim))
            pInitPoint(&pim, MAXFLOAT, MAXFLOAT);
        if (pTooBig(&pib))
            pInitPoint(&pib, MAXFLOAT, -MAXFLOAT);
        pInterpolateIn(Imi, Imo, poll, pom, pol, pob, pill, pim, pil, pib);

        /*		printf ("Quadrant 4 - Level %d",XXX); */
        if (pTooBig(&pim))
            pInitPoint(&pim, -MAXFLOAT, MAXFLOAT);
        if (pTooBig(&pir))
            pInitPoint(&pir, MAXFLOAT, MAXFLOAT);
        if (pTooBig(&pib))
            pInitPoint(&pib, -MAXFLOAT, -MAXFLOAT);
        pInterpolateIn(Imi, Imo, pob, por, pom, polr, pib, pir, pim, pilr);

        XXX--;
        return;
    }

    /* Start linear interpolation on input image. */
    pxill = pCoord2Index(Imi, &pill);
    pxiul = pCoord2Index(Imi, &piul);
    pxilr = pCoord2Index(Imi, &pilr);
    pxiur = pCoord2Index(Imi, &piur);

    /* Evaluate the increments in x and y on both sides of input image */
    dxl = (pxill.X - pxiul.X) / (Real)(y2 - y1);
    dyl = (pxill.Y - pxiul.Y) / (Real)(y2 - y1);

    dxr = (pxilr.X - pxiur.X) / (Real)(y2 - y1);
    dyr = (pxilr.Y - pxiur.Y) / (Real)(y2 - y1);

    /* Set initial values for x and y at beginning of line on input image */
    xl = pxiul.X + .5;
    yl = pxiul.Y + .5;

    /* Set initial values for x and y at end of line on input image */
    xr = pxiur.X;
    yr = pxiur.Y;

    /* Evaluate increments for the first line */
    dx = (xr - xl) / (Real)(x2 - x1);
    dy = (yr - yl) / (Real)(x2 - x1);

    x = xl + .5; /* round to nearest pixel */
    y = yl + .5; /* round to nearest pixel */

    for (i = y1; i <= y2; i++) {
        for (j = x1; j <= x2; j++) {
            pInitPoint(&paux, x, y);
            c = pInterAt(Imi, &paux);
            pPut(Imo, i, j, c);
            /* Imo->tbuf[Imo->Iny*i+j] = c; */
            /* (*this)(i,j) = imi->InterpolateAt (paux);  bilinear */
            /* (*this)(i,j) = (int)(y+.5);		 debugging */
            /* (*this)(i,j) = (*imi)((int)y,(int)x);	 nearest */
            x += dx;
            y += dy;
        }

        xl += dxl;
        x = xl + .5; /* round to nearest pixel */
        xr += dxr;
        yl += dyl;
        y = yl + .5; /* round to nearest pixel */
        yr += dyr;
        dx = (xr - xl) / (Real)(x2 - x1);
        dy = (yr - yl) / (Real)(x2 - x1);
    }
}

unsigned char pInterAt(SImage* Imi, CPoint* p)
{
    Real x, y, dx, dy, dx1, dy1, p1, p2;
    short u, v;

    x = p->X;
    y = p->Y;

    if (x < 0. || x >= (Real)(Imi->Inx - 1) || y < 0. || y >= (Real)(Imi->Iny - 1)) {
        return (unsigned char)Imi->outsidevalue;
    }

    u = (short)x; /* truncate to upper left position in a 2x2 grid */
    v = (short)y;

    dx = x - u; /* distance from input position to upper left position */
    dy = y - v;

    dx1 = 1. - dx; /* distance from input position to bottom right position */
    dy1 = 1. - dy;

    /* interpolate first col */
    p1 = (Real)pGet(Imi, v, u) * dy1 + (Real)pGet(Imi, v + 1, u) * dy;

    /* interpolate second col */
    p2 = (Real)pGet(Imi, v, u + 1) * dy1 + (Real)pGet(Imi, v + 1, u + 1) * dy;

    /* interpolate on row */
    u = (short)(p1 * dx1 + p2 * dx + 0.5);
    v = ((u >> 1) & 0x00FF) | 0x0080;
    return (unsigned char)v;
}

void pPut(SImage* Im, short lin, short col, unsigned char c)
{
    unsigned char* p;

    if (((lin < Im->Iny) && (col < Im->Inx) && (lin >= 0)) || col >= 0) {
        p = Im->tbuf;
        p[(int)(lin * Im->Inx + col)] = c;
    }
    else {
        printf("Index problem:%d %d\n", lin, col);
    }
}

unsigned char pGet(SImage* Im, short lin, short col)
{
    unsigned char* p;

    if (lin >= Im->Iny || col >= Im->Inx || lin < 0 || col < 0)
        return (unsigned char)Im->pixel;

    p = Im->tbuf;

    return p[(int)(lin * Im->Inx + col)];
}

CPoint pCoord2Index(SImage* Im, CPoint* p)
{
    Real x, y;
    CPoint q;
    short ss;

    x = (p->X - Im->IBBox->Bll.X) / Im->Irx;
    y = (Im->IBBox->Bur.Y - p->Y) / Im->Iry;
    ss = Im->Inx;
    if (x > ss)
        x = ss;
    else if (x < 0.)
        x = 0.;
    ss = Im->Iny;
    if (y > ss)
        y = ss;
    else if (y < 0.)
        y = 0.;

    q.X = x;
    q.Y = y;
    return q;
}

void pRemapI(SImage* Imi, SImage* Imo)
{
    SProjection* p = Imi->IProj;

    /* Evaluate coordinates of output image four corners */

    CPoint poll, pour, pill, piur, piul, pilr, poul, polr;

    pInitPoint(&poll, Imo->IBBox->Bll.X, Imo->IBBox->Bll.Y);
    pInitPoint(&pour, Imo->IBBox->Bur.X, Imo->IBBox->Bur.Y);
    pInitPoint(&poul, poll.X, pour.Y);
    pInitPoint(&polr, pour.X, poll.Y);

    /* Evaluate corresponding coordinates of input image corners */

    if (pProjEq(Imo->IProj, p)) {
        piul.X = poul.X;
        piul.Y = poul.Y;
        piur.X = pour.X;
        piur.Y = pour.Y;
        pill.X = poll.X;
        pill.Y = poll.Y;
        pilr.X = polr.X;
        pilr.X = polr.Y;
    }
    else {
        piul = pPC2LL(Imo->IProj, poul);
        if (pTooBig(&piul))
            pInitPoint(&piul, -MAXFLOAT, MAXFLOAT);
        else {
            piul = pLL2PC(p, piul);
            if (pTooBig(&piul))
                pInitPoint(&piul, -MAXFLOAT, MAXFLOAT);
        }
        piur = pPC2LL(Imo->IProj, pour);
        if (pTooBig(&piur))
            pInitPoint(&piur, MAXFLOAT, MAXFLOAT);
        else {
            piur = pLL2PC(p, piur);
            if (pTooBig(&piur))
                pInitPoint(&piur, MAXFLOAT, MAXFLOAT);
        }
        pill = pPC2LL(Imo->IProj, poll);
        if (pTooBig(&pill))
            pInitPoint(&pill, -MAXFLOAT, -MAXFLOAT);
        else {
            pill = pLL2PC(p, pill);
            if (pTooBig(&pill))
                pInitPoint(&pill, -MAXFLOAT, -MAXFLOAT);
        }
        pilr = pPC2LL(Imo->IProj, polr);
        if (pTooBig(&pilr))
            pInitPoint(&pilr, MAXFLOAT, -MAXFLOAT);
        else {
            pilr = pLL2PC(p, pilr);
            if (pTooBig(&pilr))
                pInitPoint(&pilr, MAXFLOAT, -MAXFLOAT);
        }
    }
    /* Start generating grid */
    /*
    printf("Start InterpolateIn:%f,%f,%f,%f\n",poll.X,poll.Y,pour.X,pour.Y);
*/
    XXX = 0;
    pInterpolateIn(Imi, Imo, poll, pour, poul, polr, pill, piur, piul, pilr);
}

void pInitRemap(SImage* Imo, BBox* IBBox, SProjection* Sp, Real rx, Real ry, unsigned char** tmpbuf, SImage* Imi)
{
    unsigned char *bufp, *bufc;
    Real y1, y2, x1, x2, incx, incy, resx, resy;
    short nx, ny;
    int i;

    Imo->IProj = Sp;
    Imo->IBBox = IBBox;
    if ((int)rx == 0)
        resx = Imi->Irx;
    else
        resx = rx;
    if ((int)ry == 0)
        resy = Imi->Iry;
    else
        resy = ry;
    x1 = IBBox->Bll.X;
    y1 = IBBox->Bll.Y;
    x2 = IBBox->Bur.X;
    y2 = IBBox->Bur.Y;
    nx = (short)(ABS((x2 - x1) / resx) + 1.);
    ny = (short)(ABS((y2 - y1) / resy) + 1.);
    Imo->Inx = nx;
    Imo->Iny = ny;
    incx = (x2 - x1) / (Real)(nx - 1);
    incy = (y2 - y1) / (Real)(ny - 1);
    Imo->Irx = ABS(incx);
    Imo->Iry = ABS(incy);
    Imo->pixel = Imi->pixel;
    Imo->outsidevalue = Imi->outsidevalue;

    bufp = (unsigned char*)malloc((int)(sizeof(unsigned char) * nx * ny));
    bufc = bufp;
    for (i = 0; i < (int)nx * ny; i++)
        *bufc++ = (unsigned char)0;
    *tmpbuf = bufp;
    Imo->tbuf = bufp;

    pRemapI(Imi, Imo);
}
