Beispiel #1
0
int pj_apply_gridshift_2( PJ *defn, int inverse, 
                          long point_count, int point_offset,
                          double *x, double *y, double *z )

{
    if( defn->gridlist == NULL )
    {
        defn->gridlist = 
            pj_gridlist_from_nadgrids( pj_get_ctx( defn ),
                                       pj_param(defn->ctx, defn->params,"snadgrids").s,
                                       &(defn->gridlist_count) );

        if( defn->gridlist == NULL || defn->gridlist_count == 0 )
            return defn->ctx->last_errno;
    }
     
    return pj_apply_gridshift_3( pj_get_ctx( defn ),
                                 defn->gridlist, defn->gridlist_count, inverse, 
                                 point_count, point_offset, x, y, z );
}
Beispiel #2
0
int proj_errno_set (const PJ *P, int err) {
/******************************************************************************
    Set context-errno, bubble it up to the thread local errno, return err
******************************************************************************/
    /* Use proj_errno_reset to explicitly clear the error status */
    if (0==err)
        return 0;

    /* For P==0 err goes to the default context */
    proj_context_errno_set (pj_get_ctx ((PJ *) P), err);
    errno = err;
    return err;
}
Beispiel #3
0
int proj_errno_reset (const PJ *P) {
/******************************************************************************
    Clears errno in the context and thread local levels
    through the low level pj_ctx interface.

    Returns the previous value of the errno, for convenient reset/restore
    operations:

    int foo (PJ *P) {
        // errno may be set on entry, but we need to reset it to be able to
        // check for errors from "do_something_with_P(P)"
        int last_errno = proj_errno_reset (P);

        // local failure
        if (0==P)
            return proj_errno_set (P, 42);

        // call to function that may fail
        do_something_with_P (P);

        // failure in do_something_with_P? - keep latest error status
        if (proj_errno(P))
            return proj_errno (P);

        // success - restore previous error status, return 0
        return proj_errno_restore (P, last_errno);
    }
******************************************************************************/
    int last_errno;
    last_errno = proj_errno (P);

    pj_ctx_set_errno (pj_get_ctx ((PJ *) P), 0);
    errno = 0;
    pj_errno = 0;
    return last_errno;
}
Beispiel #4
0
int proj_errno (const PJ *P) {
/******************************************************************************
    Read an error level from the context of a PJ.
******************************************************************************/
    return pj_ctx_get_errno (pj_get_ctx ((PJ *) P));
}
Beispiel #5
0
int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset,
                  double *x, double *y, double *z )

{
    long      i;
    int       err;

    srcdefn->ctx->last_errno = 0;
    dstdefn->ctx->last_errno = 0;

    if( point_offset == 0 )
        point_offset = 1;

/* -------------------------------------------------------------------- */
/*      Transform unusual input coordinate axis orientation to          */
/*      standard form if needed.                                        */
/* -------------------------------------------------------------------- */
    if( strcmp(srcdefn->axis,"enu") != 0 )
    {
        int err;

        err = pj_adjust_axis( srcdefn->ctx, srcdefn->axis, 
                              0, point_count, point_offset, x, y, z );
        if( err != 0 )
            return err;
    }

/* -------------------------------------------------------------------- */
/*      Transform Z to meters if it isn't already.                      */
/* -------------------------------------------------------------------- */
    if( srcdefn->vto_meter != 1.0 && z != NULL )
    {
        for( i = 0; i < point_count; i++ )
            z[point_offset*i] *= srcdefn->vto_meter;
    }

/* -------------------------------------------------------------------- */
/*      Transform geocentric source coordinates to lat/long.            */
/* -------------------------------------------------------------------- */
    if( srcdefn->is_geocent )
    {
        if( z == NULL )
        {
            pj_ctx_set_errno( pj_get_ctx(srcdefn), PJD_ERR_GEOCENTRIC);
            return PJD_ERR_GEOCENTRIC;
        }

        if( srcdefn->to_meter != 1.0 )
        {
            for( i = 0; i < point_count; i++ )
            {
                if( x[point_offset*i] != HUGE_VAL )
                {
                    x[point_offset*i] *= srcdefn->to_meter;
                    y[point_offset*i] *= srcdefn->to_meter;
                }
            }
        }

        err = pj_geocentric_to_geodetic( srcdefn->a_orig, srcdefn->es_orig,
                                         point_count, point_offset, 
                                         x, y, z );
        if( err != 0 )
            return err;
    }

/* -------------------------------------------------------------------- */
/*      Transform source points to lat/long, if they aren't             */
/*      already.                                                        */
/* -------------------------------------------------------------------- */
    else if( !srcdefn->is_latlong )
    {
        if( srcdefn->inv == NULL )
        {
            pj_ctx_set_errno( pj_get_ctx(srcdefn), -17 );
            pj_log( pj_get_ctx(srcdefn), PJ_LOG_ERROR, 
                    "pj_transform(): source projection not invertable" );
            return -17;
        }

        for( i = 0; i < point_count; i++ )
        {
            XY         projected_loc;
            LP	       geodetic_loc;

            projected_loc.u = x[point_offset*i];
            projected_loc.v = y[point_offset*i];

            if( projected_loc.u == HUGE_VAL )
                continue;

            geodetic_loc = pj_inv( projected_loc, srcdefn );
            if( srcdefn->ctx->last_errno != 0 )
            {
                if( (srcdefn->ctx->last_errno != 33 /*EDOM*/ 
                     && srcdefn->ctx->last_errno != 34 /*ERANGE*/ )
                    && (srcdefn->ctx->last_errno > 0 
                        || srcdefn->ctx->last_errno < -44 || point_count == 1
                        || transient_error[-srcdefn->ctx->last_errno] == 0 ) )
                    return srcdefn->ctx->last_errno;
                else
                {
                    geodetic_loc.u = HUGE_VAL;
                    geodetic_loc.v = HUGE_VAL;
                }
            }

            x[point_offset*i] = geodetic_loc.u;
            y[point_offset*i] = geodetic_loc.v;
        }
    }
/* -------------------------------------------------------------------- */
/*      But if they are already lat long, adjust for the prime          */
/*      meridian if there is one in effect.                             */
/* -------------------------------------------------------------------- */
    if( srcdefn->from_greenwich != 0.0 )
    {
        for( i = 0; i < point_count; i++ )
        {
            if( x[point_offset*i] != HUGE_VAL )
                x[point_offset*i] += srcdefn->from_greenwich;
        }
    }

/* -------------------------------------------------------------------- */
/*      Do we need to translate from geoid to ellipsoidal vertical      */
/*      datum?                                                          */
/* -------------------------------------------------------------------- */
    if( srcdefn->has_geoid_vgrids )
    {
        if( pj_apply_vgridshift( srcdefn, "sgeoidgrids", 
                                 &(srcdefn->vgridlist_geoid), 
                                 &(srcdefn->vgridlist_geoid_count),
                                 0, point_count, point_offset, x, y, z ) != 0 )
            return pj_ctx_get_errno(srcdefn->ctx);
    }
        
/* -------------------------------------------------------------------- */
/*      Convert datums if needed, and possible.                         */
/* -------------------------------------------------------------------- */
    if( pj_datum_transform( srcdefn, dstdefn, point_count, point_offset, 
                            x, y, z ) != 0 )
    {
        if( srcdefn->ctx->last_errno != 0 )
            return srcdefn->ctx->last_errno;
        else
            return dstdefn->ctx->last_errno;
    }

/* -------------------------------------------------------------------- */
/*      Do we need to translate from geoid to ellipsoidal vertical      */
/*      datum?                                                          */
/* -------------------------------------------------------------------- */
    if( dstdefn->has_geoid_vgrids )
    {
        if( pj_apply_vgridshift( dstdefn, "sgeoidgrids", 
                                 &(dstdefn->vgridlist_geoid), 
                                 &(dstdefn->vgridlist_geoid_count),
                                 1, point_count, point_offset, x, y, z ) != 0 )
            return dstdefn->ctx->last_errno;
    }
        
/* -------------------------------------------------------------------- */
/*      But if they are staying lat long, adjust for the prime          */
/*      meridian if there is one in effect.                             */
/* -------------------------------------------------------------------- */
    if( dstdefn->from_greenwich != 0.0 )
    {
        for( i = 0; i < point_count; i++ )
        {
            if( x[point_offset*i] != HUGE_VAL )
                x[point_offset*i] -= dstdefn->from_greenwich;
        }
    }


/* -------------------------------------------------------------------- */
/*      Transform destination latlong to geocentric if required.        */
/* -------------------------------------------------------------------- */
    if( dstdefn->is_geocent )
    {
        if( z == NULL )
        {
            pj_ctx_set_errno( dstdefn->ctx, PJD_ERR_GEOCENTRIC );
            return PJD_ERR_GEOCENTRIC;
        }

        pj_geodetic_to_geocentric( dstdefn->a_orig, dstdefn->es_orig,
                                   point_count, point_offset, x, y, z );

        if( dstdefn->fr_meter != 1.0 )
        {
            for( i = 0; i < point_count; i++ )
            {
                if( x[point_offset*i] != HUGE_VAL )
                {
                    x[point_offset*i] *= dstdefn->fr_meter;
                    y[point_offset*i] *= dstdefn->fr_meter;
                }
            }
        }
    }

/* -------------------------------------------------------------------- */
/*      Transform destination points to projection coordinates, if      */
/*      desired.                                                        */
/* -------------------------------------------------------------------- */
    else if( !dstdefn->is_latlong )
    {
        for( i = 0; i < point_count; i++ )
        {
            XY         projected_loc;
            LP	       geodetic_loc;

            geodetic_loc.u = x[point_offset*i];
            geodetic_loc.v = y[point_offset*i];

            if( geodetic_loc.u == HUGE_VAL )
                continue;

            projected_loc = pj_fwd( geodetic_loc, dstdefn );
            if( dstdefn->ctx->last_errno != 0 )
            {
                if( (dstdefn->ctx->last_errno != 33 /*EDOM*/ 
                     && dstdefn->ctx->last_errno != 34 /*ERANGE*/ )
                    && (dstdefn->ctx->last_errno > 0 
                        || dstdefn->ctx->last_errno < -44 || point_count == 1
                        || transient_error[-dstdefn->ctx->last_errno] == 0 ) )
                    return dstdefn->ctx->last_errno;
                else
                {
                    projected_loc.u = HUGE_VAL;
                    projected_loc.v = HUGE_VAL;
                }
            }

            x[point_offset*i] = projected_loc.u;
            y[point_offset*i] = projected_loc.v;
        }
    }

/* -------------------------------------------------------------------- */
/*      If a wrapping center other than 0 is provided, rewrap around    */
/*      the suggested center (for latlong coordinate systems only).     */
/* -------------------------------------------------------------------- */
    else if( dstdefn->is_latlong && dstdefn->is_long_wrap_set )
    {
        for( i = 0; i < point_count; i++ )
        {
            if( x[point_offset*i] == HUGE_VAL )
                continue;

            while( x[point_offset*i] < dstdefn->long_wrap_center - PI )
                x[point_offset*i] += TWOPI;
            while( x[point_offset*i] > dstdefn->long_wrap_center + PI )
                x[point_offset*i] -= TWOPI;
        }
    }

/* -------------------------------------------------------------------- */
/*      Transform Z from meters if needed.                              */
/* -------------------------------------------------------------------- */
    if( dstdefn->vto_meter != 1.0 && z != NULL )
    {
        for( i = 0; i < point_count; i++ )
            z[point_offset*i] *= dstdefn->vfr_meter;
    }

/* -------------------------------------------------------------------- */
/*      Transform normalized axes into unusual output coordinate axis   */
/*      orientation if needed.                                          */
/* -------------------------------------------------------------------- */
    if( strcmp(dstdefn->axis,"enu") != 0 )
    {
        int err;

        err = pj_adjust_axis( dstdefn->ctx, dstdefn->axis, 
                              1, point_count, point_offset, x, y, z );
        if( err != 0 )
            return err;
    }

    return 0;
}
Beispiel #6
0
static double read_vgrid_value( PJ *defn, LP input, int *gridlist_count_p, PJ_GRIDINFO **tables, struct CTABLE *ct) {
    int  itable = 0;
    double value = HUGE_VAL;
    double grid_x, grid_y;
    long   grid_ix, grid_iy;
    long   grid_ix2, grid_iy2;
    float  *cvs;
    /* do not deal with NaN coordinates */
    /* cppcheck-suppress duplicateExpression */
    if( isnan(input.phi) || isnan(input.lam) )
        itable = *gridlist_count_p;

    /* keep trying till we find a table that works */
    for ( ; itable < *gridlist_count_p; itable++ )
    {
        PJ_GRIDINFO *gi = tables[itable];

        ct = gi->ct;

        /* skip tables that don't match our point at all.  */
        if( ct->ll.phi > input.phi || ct->ll.lam > input.lam
            || ct->ll.phi + (ct->lim.phi-1) * ct->del.phi < input.phi
            || ct->ll.lam + (ct->lim.lam-1) * ct->del.lam < input.lam )
            continue;

        /* If we have child nodes, check to see if any of them apply. */
        while( gi->child != NULL )
        {
            PJ_GRIDINFO *child;

            for( child = gi->child; child != NULL; child = child->next )
            {
                struct CTABLE *ct1 = child->ct;

                if( ct1->ll.phi > input.phi || ct1->ll.lam > input.lam
                  || ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi < input.phi
                  || ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam < input.lam)
                    continue;

                break;
            }

            /* we didn't find a more refined child node to use, so go with current grid */
            if( child == NULL )
            {
                break;
            }

            /* Otherwise let's try for childrens children .. */
            gi = child;
            ct = child->ct;
        }

        /* load the grid shift info if we don't have it. */
        if( ct->cvs == NULL && !pj_gridinfo_load( pj_get_ctx(defn), gi ) )
        {
            pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID );
            return PJD_ERR_FAILED_TO_LOAD_GRID;
        }


        /* Interpolation a location within the grid */
        grid_x = (input.lam - ct->ll.lam) / ct->del.lam;
        grid_y = (input.phi - ct->ll.phi) / ct->del.phi;
        grid_ix = lround(floor(grid_x));
        grid_iy = lround(floor(grid_y));
        grid_x -= grid_ix;
        grid_y -= grid_iy;

        grid_ix2 = grid_ix + 1;
        if( grid_ix2 >= ct->lim.lam )
            grid_ix2 = ct->lim.lam - 1;
        grid_iy2 = grid_iy + 1;
        if( grid_iy2 >= ct->lim.phi )
            grid_iy2 = ct->lim.phi - 1;

        cvs = (float *) ct->cvs;
        {
            float value_a = cvs[grid_ix + grid_iy * ct->lim.lam];
            float value_b = cvs[grid_ix2 + grid_iy * ct->lim.lam];
            float value_c = cvs[grid_ix + grid_iy2 * ct->lim.lam];
            float value_d = cvs[grid_ix2 + grid_iy2 * ct->lim.lam];
            double total_weight = 0.0;
            int n_weights = 0;
            value = 0.0f;
            if( !is_nodata(value_a) )
            {
                double weight = (1.0-grid_x) * (1.0-grid_y);
                value += value_a * weight;
                total_weight += weight;
                n_weights ++;
            }
            if( !is_nodata(value_b) )
            {
                double weight = (grid_x) * (1.0-grid_y);
                value += value_b * weight;
                total_weight += weight;
                n_weights ++;
            }
            if( !is_nodata(value_c) )
            {
                double weight = (1.0-grid_x) * (grid_y);
                value += value_c * weight;
                total_weight += weight;
                n_weights ++;
            }
            if( !is_nodata(value_d) )
            {
                double weight = (grid_x) * (grid_y);
                value += value_d * weight;
                total_weight += weight;
                n_weights ++;
            }
            if( n_weights == 0 )
                value = HUGE_VAL;
            else if( n_weights != 4 )
                value /= total_weight;
        }

    }

    return value;
}
Beispiel #7
0
int pj_apply_vgridshift( PJ *defn, const char *listname,
                         PJ_GRIDINFO ***gridlist_p,
                         int *gridlist_count_p,
                         int inverse,
                         long point_count, int point_offset,
                         double *x, double *y, double *z )

{
    int  i;
    static int debug_count = 0;
    PJ_GRIDINFO **tables;
    struct CTABLE ct;

    if( *gridlist_p == NULL )
    {
        *gridlist_p =
            pj_gridlist_from_nadgrids( pj_get_ctx(defn),
                                       pj_param(defn->ctx,defn->params,listname).s,
                                       gridlist_count_p );

        if( *gridlist_p == NULL || *gridlist_count_p == 0 )
            return defn->ctx->last_errno;
    }

    if( *gridlist_count_p == 0 )
    {
        pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
        return PJD_ERR_FAILED_TO_LOAD_GRID;
    }

    tables = *gridlist_p;
    defn->ctx->last_errno = 0;

    for( i = 0; i < point_count; i++ )
    {
        double value;
        long io = i * point_offset;
        LP   input;

        input.phi = y[io];
        input.lam = x[io];

        value = read_vgrid_value(defn, input, gridlist_count_p, tables, &ct);

        if( inverse )
            z[io] -= value;
        else
            z[io] += value;
        if( value != HUGE_VAL )
        {
            if( debug_count++ < 20 ) {
                proj_log_trace(defn, "pj_apply_gridshift(): used %s", ct.id);
                break;
            }
        }

        if( value == HUGE_VAL )
        {
            int itable;
            char gridlist[3000];

            proj_log_debug(defn,
                "pj_apply_vgridshift(): failed to find a grid shift table for\n"
                "                       location (%.7fdW,%.7fdN)",
                x[io] * RAD_TO_DEG,
                y[io] * RAD_TO_DEG );

            gridlist[0] = '\0';
            for( itable = 0; itable < *gridlist_count_p; itable++ )
            {
                PJ_GRIDINFO *gi = tables[itable];
                if( strlen(gridlist) + strlen(gi->gridname) > sizeof(gridlist)-100 )
                {
                    strcat( gridlist, "..." );
                    break;
                }

                if( itable == 0 )
                    sprintf( gridlist, "   tried: %s", gi->gridname );
                else
                    sprintf( gridlist+strlen(gridlist), ",%s", gi->gridname );
            }

            proj_log_debug(defn, "%s", gridlist);
            pj_ctx_set_errno( defn->ctx, PJD_ERR_GRID_AREA );

            return PJD_ERR_GRID_AREA;
        }
    }

    return 0;
}
Beispiel #8
0
PJ *PROJECTION(ob_tran) {
    double phip;
    char *name;
    ARGS args;
    PJ *R; /* projection to rotate */

    struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
    if (nullptr==Q)
        return destructor(P, ENOMEM);

    P->opaque = Q;
    P->destructor = destructor;

    /* get name of projection to be translated */
    if (!(name = pj_param(P->ctx, P->params, "so_proj").s))
        return destructor(P, PJD_ERR_NO_ROTATION_PROJ);

    /* avoid endless recursion */
    if( strcmp(name, "ob_tran") == 0 )
        return destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);

    /* Create the target projection object to rotate */
    args = ob_tran_target_params (P->params);
    R = pj_init_ctx (pj_get_ctx(P), args.argc, args.argv);
    pj_dealloc (args.argv);

    if (nullptr==R)
        return destructor (P, PJD_ERR_UNKNOWN_PROJECTION_ID);
    Q->link = R;

    if (pj_param(P->ctx, P->params, "to_alpha").i) {
        double lamc, phic, alpha;

        lamc    = pj_param(P->ctx, P->params, "ro_lon_c").f;
        phic    = pj_param(P->ctx, P->params, "ro_lat_c").f;
        alpha   = pj_param(P->ctx, P->params, "ro_alpha").f;

        if (fabs(fabs(phic) - M_HALFPI) <= TOL)
            return destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90);

        Q->lamp = lamc + aatan2(-cos(alpha), -sin(alpha) * sin(phic));
        phip = aasin(P->ctx,cos(phic) * sin(alpha));
    } else if (pj_param(P->ctx, P->params, "to_lat_p").i) { /* specified new pole */
        Q->lamp = pj_param(P->ctx, P->params, "ro_lon_p").f;
        phip = pj_param(P->ctx, P->params, "ro_lat_p").f;
    } else { /* specified new "equator" points */
        double lam1, lam2, phi1, phi2, con;

        lam1 = pj_param(P->ctx, P->params, "ro_lon_1").f;
        phi1 = pj_param(P->ctx, P->params, "ro_lat_1").f;
        lam2 = pj_param(P->ctx, P->params, "ro_lon_2").f;
        phi2 = pj_param(P->ctx, P->params, "ro_lat_2").f;
        if (fabs(phi1 - phi2) <= TOL || (con = fabs(phi1)) <= TOL ||
            fabs(con - M_HALFPI) <= TOL || fabs(fabs(phi2) - M_HALFPI) <= TOL)
                return destructor(P, PJD_ERR_LAT_1_OR_2_ZERO_OR_90);

        Q->lamp = atan2(cos(phi1) * sin(phi2) * cos(lam1) -
            sin(phi1) * cos(phi2) * cos(lam2),
            sin(phi1) * cos(phi2) * sin(lam2) -
            cos(phi1) * sin(phi2) * sin(lam1));
        phip = atan(-cos(Q->lamp - lam1) / tan(phi1));
    }

    if (fabs(phip) > TOL) { /* oblique */
        Q->cphip = cos(phip);
        Q->sphip = sin(phip);
        P->fwd = Q->link->fwd ? o_forward : nullptr;
        P->inv = Q->link->inv ? o_inverse : nullptr;
    } else { /* transverse */
        P->fwd = Q->link->fwd ? t_forward : nullptr;
        P->inv = Q->link->inv ? t_inverse : nullptr;
    }

    /* Support some rather speculative test cases, where the rotated projection */
    /* is actually latlong. We do not want scaling in that case... */
    if (Q->link->right==PJ_IO_UNITS_RADIANS)
        P->right = PJ_IO_UNITS_PROJECTED;


    return P;
}
int pj_apply_vgridshift(PJ *defn, const char *listname, PJ_GRIDINFO ***gridlist_p, int *gridlist_count_p, int inverse,
                        long point_count, int point_offset, double *x, double *y, double *z)

{
	int i;
	static int debug_count = 0;
	PJ_GRIDINFO **tables;

	if (*gridlist_p == NULL) {
		*gridlist_p =
		    pj_gridlist_from_nadgrids(pj_get_ctx(defn), pj_param(defn->ctx, defn->params, listname).s, gridlist_count_p);

		if (*gridlist_p == NULL || *gridlist_count_p == 0)
			return defn->ctx->last_errno;
	}

	if (*gridlist_count_p == 0) {
		pj_ctx_set_errno(defn->ctx, -38);
		return -38;
	}

	tables = *gridlist_p;
	defn->ctx->last_errno = 0;

	for (i = 0; i < point_count; i++) {
		long io = i * point_offset;
		LP input;
		int itable;
		double value = HUGE_VAL;

		input.phi = y[io];
		input.lam = x[io];

		/* keep trying till we find a table that works */
		for (itable = 0; itable < *gridlist_count_p; itable++) {
			PJ_GRIDINFO *gi = tables[itable];
			struct CTABLE *ct = gi->ct;
			double grid_x, grid_y;
			int grid_ix, grid_iy;
			float *cvs;

			/* skip tables that don't match our point at all.  */
			if (ct->ll.phi > input.phi || ct->ll.lam > input.lam || ct->ll.phi + (ct->lim.phi - 1) * ct->del.phi < input.phi ||
			    ct->ll.lam + (ct->lim.lam - 1) * ct->del.lam < input.lam)
				continue;

			/* If we have child nodes, check to see if any of them apply. */
			if (gi->child != NULL) {
				PJ_GRIDINFO *child;

				for (child = gi->child; child != NULL; child = child->next) {
					struct CTABLE *ct1 = child->ct;

					if (ct1->ll.phi > input.phi || ct1->ll.lam > input.lam ||
					    ct1->ll.phi + (ct1->lim.phi - 1) * ct1->del.phi < input.phi ||
					    ct1->ll.lam + (ct1->lim.lam - 1) * ct1->del.lam < input.lam)
						continue;

					break;
				}

				/* we found a more refined child node to use */
				if (child != NULL) {
					gi = child;
					ct = child->ct;
				}
			}

			/* load the grid shift info if we don't have it. */
			if (ct->cvs == NULL && !pj_gridinfo_load(pj_get_ctx(defn), gi)) {
				pj_ctx_set_errno(defn->ctx, -38);
				return -38;
			}

			/* Interpolation a location within the grid */
			grid_x = (input.lam - ct->ll.lam) / ct->del.lam;
			grid_y = (input.phi - ct->ll.phi) / ct->del.phi;
			grid_ix = (int)floor(grid_x);
			grid_iy = (int)floor(grid_y);
			grid_x -= grid_ix;
			grid_y -= grid_iy;

			cvs = (float *)ct->cvs;
			value = cvs[grid_ix + grid_iy * ct->lim.lam] * (1.0 - grid_x) * (1.0 - grid_y) +
			        cvs[grid_ix + 1 + grid_iy * ct->lim.lam] * (grid_x) * (1.0 - grid_y) +
			        cvs[grid_ix + (grid_iy + 1) * ct->lim.lam] * (1.0 - grid_x) * (grid_y) +
			        cvs[grid_ix + 1 + (grid_iy + 1) * ct->lim.lam] * (grid_x) * (grid_y);

			if (value > 1000 || value < -1000) /* nodata? */
				value = HUGE_VAL;
			else {
				if (inverse)
					z[io] -= value;
				else
					z[io] += value;
			}

			if (value != HUGE_VAL) {
				if (debug_count++ < 20)
					pj_log(defn->ctx, PJ_LOG_DEBUG_MINOR, "pj_apply_gridshift(): used %s", ct->id);
				break;
			}
		}

		if (value == HUGE_VAL) {
			char gridlist[3000];

			pj_log(defn->ctx, PJ_LOG_DEBUG_MAJOR,
			       "pj_apply_vgridshift(): failed to find a grid shift table for\n"
			       "                       location (%.7fdW,%.7fdN)",
			       x[io] * RAD_TO_DEG, y[io] * RAD_TO_DEG);

			gridlist[0] = '\0';
			for (itable = 0; itable < *gridlist_count_p; itable++) {
				PJ_GRIDINFO *gi = tables[itable];
				if (strlen(gridlist) + strlen(gi->gridname) > sizeof(gridlist) - 100) {
					strcat(gridlist, "...");
					break;
				}

				if (itable == 0)
					sprintf(gridlist, "   tried: %s", gi->gridname);
				else
					sprintf(gridlist + strlen(gridlist), ",%s", gi->gridname);
			}
			pj_log(defn->ctx, PJ_LOG_DEBUG_MAJOR, "%s", gridlist);

			pj_ctx_set_errno(defn->ctx, PJD_ERR_GRID_AREA);
			return PJD_ERR_GRID_AREA;
		}
	}

	return 0;
}