//********************************************************************** // pj_gridinfo_free() //********************************************************************** public static void pj_gridinfo_free(projCtx ctx, PJ_GRIDINFO gi) { if (gi == null) { return; } if (gi.child != null) { PJ_GRIDINFO child, next; for (child = gi.child; child != null; child = next) { next = child.next; pj_gridinfo_free(ctx, child); child = null; } } if (gi.ct != null) { nad_free(gi.ct); } gi.ct = null; }
//********************************************************************** // pj_deallocate_grids() // // Deallocate all loaded grids. //********************************************************************** public static void pj_deallocate_grids() { while (grid_list != null) { PJ_GRIDINFO item = grid_list; grid_list = grid_list.next; item.next = null; pj_gridinfo_free(Proj.pj_get_default_ctx(), item); item = null; } }
//********************************************************************** // pj_deallocate_grids() // // Deallocate all loaded grids. //********************************************************************** public static void pj_deallocate_grids() { while(grid_list!=null) { PJ_GRIDINFO item=grid_list; grid_list=grid_list.next; item.next=null; pj_gridinfo_free(Proj.pj_get_default_ctx(), item); item=null; } }
static bool IS_LSB = true; // (1 == ((unsigned char *) (&byte_order_test))[0]) #endregion Fields #region Methods //********************************************************************** // pj_gridinfo_free() //********************************************************************** public static void pj_gridinfo_free(projCtx ctx, PJ_GRIDINFO gi) { if(gi==null) return; if(gi.child!=null) { PJ_GRIDINFO child, next; for(child=gi.child; child!=null; child=next) { next=child.next; pj_gridinfo_free(ctx, child); child=null; } } if(gi.ct!=null) nad_free(gi.ct); gi.ct=null; }
//********************************************************************** // pj_gridinfo_parent() // // Seek a parent grid file by name from a grid list. //********************************************************************** static PJ_GRIDINFO pj_gridinfo_parent(PJ_GRIDINFO gilist, string name) { while (gilist != null) { if (gilist.ct.id == name) { return(gilist); } if (gilist.child != null) { PJ_GRIDINFO parent = pj_gridinfo_parent(gilist.child, name); if (parent != null) { return(parent); } } gilist = gilist.next; } return(gilist); }
//********************************************************************** // pj_gridinfo_init() // // Open and parse header details from a datum gridshift file // returning a list of PJ_GRIDINFOs for the grids in that // file. This superceeds use of nad_init() for modern // applications. //********************************************************************** public static PJ_GRIDINFO pj_gridinfo_init(projCtx ctx, string gridname) { Libc.errno = Proj.pj_errno = 0; ctx.last_errno = 0; // -------------------------------------------------------------------- // Initialize a GRIDINFO with stub info we would use if it // cannot be loaded. // -------------------------------------------------------------------- PJ_GRIDINFO gilist = new PJ_GRIDINFO(); gilist.gridname = gridname; gilist.filename = null; gilist.format = "missing"; gilist.grid_offset = 0; gilist.ct = null; gilist.next = null; // -------------------------------------------------------------------- // Open the file using the usual search rules. // -------------------------------------------------------------------- FileStream fp = Proj.pj_open_lib(ctx, gridname, FileAccess.Read); if (fp == null) { Proj.pj_errno = Libc.errno; ctx.last_errno = 0; // don't treat as a persistent error return(gilist); } gilist.filename = gridname; // -------------------------------------------------------------------- // Load a header, to determine the file type. // -------------------------------------------------------------------- byte[] header = new byte[160]; try { if (fp.Read(header, 0, header.Length) != header.Length) { fp.Close(); header = null; Proj.pj_ctx_set_errno(ctx, -38); return(gilist); } } catch { fp.Close(); header = null; Proj.pj_ctx_set_errno(ctx, -38); return(gilist); } fp.Seek(0, SeekOrigin.Begin); // -------------------------------------------------------------------- // Determine file type. // -------------------------------------------------------------------- if (Encoding.ASCII.GetString(header, 0, 6) == "HEADER" && Encoding.ASCII.GetString(header, 96, 6) == "W GRID" && Encoding.ASCII.GetString(header, 144, 16) == "TO NAD83 ") { pj_gridinfo_init_ntv1(ctx, fp, gilist); } else if (Encoding.ASCII.GetString(header, 0, 8) == "NUM_OREC" && Encoding.ASCII.GetString(header, 48, 7) == "GS_TYPE") { pj_gridinfo_init_ntv2(ctx, fp, gilist); } else if (gridname.Length > 4 && gridname.EndsWith("gtx", StringComparison.CurrentCultureIgnoreCase)) { pj_gridinfo_init_gtx(ctx, fp, gilist); } else if (Encoding.ASCII.GetString(header, 0, 9) == "CTABLE V2") { CTABLE ct = nad_ctable2_init(ctx, fp); gilist.format = "ctable2"; gilist.ct = ct; Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "Ctable2 {0} {1}x{2}: LL=({3},{4}) UR=({5},{6})", ct.id, ct.lim.lam, ct.lim.phi, ct.ll.lam * Proj.RAD_TO_DEG, ct.ll.phi * Proj.RAD_TO_DEG, (ct.ll.lam + (ct.lim.lam - 1) * ct.del.lam) * Proj.RAD_TO_DEG, (ct.ll.phi + (ct.lim.phi - 1) * ct.del.phi) * Proj.RAD_TO_DEG); } else { CTABLE ct = nad_ctable_init(ctx, fp); if (ct == null) { Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "CTABLE ct is NULL."); } else { gilist.format = "ctable"; gilist.ct = ct; Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "Ctable {0} {1}x{2}: LL=({3},{4}) UR=({5},{6})", ct.id, ct.lim.lam, ct.lim.phi, ct.ll.lam * Proj.RAD_TO_DEG, ct.ll.phi * Proj.RAD_TO_DEG, (ct.ll.lam + (ct.lim.lam - 1) * ct.del.lam) * Proj.RAD_TO_DEG, (ct.ll.phi + (ct.lim.phi - 1) * ct.del.phi) * Proj.RAD_TO_DEG); } } fp.Close(); return(gilist); }
//********************************************************************** // pj_gridinfo_init_gtx() // // Load a NOAA .gtx vertical datum shift file. //********************************************************************** static bool pj_gridinfo_init_gtx(projCtx ctx, FileStream fid, PJ_GRIDINFO gi) { byte[] header = new byte[40]; CTABLE ct; double xorigin, yorigin, xstep, ystep; int rows, columns; // -------------------------------------------------------------------- // Read the header. // -------------------------------------------------------------------- if (fid.Read(header, 0, 40) != 40) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } // -------------------------------------------------------------------- // Regularize fields of interest and extract. // -------------------------------------------------------------------- if (IS_LSB) { swap_words(header, 0, 8, 4); swap_words(header, 32, 4, 2); } yorigin = BitConverter.ToDouble(header, 0); xorigin = BitConverter.ToDouble(header, 8); ystep = BitConverter.ToDouble(header, 16); xstep = BitConverter.ToDouble(header, 24); rows = BitConverter.ToInt32(header, 32); columns = BitConverter.ToInt32(header, 36); if (xorigin < -360 || xorigin > 360 || yorigin < -90 || yorigin > 90) { Console.WriteLine("gtx file header has invalid extents, corrupt?"); Proj.pj_ctx_set_errno(ctx, -38); return(false); } // -------------------------------------------------------------------- // Fill in CTABLE structure. // -------------------------------------------------------------------- ct = new CTABLE(); ct.id = "GTX Vertical Grid Shift File"; ct.ll.lam = xorigin; ct.ll.phi = yorigin; ct.del.lam = xstep; ct.del.phi = ystep; ct.lim.lam = columns; ct.lim.phi = rows; // some GTX files come in 0-360 and we shift them back into the // expected -180 to 180 range if possible. This does not solve // problems with grids spanning the dateline. if (ct.ll.lam >= 180.0) { ct.ll.lam -= 360.0; } #if !DEBUG if (ct.ll.lam >= 0.0 && ct.ll.lam + ct.del.lam * ct.lim.lam > 180.0) { Console.Error.WriteLine("This GTX spans the dateline! This will cause problems."); } Console.Error.WriteLine("GTX {0}x{1}: LL=({2},{3}) UR=({4},{5})", ct.lim.lam, ct.lim.phi, ct.ll.lam, ct.ll.phi, ct.ll.lam + (columns - 1) * xstep, ct.ll.phi + (rows - 1) * ystep); #endif ct.ll.lam *= Proj.DEG_TO_RAD; ct.ll.phi *= Proj.DEG_TO_RAD; ct.del.lam *= Proj.DEG_TO_RAD; ct.del.phi *= Proj.DEG_TO_RAD; ct.cvs = null; gi.ct = ct; gi.grid_offset = 40; gi.format = "gtx"; return(true); }
//********************************************************************** // pj_gridinfo_parent() // // Seek a parent grid file by name from a grid list. //********************************************************************** static PJ_GRIDINFO pj_gridinfo_parent(PJ_GRIDINFO gilist, string name) { while(gilist!=null) { if(gilist.ct.id==name) return gilist; if(gilist.child!=null) { PJ_GRIDINFO parent=pj_gridinfo_parent(gilist.child, name); if(parent!=null) return parent; } gilist=gilist.next; } return gilist; }
//********************************************************************** // pj_gridinfo_init_gtx() // // Load a NOAA .gtx vertical datum shift file. //********************************************************************** static bool pj_gridinfo_init_gtx(projCtx ctx, FileStream fid, PJ_GRIDINFO gi) { byte[] header=new byte[40]; CTABLE ct; double xorigin, yorigin, xstep, ystep; int rows, columns; // -------------------------------------------------------------------- // Read the header. // -------------------------------------------------------------------- if(fid.Read(header, 0, 40)!=40) { Proj.pj_ctx_set_errno(ctx, -38); return false; } // -------------------------------------------------------------------- // Regularize fields of interest and extract. // -------------------------------------------------------------------- if(IS_LSB) { swap_words(header, 0, 8, 4); swap_words(header, 32, 4, 2); } yorigin=BitConverter.ToDouble(header, 0); xorigin=BitConverter.ToDouble(header, 8); ystep=BitConverter.ToDouble(header, 16); xstep=BitConverter.ToDouble(header, 24); rows=BitConverter.ToInt32(header, 32); columns=BitConverter.ToInt32(header, 36); if(xorigin<-360||xorigin>360 ||yorigin<-90||yorigin>90) { Console.WriteLine("gtx file header has invalid extents, corrupt?"); Proj.pj_ctx_set_errno(ctx, -38); return false; } // -------------------------------------------------------------------- // Fill in CTABLE structure. // -------------------------------------------------------------------- ct=new CTABLE(); ct.id="GTX Vertical Grid Shift File"; ct.ll.lam=xorigin; ct.ll.phi=yorigin; ct.del.lam=xstep; ct.del.phi=ystep; ct.lim.lam=columns; ct.lim.phi=rows; // some GTX files come in 0-360 and we shift them back into the // expected -180 to 180 range if possible. This does not solve // problems with grids spanning the dateline. if(ct.ll.lam>=180.0) ct.ll.lam-=360.0; #if !DEBUG if(ct.ll.lam>=0.0&&ct.ll.lam+ct.del.lam*ct.lim.lam>180.0) Console.Error.WriteLine("This GTX spans the dateline! This will cause problems."); Console.Error.WriteLine("GTX {0}x{1}: LL=({2},{3}) UR=({4},{5})", ct.lim.lam, ct.lim.phi, ct.ll.lam, ct.ll.phi, ct.ll.lam+(columns-1)*xstep, ct.ll.phi+(rows-1)*ystep); #endif ct.ll.lam*=Proj.DEG_TO_RAD; ct.ll.phi*=Proj.DEG_TO_RAD; ct.del.lam*=Proj.DEG_TO_RAD; ct.del.phi*=Proj.DEG_TO_RAD; ct.cvs=null; gi.ct=ct; gi.grid_offset=40; gi.format="gtx"; return true; }
public static int pj_apply_vgridshift(PJ defn, string listname, ref PJ_GRIDINFO[] gridlist_p, ref int gridlist_count_p, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { if (gridlist_p == null) { gridlist_p = pj_gridlist_from_nadgrids(Proj.pj_get_ctx(defn), Proj.pj_param_s(defn.ctx, defn.parameters, listname), out gridlist_count_p); if (gridlist_p == null || gridlist_count_p == 0) { return(defn.ctx.last_errno); } } if (gridlist_p == null || gridlist_count_p == 0) { Proj.pj_ctx_set_errno(defn.ctx, -38); return(-38); } PJ_GRIDINFO[] tables = gridlist_p; defn.ctx.last_errno = 0; for (int i = 0; i < point_count; i++) { int io = i * point_offset; double value = Libc.HUGE_VAL; LP input; input.phi = y[io]; input.lam = x[io]; // keep trying till we find a table that works for (int itable = 0; itable < gridlist_count_p; itable++) { PJ_GRIDINFO gi = tables[itable]; CTABLE ct = gi.ct; double grid_x, grid_y; int grid_ix, grid_iy; // 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 = gi.child; for (; child != null; child = child.next) { 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; } // If we didn't find a child then nothing more to do, if (child == null) { break; } // otherwise use the child, first checking it's children. gi = child; ct = child.ct; } // load the grid shift info if we don't have it. if (ct.cvs == null && !pj_gridinfo_load(Proj.pj_get_ctx(defn), gi)) { Proj.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)Math.Floor(grid_x); grid_iy = (int)Math.Floor(grid_y); grid_x -= grid_ix; grid_y -= grid_iy; LP cvs1 = ct.cvs[(grid_ix + grid_iy * ct.lim.lam) / 2]; LP cvs2 = ct.cvs[(grid_ix + (grid_iy + 1) * ct.lim.lam) / 2]; value = cvs1.lam * (1.0 - grid_x) * (1.0 - grid_y) + cvs1.phi * grid_x * (1.0 - grid_y) + cvs2.lam * (1.0 - grid_x) * grid_y + cvs2.phi * grid_x * grid_y; if (Math.Abs(value + 88.8888) < 0.0001) { value = Libc.HUGE_VAL; // nodata? } else { if (inverse) { z[io] -= value; } else { z[io] += value; } } if (value != Libc.HUGE_VAL) { if (debug_count_apply_vgridshift++ < 20) { Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MINOR, "pj_apply_gridshift(): used {0}", ct.id); } break; } } if (value == Libc.HUGE_VAL) { Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, "pj_apply_vgridshift(): failed to find a grid shift table for\n\t\t\tlocation ({0}dW,{1}dN)", x[io] * Proj.RAD_TO_DEG, y[io] * Proj.RAD_TO_DEG); StringBuilder gridlist = new StringBuilder(); for (int itable = 0; itable < gridlist_count_p; itable++) { PJ_GRIDINFO gi = tables[itable]; if (itable == 0) { gridlist.AppendFormat(" tried: {0}", gi.gridname); } else { gridlist.AppendFormat(",{0}", gi.gridname); } } Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, "{0}", gridlist.ToString()); Proj.pj_ctx_set_errno(defn.ctx, (int)PJD_ERR.GRID_AREA); return((int)PJD_ERR.GRID_AREA); } } return(0); }
//********************************************************************** // pj_gridlist_merge_grid() // // Find/load the named gridfile and merge it into the // last_nadgrids_list. //********************************************************************** static bool pj_gridlist_merge_gridfile(projCtx ctx, string gridname, ref PJ_GRIDINFO[] p_gridlist, ref int p_gridcount, ref int p_gridmax) { bool got_match=false; PJ_GRIDINFO this_grid, tail=null; // -------------------------------------------------------------------- // Try to find in the existing list of loaded grids. Add all // matching grids as with NTv2 we can get many grids from one // file (one shared gridname). // -------------------------------------------------------------------- for(this_grid=grid_list; this_grid!=null; this_grid=this_grid.next) { if(this_grid.gridname==gridname) { got_match=true; // dont add to the list if it is invalid. if(this_grid.ct==null) return false; // do we need to grow the list? if(p_gridcount>=p_gridmax-2) { PJ_GRIDINFO[] new_list; int new_max=p_gridmax+20; new_list=new PJ_GRIDINFO[new_max]; if(p_gridlist!=null) { Array.Copy(p_gridlist, new_list, p_gridmax); p_gridlist=null; } p_gridlist=new_list; p_gridmax=new_max; } // add to the list p_gridlist[p_gridcount++]=this_grid; p_gridlist[p_gridcount]=null; } tail=this_grid; } if(got_match) return true; // -------------------------------------------------------------------- // Try to load the named grid. // -------------------------------------------------------------------- this_grid=pj_gridinfo_init(ctx, gridname); // we should get at least a stub grid with a missing "ct" member if(this_grid==null) return false; if(tail!=null) tail.next=this_grid; else grid_list=this_grid; // -------------------------------------------------------------------- // Recurse to add the grid now that it is loaded. // -------------------------------------------------------------------- return pj_gridlist_merge_gridfile(ctx, gridname, ref p_gridlist, ref p_gridcount, ref p_gridmax); }
public static int pj_apply_gridshift_3(projCtx ctx, PJ_GRIDINFO[] tables, int grid_count, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { if(tables==null||grid_count==0) { Proj.pj_ctx_set_errno(ctx, -38); return -38; } ctx.last_errno=0; for(int i=0; i<point_count; i++) { long io=i*point_offset; LP input, output; int itable; input.phi=y[io]; input.lam=x[io]; output.phi=Libc.HUGE_VAL; output.lam=Libc.HUGE_VAL; // keep trying till we find a table that works for(itable=0; itable<grid_count; itable++) { PJ_GRIDINFO gi=tables[itable]; CTABLE ct=gi.ct; double epsilon=(Math.Abs(ct.del.phi)+Math.Abs(ct.del.lam))/10000.0; // skip tables that don't match our point at all. if(ct.ll.phi-epsilon>input.phi|| ct.ll.lam-epsilon>input.lam|| ct.ll.phi+(ct.lim.phi-1)*ct.del.phi+epsilon<input.phi|| ct.ll.lam+(ct.lim.lam-1)*ct.del.lam+epsilon<input.lam) continue; // If we have child nodes, check to see if any of them apply. while(gi.child!=null) { PJ_GRIDINFO child=gi.child; for(; child!=null; child=child.next) { CTABLE ct1=child.ct; if(ct1.ll.phi-epsilon>input.phi|| ct1.ll.lam-epsilon>input.lam|| ct1.ll.phi+(ct1.lim.phi-1)*ct1.del.phi+epsilon<input.phi|| ct1.ll.lam+(ct1.lim.lam-1)*ct1.del.lam+epsilon<input.lam) continue; break; } // If we didn't find a child then nothing more to do, if(child==null) break; // otherwise use the child, first checking it's children. gi=child; ct=child.ct; } // load the grid shift info if we don't have it. if(ct.cvs==null&&!pj_gridinfo_load(ctx, gi)) { Proj.pj_ctx_set_errno(ctx, -38); return -38; } output=nad_cvt(input, inverse, ct); if(output.lam!=Libc.HUGE_VAL) { if(debug_count_apply_gridshift_3++<20) Proj.pj_log(ctx, PJ_LOG.DEBUG_MINOR, "pj_apply_gridshift(): used {0}", ct.id); break; } } if(output.lam==Libc.HUGE_VAL) { if(ctx.debug_level>=PJ_LOG.DEBUG_MAJOR) { Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "pj_apply_gridshift(): failed to find a grid shift table for\n\t\t\tlocation ({0}dW,{1}dN)", x[io]*Proj.RAD_TO_DEG, y[io]*Proj.RAD_TO_DEG); for(itable=0; itable<grid_count; itable++) { PJ_GRIDINFO gi=tables[itable]; if(itable==0) Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, " tried: {0}", gi.gridname); else Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, ",{0}", gi.gridname); } } // We don't actually have any machinery currently to set the // following macro, so this is mostly kept here to make it clear // how we ought to operate if we wanted to make it super clear // that an error has occured when points are outside our available // datum shift areas. But if this is on, we will find that "low // value" points on the fringes of some datasets will completely // fail causing lots of problems when it is more or less ok to // just not apply a datum shift. So rather than deal with // that we just fallback to no shift. (see also bug #45). #if ERR_GRID_AREA_TRANSIENT_SEVERE y[io]=Libc.HUGE_VAL; x[io]=Libc.HUGE_VAL; #else // leave x/y unshifted. #endif } else { y[io]=output.phi; x[io]=output.lam; } } return 0; }
static int debug_count_apply_gridshift_3 = 0; // TODO public static int pj_apply_gridshift_3(projCtx ctx, PJ_GRIDINFO[] tables, int grid_count, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { if (tables == null || grid_count == 0) { Proj.pj_ctx_set_errno(ctx, -38); return(-38); } ctx.last_errno = 0; for (int i = 0; i < point_count; i++) { long io = i * point_offset; LP input, output; int itable; input.phi = y[io]; input.lam = x[io]; output.phi = Libc.HUGE_VAL; output.lam = Libc.HUGE_VAL; // keep trying till we find a table that works for (itable = 0; itable < grid_count; itable++) { PJ_GRIDINFO gi = tables[itable]; CTABLE ct = gi.ct; double epsilon = (Math.Abs(ct.del.phi) + Math.Abs(ct.del.lam)) / 10000.0; // skip tables that don't match our point at all. if (ct.ll.phi - epsilon > input.phi || ct.ll.lam - epsilon > input.lam || ct.ll.phi + (ct.lim.phi - 1) * ct.del.phi + epsilon < input.phi || ct.ll.lam + (ct.lim.lam - 1) * ct.del.lam + epsilon < input.lam) { continue; } // If we have child nodes, check to see if any of them apply. while (gi.child != null) { PJ_GRIDINFO child = gi.child; for (; child != null; child = child.next) { CTABLE ct1 = child.ct; if (ct1.ll.phi - epsilon > input.phi || ct1.ll.lam - epsilon > input.lam || ct1.ll.phi + (ct1.lim.phi - 1) * ct1.del.phi + epsilon < input.phi || ct1.ll.lam + (ct1.lim.lam - 1) * ct1.del.lam + epsilon < input.lam) { continue; } break; } // If we didn't find a child then nothing more to do, if (child == null) { break; } // otherwise use the child, first checking it's children. gi = child; ct = child.ct; } // load the grid shift info if we don't have it. if (ct.cvs == null && !pj_gridinfo_load(ctx, gi)) { Proj.pj_ctx_set_errno(ctx, -38); return(-38); } output = nad_cvt(input, inverse, ct); if (output.lam != Libc.HUGE_VAL) { if (debug_count_apply_gridshift_3++ < 20) { Proj.pj_log(ctx, PJ_LOG.DEBUG_MINOR, "pj_apply_gridshift(): used {0}", ct.id); } break; } } if (output.lam == Libc.HUGE_VAL) { if (ctx.debug_level >= PJ_LOG.DEBUG_MAJOR) { Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "pj_apply_gridshift(): failed to find a grid shift table for\n\t\t\tlocation ({0}dW,{1}dN)", x[io] * Proj.RAD_TO_DEG, y[io] * Proj.RAD_TO_DEG); for (itable = 0; itable < grid_count; itable++) { PJ_GRIDINFO gi = tables[itable]; if (itable == 0) { Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, " tried: {0}", gi.gridname); } else { Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, ",{0}", gi.gridname); } } } // We don't actually have any machinery currently to set the // following macro, so this is mostly kept here to make it clear // how we ought to operate if we wanted to make it super clear // that an error has occured when points are outside our available // datum shift areas. But if this is on, we will find that "low // value" points on the fringes of some datasets will completely // fail causing lots of problems when it is more or less ok to // just not apply a datum shift. So rather than deal with // that we just fallback to no shift. (see also bug #45). #if ERR_GRID_AREA_TRANSIENT_SEVERE y[io] = Libc.HUGE_VAL; x[io] = Libc.HUGE_VAL; #else // leave x/y unshifted. #endif } else { y[io] = output.phi; x[io] = output.lam; } } return(0); }
//********************************************************************** // pj_gridlist_merge_grid() // // Find/load the named gridfile and merge it into the // last_nadgrids_list. //********************************************************************** static bool pj_gridlist_merge_gridfile(projCtx ctx, string gridname, ref PJ_GRIDINFO[] p_gridlist, ref int p_gridcount, ref int p_gridmax) { bool got_match = false; PJ_GRIDINFO this_grid, tail = null; // -------------------------------------------------------------------- // Try to find in the existing list of loaded grids. Add all // matching grids as with NTv2 we can get many grids from one // file (one shared gridname). // -------------------------------------------------------------------- for (this_grid = grid_list; this_grid != null; this_grid = this_grid.next) { if (this_grid.gridname == gridname) { got_match = true; // dont add to the list if it is invalid. if (this_grid.ct == null) { return(false); } // do we need to grow the list? if (p_gridcount >= p_gridmax - 2) { PJ_GRIDINFO[] new_list; int new_max = p_gridmax + 20; new_list = new PJ_GRIDINFO[new_max]; if (p_gridlist != null) { Array.Copy(p_gridlist, new_list, p_gridmax); p_gridlist = null; } p_gridlist = new_list; p_gridmax = new_max; } // add to the list p_gridlist[p_gridcount++] = this_grid; p_gridlist[p_gridcount] = null; } tail = this_grid; } if (got_match) { return(true); } // -------------------------------------------------------------------- // Try to load the named grid. // -------------------------------------------------------------------- this_grid = pj_gridinfo_init(ctx, gridname); // we should get at least a stub grid with a missing "ct" member if (this_grid == null) { return(false); } if (tail != null) { tail.next = this_grid; } else { grid_list = this_grid; } // -------------------------------------------------------------------- // Recurse to add the grid now that it is loaded. // -------------------------------------------------------------------- return(pj_gridlist_merge_gridfile(ctx, gridname, ref p_gridlist, ref p_gridcount, ref p_gridmax)); }
public static int pj_apply_vgridshift(PJ defn, string listname, ref PJ_GRIDINFO[] gridlist_p, ref int gridlist_count_p, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { if(gridlist_p==null) { gridlist_p=pj_gridlist_from_nadgrids(Proj.pj_get_ctx(defn), Proj.pj_param_s(defn.ctx, defn.parameters, listname), out gridlist_count_p); if(gridlist_p==null||gridlist_count_p==0) return defn.ctx.last_errno; } if(gridlist_p==null||gridlist_count_p==0) { Proj.pj_ctx_set_errno(defn.ctx, -38); return -38; } PJ_GRIDINFO[] tables=gridlist_p; defn.ctx.last_errno=0; for(int i=0; i<point_count; i++) { int io=i*point_offset; double value=Libc.HUGE_VAL; LP input; input.phi=y[io]; input.lam=x[io]; // keep trying till we find a table that works for(int itable=0; itable<gridlist_count_p; itable++) { PJ_GRIDINFO gi=tables[itable]; CTABLE ct=gi.ct; double grid_x, grid_y; int grid_ix, grid_iy; // 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=gi.child; for(; child!=null; child=child.next) { 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; } // If we didn't find a child then nothing more to do, if(child==null) break; // otherwise use the child, first checking it's children. gi=child; ct=child.ct; } // load the grid shift info if we don't have it. if(ct.cvs==null&&!pj_gridinfo_load(Proj.pj_get_ctx(defn), gi)) { Proj.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)Math.Floor(grid_x); grid_iy=(int)Math.Floor(grid_y); grid_x-=grid_ix; grid_y-=grid_iy; LP cvs1=ct.cvs[(grid_ix+grid_iy*ct.lim.lam)/2]; LP cvs2=ct.cvs[(grid_ix+(grid_iy+1)*ct.lim.lam)/2]; value=cvs1.lam*(1.0-grid_x)*(1.0-grid_y)+cvs1.phi*grid_x*(1.0-grid_y)+ cvs2.lam*(1.0-grid_x)*grid_y+cvs2.phi*grid_x*grid_y; if(Math.Abs(value+88.8888)<0.0001) value=Libc.HUGE_VAL; // nodata? else { if(inverse) z[io]-=value; else z[io]+=value; } if(value!=Libc.HUGE_VAL) { if(debug_count_apply_vgridshift++<20) Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MINOR, "pj_apply_gridshift(): used {0}", ct.id); break; } } if(value==Libc.HUGE_VAL) { Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, "pj_apply_vgridshift(): failed to find a grid shift table for\n\t\t\tlocation ({0}dW,{1}dN)", x[io]*Proj.RAD_TO_DEG, y[io]*Proj.RAD_TO_DEG); StringBuilder gridlist=new StringBuilder(); for(int itable=0; itable<gridlist_count_p; itable++) { PJ_GRIDINFO gi=tables[itable]; if(itable==0) gridlist.AppendFormat(" tried: {0}", gi.gridname); else gridlist.AppendFormat(",{0}", gi.gridname); } Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, "{0}", gridlist.ToString()); Proj.pj_ctx_set_errno(defn.ctx, (int)PJD_ERR.GRID_AREA); return (int)PJD_ERR.GRID_AREA; } } return 0; }
//********************************************************************** // pj_gridinfo_load() // // This function is intended to implement delayed loading of // the data contents of a grid file. The header and related // stuff are loaded by pj_gridinfo_init(). //********************************************************************** public static bool pj_gridinfo_load(projCtx ctx, PJ_GRIDINFO gi) { if(gi==null||gi.ct==null) return false; lock(gridlock) { if(gi.ct.cvs!=null) return true; CTABLE ct_tmp=gi.ct.Clone(); // -------------------------------------------------------------------- // Original platform specific CTable format. // -------------------------------------------------------------------- if(gi.format=="ctable") { FileStream fid=Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if(fid==null) { Proj.pj_ctx_set_errno(ctx, -38); return false; } bool result=nad_ctable_load(ctx, ct_tmp, fid); fid.Close(); gi.ct.cvs=ct_tmp.cvs; return result; } // -------------------------------------------------------------------- // CTable2 format. // -------------------------------------------------------------------- else if(gi.format=="ctable2") { FileStream fid=Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if(fid==null) { Proj.pj_ctx_set_errno(ctx, -38); return false; } bool result=nad_ctable2_load(ctx, ct_tmp, fid); fid.Close(); gi.ct.cvs=ct_tmp.cvs; return result; } // -------------------------------------------------------------------- // NTv1 format. // We process one line at a time. Note that the array storage // direction (e-w) is different in the NTv1 file and what // the CTABLE is supposed to have. The phi/lam are also // reversed, and we have to be aware of byte swapping. // -------------------------------------------------------------------- if(gi.format=="ntv1") { FileStream fid=Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if(fid==null) { Proj.pj_ctx_set_errno(ctx, -38); return false; } fid.Seek(gi.grid_offset, SeekOrigin.Begin); byte[] row_buf; try { row_buf=new byte[gi.ct.lim.lam*2*sizeof(double)]; ct_tmp.cvs=new LP[gi.ct.lim.lam*gi.ct.lim.phi]; } catch { row_buf=null; ct_tmp.cvs=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } for(int row=0; row<gi.ct.lim.phi; row++) { try { if(fid.Read(row_buf, 0, gi.ct.lim.lam*2*sizeof(double))!=gi.ct.lim.lam*2*sizeof(double)) { row_buf=null; ct_tmp.cvs=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } } catch { row_buf=null; ct_tmp.cvs=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } if(IS_LSB) swap_words(row_buf, 8, gi.ct.lim.lam*2); // convert seconds to radians int diff_seconds=0; for(int i=0; i<gi.ct.lim.lam; i++) { int cvs=row*gi.ct.lim.lam+(gi.ct.lim.lam-i-1); ct_tmp.cvs[cvs].phi=BitConverter.ToDouble(row_buf, (diff_seconds++)*sizeof(double))*((Proj.PI/180.0)/3600.0); ct_tmp.cvs[cvs].lam=BitConverter.ToDouble(row_buf, (diff_seconds++)*sizeof(double))*((Proj.PI/180.0)/3600.0); } } row_buf=null; fid.Close(); gi.ct.cvs=ct_tmp.cvs; return true; } // -------------------------------------------------------------------- // NTv2 format. // We process one line at a time. Note that the array storage // direction (e-w) is different in the NTv2 file and what // the CTABLE is supposed to have. The phi/lam are also // reversed, and we have to be aware of byte swapping. // -------------------------------------------------------------------- if(gi.format=="ntv2") { #if DEBUG Console.Error.WriteLine("NTv2 - loading grid "+gi.ct.id); #endif FileStream fid=Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if(fid==null) { Proj.pj_ctx_set_errno(ctx, -38); return false; } fid.Seek(gi.grid_offset, SeekOrigin.Begin); byte[] row_buf; try { row_buf=new byte[gi.ct.lim.lam*4*sizeof(float)]; ct_tmp.cvs=new LP[gi.ct.lim.lam*gi.ct.lim.phi]; } catch { row_buf=null; ct_tmp.cvs=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } for(int row=0; row<gi.ct.lim.phi; row++) { try { if(fid.Read(row_buf, 0, gi.ct.lim.lam*4*sizeof(float))!=gi.ct.lim.lam*4*sizeof(float)) { row_buf=null; ct_tmp.cvs=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } } catch { row_buf=null; ct_tmp.cvs=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } if(!IS_LSB) swap_words(row_buf, 4, gi.ct.lim.lam*4); // convert seconds to radians int diff_seconds=0; for(int i=0; i<gi.ct.lim.lam; i++) { int cvs=row*gi.ct.lim.lam+(gi.ct.lim.lam-i-1); ct_tmp.cvs[cvs].phi=BitConverter.ToSingle(row_buf, (diff_seconds++)*sizeof(float))*((Proj.PI/180.0)/3600.0); ct_tmp.cvs[cvs].lam=BitConverter.ToSingle(row_buf, (diff_seconds++)*sizeof(float))*((Proj.PI/180.0)/3600.0); diff_seconds+=2; // skip accuracy values } } row_buf=null; fid.Close(); gi.ct.cvs=ct_tmp.cvs; return true; } // -------------------------------------------------------------------- // GTX format. // -------------------------------------------------------------------- if(gi.format=="gtx") { int words=gi.ct.lim.lam*gi.ct.lim.phi; FileStream fid=Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if(fid==null) { Proj.pj_ctx_set_errno(ctx, -38); return false; } fid.Seek(gi.grid_offset, SeekOrigin.Begin); byte[] buf; try { buf=new byte[words*sizeof(float)]; ct_tmp.cvs=new LP[words/2]; } catch { Proj.pj_ctx_set_errno(ctx, -38); return false; } try { if(fid.Read(buf, 0, words*sizeof(float))!=words*sizeof(float)) { buf=null; ct_tmp.cvs=null; return false; } } catch { buf=null; ct_tmp.cvs=null; return false; } if(IS_LSB) swap_words(buf, 4, words); for(int i=0; i<words; i+=2) { ct_tmp.cvs[i/2].phi=BitConverter.ToSingle(buf, i*sizeof(float)); ct_tmp.cvs[i/2].lam=BitConverter.ToSingle(buf, (i+1)*sizeof(float)); } fid.Close(); gi.ct.cvs=ct_tmp.cvs; return true; } return false; } // lock(gridlock) }
//********************************************************************** // pj_gridinfo_load() // // This function is intended to implement delayed loading of // the data contents of a grid file. The header and related // stuff are loaded by pj_gridinfo_init(). //********************************************************************** public static bool pj_gridinfo_load(projCtx ctx, PJ_GRIDINFO gi) { if (gi == null || gi.ct == null) { return(false); } lock (gridlock) { if (gi.ct.cvs != null) { return(true); } CTABLE ct_tmp = gi.ct.Clone(); // -------------------------------------------------------------------- // Original platform specific CTable format. // -------------------------------------------------------------------- if (gi.format == "ctable") { FileStream fid = Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if (fid == null) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } bool result = nad_ctable_load(ctx, ct_tmp, fid); fid.Close(); gi.ct.cvs = ct_tmp.cvs; return(result); } // -------------------------------------------------------------------- // CTable2 format. // -------------------------------------------------------------------- else if (gi.format == "ctable2") { FileStream fid = Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if (fid == null) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } bool result = nad_ctable2_load(ctx, ct_tmp, fid); fid.Close(); gi.ct.cvs = ct_tmp.cvs; return(result); } // -------------------------------------------------------------------- // NTv1 format. // We process one line at a time. Note that the array storage // direction (e-w) is different in the NTv1 file and what // the CTABLE is supposed to have. The phi/lam are also // reversed, and we have to be aware of byte swapping. // -------------------------------------------------------------------- if (gi.format == "ntv1") { FileStream fid = Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if (fid == null) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } fid.Seek(gi.grid_offset, SeekOrigin.Begin); byte[] row_buf; try { row_buf = new byte[gi.ct.lim.lam * 2 * sizeof(double)]; ct_tmp.cvs = new LP[gi.ct.lim.lam * gi.ct.lim.phi]; } catch { row_buf = null; ct_tmp.cvs = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } for (int row = 0; row < gi.ct.lim.phi; row++) { try { if (fid.Read(row_buf, 0, gi.ct.lim.lam * 2 * sizeof(double)) != gi.ct.lim.lam * 2 * sizeof(double)) { row_buf = null; ct_tmp.cvs = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } } catch { row_buf = null; ct_tmp.cvs = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } if (IS_LSB) { swap_words(row_buf, 8, gi.ct.lim.lam * 2); } // convert seconds to radians int diff_seconds = 0; for (int i = 0; i < gi.ct.lim.lam; i++) { int cvs = row * gi.ct.lim.lam + (gi.ct.lim.lam - i - 1); ct_tmp.cvs[cvs].phi = BitConverter.ToDouble(row_buf, (diff_seconds++) * sizeof(double)) * ((Proj.PI / 180.0) / 3600.0); ct_tmp.cvs[cvs].lam = BitConverter.ToDouble(row_buf, (diff_seconds++) * sizeof(double)) * ((Proj.PI / 180.0) / 3600.0); } } row_buf = null; fid.Close(); gi.ct.cvs = ct_tmp.cvs; return(true); } // -------------------------------------------------------------------- // NTv2 format. // We process one line at a time. Note that the array storage // direction (e-w) is different in the NTv2 file and what // the CTABLE is supposed to have. The phi/lam are also // reversed, and we have to be aware of byte swapping. // -------------------------------------------------------------------- if (gi.format == "ntv2") { #if DEBUG Console.Error.WriteLine("NTv2 - loading grid " + gi.ct.id); #endif FileStream fid = Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if (fid == null) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } fid.Seek(gi.grid_offset, SeekOrigin.Begin); byte[] row_buf; try { row_buf = new byte[gi.ct.lim.lam * 4 * sizeof(float)]; ct_tmp.cvs = new LP[gi.ct.lim.lam * gi.ct.lim.phi]; } catch { row_buf = null; ct_tmp.cvs = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } for (int row = 0; row < gi.ct.lim.phi; row++) { try { if (fid.Read(row_buf, 0, gi.ct.lim.lam * 4 * sizeof(float)) != gi.ct.lim.lam * 4 * sizeof(float)) { row_buf = null; ct_tmp.cvs = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } } catch { row_buf = null; ct_tmp.cvs = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } if (!IS_LSB) { swap_words(row_buf, 4, gi.ct.lim.lam * 4); } // convert seconds to radians int diff_seconds = 0; for (int i = 0; i < gi.ct.lim.lam; i++) { int cvs = row * gi.ct.lim.lam + (gi.ct.lim.lam - i - 1); ct_tmp.cvs[cvs].phi = BitConverter.ToSingle(row_buf, (diff_seconds++) * sizeof(float)) * ((Proj.PI / 180.0) / 3600.0); ct_tmp.cvs[cvs].lam = BitConverter.ToSingle(row_buf, (diff_seconds++) * sizeof(float)) * ((Proj.PI / 180.0) / 3600.0); diff_seconds += 2; // skip accuracy values } } row_buf = null; fid.Close(); gi.ct.cvs = ct_tmp.cvs; return(true); } // -------------------------------------------------------------------- // GTX format. // -------------------------------------------------------------------- if (gi.format == "gtx") { int words = gi.ct.lim.lam * gi.ct.lim.phi; FileStream fid = Proj.pj_open_lib(ctx, gi.filename, FileAccess.Read); if (fid == null) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } fid.Seek(gi.grid_offset, SeekOrigin.Begin); byte[] buf; try { buf = new byte[words * sizeof(float)]; ct_tmp.cvs = new LP[words / 2]; } catch { Proj.pj_ctx_set_errno(ctx, -38); return(false); } try { if (fid.Read(buf, 0, words * sizeof(float)) != words * sizeof(float)) { buf = null; ct_tmp.cvs = null; return(false); } } catch { buf = null; ct_tmp.cvs = null; return(false); } if (IS_LSB) { swap_words(buf, 4, words); } for (int i = 0; i < words; i += 2) { ct_tmp.cvs[i / 2].phi = BitConverter.ToSingle(buf, i * sizeof(float)); ct_tmp.cvs[i / 2].lam = BitConverter.ToSingle(buf, (i + 1) * sizeof(float)); } fid.Close(); gi.ct.cvs = ct_tmp.cvs; return(true); } return(false); } // lock(gridlock) }
//********************************************************************** // pj_gridinfo_init_ntv1() // // Load an NTv1 style Canadian grid shift file. //********************************************************************** static bool pj_gridinfo_init_ntv1(projCtx ctx, FileStream fid, PJ_GRIDINFO gi) { byte[] header=new byte[176]; // -------------------------------------------------------------------- // Read the header. // -------------------------------------------------------------------- try { if(fid.Read(header, 0, header.Length)!=header.Length) { header=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } } catch { header=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } // -------------------------------------------------------------------- // Regularize fields of interest. // -------------------------------------------------------------------- if(IS_LSB) { swap_words(header, 8, 4, 1); swap_words(header, 24, 8, 1); swap_words(header, 40, 8, 1); swap_words(header, 56, 8, 1); swap_words(header, 72, 8, 1); swap_words(header, 88, 8, 1); swap_words(header, 104, 8, 1); } if(BitConverter.ToInt32(header, 8)!=12) { Console.Error.WriteLine("NTv1 grid shift file has wrong record count, corrupt?"); Proj.pj_ctx_set_errno(ctx, -38); return false; } // -------------------------------------------------------------------- // Fill in CTABLE structure. // -------------------------------------------------------------------- CTABLE ct=new CTABLE(); ct.id="NTv1 Grid Shift File"; ct.ll.lam=-BitConverter.ToDouble(header, 72); ct.ll.phi=BitConverter.ToDouble(header, 24); LP ur; ur.lam=-BitConverter.ToDouble(header, 56); ur.phi=BitConverter.ToDouble(header, 40); ct.del.lam=BitConverter.ToDouble(header, 104); ct.del.phi=BitConverter.ToDouble(header, 88); ct.lim.lam=(int)(Math.Abs(ur.lam-ct.ll.lam)/ct.del.lam+0.5)+1; ct.lim.phi=(int)(Math.Abs(ur.phi-ct.ll.phi)/ct.del.phi+0.5)+1; #if DEBUG Console.Error.WriteLine("NTv1 {0}x{1}: LL=({2},{3}) UR=({4},{5})", ct.lim.lam, ct.lim.phi, ct.ll.lam, ct.ll.phi, ur.lam, ur.phi); #endif ct.ll.lam*=Proj.DEG_TO_RAD; ct.ll.phi*=Proj.DEG_TO_RAD; ct.del.lam*=Proj.DEG_TO_RAD; ct.del.phi*=Proj.DEG_TO_RAD; ct.cvs=null; gi.ct=ct; gi.grid_offset=fid.Position; gi.format="ntv1"; return true; }
//********************************************************************** // pj_gridinfo_init_ntv2() // // Load a ntv2 (.gsb) file. //********************************************************************** static bool pj_gridinfo_init_ntv2(projCtx ctx, FileStream fid, PJ_GRIDINFO gilist) { byte[] header = new byte[11 * 16]; // -------------------------------------------------------------------- // Read the overview header. // -------------------------------------------------------------------- try { if (fid.Read(header, 0, header.Length) != header.Length) { header = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } } catch { header = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } // -------------------------------------------------------------------- // Byte swap interesting fields if needed. // -------------------------------------------------------------------- if (!IS_LSB) { swap_words(header, 8, 4, 1); swap_words(header, 8 + 16, 4, 1); swap_words(header, 8 + 32, 4, 1); swap_words(header, 8 + 7 * 16, 8, 1); swap_words(header, 8 + 8 * 16, 8, 1); swap_words(header, 8 + 9 * 16, 8, 1); swap_words(header, 8 + 10 * 16, 8, 1); } // -------------------------------------------------------------------- // Get the subfile count out ... all we really use for now. // -------------------------------------------------------------------- int num_subfiles = BitConverter.ToInt32(header, 8 + 32); // ==================================================================== // Step through the subfiles, creating a PJ_GRIDINFO for each. // ==================================================================== for (int subfile = 0; subfile < num_subfiles; subfile++) { // -------------------------------------------------------------------- // Read header. // -------------------------------------------------------------------- try { if (fid.Read(header, 0, header.Length) != header.Length) { header = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } } catch { header = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } if (Encoding.ASCII.GetString(header, 0, 8).StartsWith("SUB_NAME")) { Proj.pj_ctx_set_errno(ctx, -38); return(false); } // -------------------------------------------------------------------- // Byte swap interesting fields if needed. // -------------------------------------------------------------------- if (!IS_LSB) { swap_words(header, 8 + 16 * 4, 8, 1); swap_words(header, 8 + 16 * 5, 8, 1); swap_words(header, 8 + 16 * 6, 8, 1); swap_words(header, 8 + 16 * 7, 8, 1); swap_words(header, +8 + 16 * 8, 8, 1); swap_words(header, 8 + 16 * 9, 8, 1); swap_words(header, 8 + 16 * 10, 4, 1); } // -------------------------------------------------------------------- // Initialize a corresponding "ct" structure. // -------------------------------------------------------------------- CTABLE ct = new CTABLE(); ct.id = Encoding.ASCII.GetString(header, 8, 8); ct.ll.lam = -BitConverter.ToDouble(header, 7 * 16 + 8); // W_LONG ct.ll.phi = BitConverter.ToDouble(header, 4 * 16 + 8); // S_LAT LP ur; ur.lam = -BitConverter.ToDouble(header, 6 * 16 + 8); // E_LONG ur.phi = BitConverter.ToDouble(header, 5 * 16 + 8); // N_LAT ct.del.lam = BitConverter.ToDouble(header, 9 * 16 + 8); ct.del.phi = BitConverter.ToDouble(header, 8 * 16 + 8); ct.lim.lam = (int)(Math.Abs(ur.lam - ct.ll.lam) / ct.del.lam + 0.5) + 1; ct.lim.phi = (int)(Math.Abs(ur.phi - ct.ll.phi) / ct.del.phi + 0.5) + 1; #if DEBUG Console.Error.WriteLine("NTv2 {0} {1}x{2}: LL=({3},{4}) UR=({5},{6})", ct.id, ct.lim.lam, ct.lim.phi, ct.ll.lam / 3600.0, ct.ll.phi / 3600.0, ur.lam / 3600.0, ur.phi / 3600.0); #endif ct.ll.lam *= Proj.DEG_TO_RAD / 3600.0; ct.ll.phi *= Proj.DEG_TO_RAD / 3600.0; ct.del.lam *= Proj.DEG_TO_RAD / 3600.0; ct.del.phi *= Proj.DEG_TO_RAD / 3600.0; int gs_count = BitConverter.ToInt32(header, 8 + 16 * 10); if (gs_count != ct.lim.lam * ct.lim.phi) { Console.Error.WriteLine("GS_COUNT({0}) does not match expected cells ({1}x{2}={3})", gs_count, ct.lim.lam, ct.lim.phi, ct.lim.lam * ct.lim.phi); Proj.pj_ctx_set_errno(ctx, -38); return(false); } ct.cvs = null; PJ_GRIDINFO gi; // -------------------------------------------------------------------- // Create a new gridinfo for this if we aren't processing the // 1st subfile, and initialize our grid info. // -------------------------------------------------------------------- if (subfile == 0) { gi = gilist; } else { gi = new PJ_GRIDINFO(); gi.gridname = gilist.gridname; gi.filename = gilist.filename; gi.next = null; } gi.ct = ct; gi.format = "ntv2"; gi.grid_offset = fid.Position; // -------------------------------------------------------------------- // Attach to the correct list or sublist. // -------------------------------------------------------------------- if (Encoding.ASCII.GetString(header, 24, 4) == "NONE") { if (gi != gilist) { PJ_GRIDINFO lnk = gilist; while (lnk.next != null) { lnk = lnk.next; } lnk.next = gi; } } else { PJ_GRIDINFO gp = pj_gridinfo_parent(gilist, Encoding.ASCII.GetString(header, 24, 8)); if (gp == null) { #if DEBUG Console.Error.WriteLine("pj_gridinfo_init_ntv2(): failed to find parent {0} for {1}.", Encoding.ASCII.GetString(header, 24, 8), gi.ct.id); #endif PJ_GRIDINFO lnk = gilist; while (lnk.next != null) { lnk = lnk.next; } lnk.next = gi; } else if (gp.child == null) { gp.child = gi; } else { PJ_GRIDINFO lnk = gp.child; while (lnk.next != null) { lnk = lnk.next; } lnk.next = gi; } } // -------------------------------------------------------------------- // Seek past the data. // -------------------------------------------------------------------- fid.Seek(gs_count * 16, SeekOrigin.Current); } return(true); }
//********************************************************************** // pj_gridinfo_init_ntv2() // // Load a ntv2 (.gsb) file. //********************************************************************** static bool pj_gridinfo_init_ntv2(projCtx ctx, FileStream fid, PJ_GRIDINFO gilist) { byte[] header=new byte[11*16]; // -------------------------------------------------------------------- // Read the overview header. // -------------------------------------------------------------------- try { if(fid.Read(header, 0, header.Length)!=header.Length) { header=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } } catch { header=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } // -------------------------------------------------------------------- // Byte swap interesting fields if needed. // -------------------------------------------------------------------- if(!IS_LSB) { swap_words(header, 8, 4, 1); swap_words(header, 8+16, 4, 1); swap_words(header, 8+32, 4, 1); swap_words(header, 8+7*16, 8, 1); swap_words(header, 8+8*16, 8, 1); swap_words(header, 8+9*16, 8, 1); swap_words(header, 8+10*16, 8, 1); } // -------------------------------------------------------------------- // Get the subfile count out ... all we really use for now. // -------------------------------------------------------------------- int num_subfiles=BitConverter.ToInt32(header, 8+32); // ==================================================================== // Step through the subfiles, creating a PJ_GRIDINFO for each. // ==================================================================== for(int subfile=0; subfile<num_subfiles; subfile++) { // -------------------------------------------------------------------- // Read header. // -------------------------------------------------------------------- try { if(fid.Read(header, 0, header.Length)!=header.Length) { header=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } } catch { header=null; Proj.pj_ctx_set_errno(ctx, -38); return false; } if(Encoding.ASCII.GetString(header, 0, 8).StartsWith("SUB_NAME")) { Proj.pj_ctx_set_errno(ctx, -38); return false; } // -------------------------------------------------------------------- // Byte swap interesting fields if needed. // -------------------------------------------------------------------- if(!IS_LSB) { swap_words(header, 8+16*4, 8, 1); swap_words(header, 8+16*5, 8, 1); swap_words(header, 8+16*6, 8, 1); swap_words(header, 8+16*7, 8, 1); swap_words(header, +8+16*8, 8, 1); swap_words(header, 8+16*9, 8, 1); swap_words(header, 8+16*10, 4, 1); } // -------------------------------------------------------------------- // Initialize a corresponding "ct" structure. // -------------------------------------------------------------------- CTABLE ct=new CTABLE(); ct.id=Encoding.ASCII.GetString(header, 8, 8); ct.ll.lam=-BitConverter.ToDouble(header, 7*16+8); // W_LONG ct.ll.phi=BitConverter.ToDouble(header, 4*16+8); // S_LAT LP ur; ur.lam=-BitConverter.ToDouble(header, 6*16+8); // E_LONG ur.phi=BitConverter.ToDouble(header, 5*16+8); // N_LAT ct.del.lam=BitConverter.ToDouble(header, 9*16+8); ct.del.phi=BitConverter.ToDouble(header, 8*16+8); ct.lim.lam=(int)(Math.Abs(ur.lam-ct.ll.lam)/ct.del.lam+0.5)+1; ct.lim.phi=(int)(Math.Abs(ur.phi-ct.ll.phi)/ct.del.phi+0.5)+1; #if DEBUG Console.Error.WriteLine("NTv2 {0} {1}x{2}: LL=({3},{4}) UR=({5},{6})", ct.id, ct.lim.lam, ct.lim.phi, ct.ll.lam/3600.0, ct.ll.phi/3600.0, ur.lam/3600.0, ur.phi/3600.0); #endif ct.ll.lam*=Proj.DEG_TO_RAD/3600.0; ct.ll.phi*=Proj.DEG_TO_RAD/3600.0; ct.del.lam*=Proj.DEG_TO_RAD/3600.0; ct.del.phi*=Proj.DEG_TO_RAD/3600.0; int gs_count=BitConverter.ToInt32(header, 8+16*10); if(gs_count!=ct.lim.lam*ct.lim.phi) { Console.Error.WriteLine("GS_COUNT({0}) does not match expected cells ({1}x{2}={3})", gs_count, ct.lim.lam, ct.lim.phi, ct.lim.lam*ct.lim.phi); Proj.pj_ctx_set_errno(ctx, -38); return false; } ct.cvs=null; PJ_GRIDINFO gi; // -------------------------------------------------------------------- // Create a new gridinfo for this if we aren't processing the // 1st subfile, and initialize our grid info. // -------------------------------------------------------------------- if(subfile==0) gi=gilist; else { gi=new PJ_GRIDINFO(); gi.gridname=gilist.gridname; gi.filename=gilist.filename; gi.next=null; } gi.ct=ct; gi.format="ntv2"; gi.grid_offset=fid.Position; // -------------------------------------------------------------------- // Attach to the correct list or sublist. // -------------------------------------------------------------------- if(Encoding.ASCII.GetString(header, 24, 4)=="NONE") { if(gi!=gilist) { PJ_GRIDINFO lnk=gilist; while(lnk.next!=null) lnk=lnk.next; lnk.next=gi; } } else { PJ_GRIDINFO gp=pj_gridinfo_parent(gilist, Encoding.ASCII.GetString(header, 24, 8)); if(gp==null) { #if DEBUG Console.Error.WriteLine("pj_gridinfo_init_ntv2(): failed to find parent {0} for {1}.", Encoding.ASCII.GetString(header, 24, 8), gi.ct.id); #endif PJ_GRIDINFO lnk=gilist; while(lnk.next!=null) lnk=lnk.next; lnk.next=gi; } else if(gp.child==null) gp.child=gi; else { PJ_GRIDINFO lnk=gp.child; while(lnk.next!=null) lnk=lnk.next; lnk.next=gi; } } // -------------------------------------------------------------------- // Seek past the data. // -------------------------------------------------------------------- fid.Seek(gs_count*16, SeekOrigin.Current); } return true; }
//********************************************************************** // pj_gridinfo_init_ntv1() // // Load an NTv1 style Canadian grid shift file. //********************************************************************** static bool pj_gridinfo_init_ntv1(projCtx ctx, FileStream fid, PJ_GRIDINFO gi) { byte[] header = new byte[176]; // -------------------------------------------------------------------- // Read the header. // -------------------------------------------------------------------- try { if (fid.Read(header, 0, header.Length) != header.Length) { header = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } } catch { header = null; Proj.pj_ctx_set_errno(ctx, -38); return(false); } // -------------------------------------------------------------------- // Regularize fields of interest. // -------------------------------------------------------------------- if (IS_LSB) { swap_words(header, 8, 4, 1); swap_words(header, 24, 8, 1); swap_words(header, 40, 8, 1); swap_words(header, 56, 8, 1); swap_words(header, 72, 8, 1); swap_words(header, 88, 8, 1); swap_words(header, 104, 8, 1); } if (BitConverter.ToInt32(header, 8) != 12) { Console.Error.WriteLine("NTv1 grid shift file has wrong record count, corrupt?"); Proj.pj_ctx_set_errno(ctx, -38); return(false); } // -------------------------------------------------------------------- // Fill in CTABLE structure. // -------------------------------------------------------------------- CTABLE ct = new CTABLE(); ct.id = "NTv1 Grid Shift File"; ct.ll.lam = -BitConverter.ToDouble(header, 72); ct.ll.phi = BitConverter.ToDouble(header, 24); LP ur; ur.lam = -BitConverter.ToDouble(header, 56); ur.phi = BitConverter.ToDouble(header, 40); ct.del.lam = BitConverter.ToDouble(header, 104); ct.del.phi = BitConverter.ToDouble(header, 88); ct.lim.lam = (int)(Math.Abs(ur.lam - ct.ll.lam) / ct.del.lam + 0.5) + 1; ct.lim.phi = (int)(Math.Abs(ur.phi - ct.ll.phi) / ct.del.phi + 0.5) + 1; #if DEBUG Console.Error.WriteLine("NTv1 {0}x{1}: LL=({2},{3}) UR=({4},{5})", ct.lim.lam, ct.lim.phi, ct.ll.lam, ct.ll.phi, ur.lam, ur.phi); #endif ct.ll.lam *= Proj.DEG_TO_RAD; ct.ll.phi *= Proj.DEG_TO_RAD; ct.del.lam *= Proj.DEG_TO_RAD; ct.del.phi *= Proj.DEG_TO_RAD; ct.cvs = null; gi.ct = ct; gi.grid_offset = fid.Position; gi.format = "ntv1"; return(true); }
//********************************************************************** // pj_gridinfo_init() // // Open and parse header details from a datum gridshift file // returning a list of PJ_GRIDINFOs for the grids in that // file. This superceeds use of nad_init() for modern // applications. //********************************************************************** public static PJ_GRIDINFO pj_gridinfo_init(projCtx ctx, string gridname) { Libc.errno=Proj.pj_errno=0; ctx.last_errno=0; // -------------------------------------------------------------------- // Initialize a GRIDINFO with stub info we would use if it // cannot be loaded. // -------------------------------------------------------------------- PJ_GRIDINFO gilist=new PJ_GRIDINFO(); gilist.gridname=gridname; gilist.filename=null; gilist.format="missing"; gilist.grid_offset=0; gilist.ct=null; gilist.next=null; // -------------------------------------------------------------------- // Open the file using the usual search rules. // -------------------------------------------------------------------- FileStream fp=Proj.pj_open_lib(ctx, gridname, FileAccess.Read); if(fp==null) { Proj.pj_errno=Libc.errno; ctx.last_errno=0; // don't treat as a persistent error return gilist; } gilist.filename=gridname; // -------------------------------------------------------------------- // Load a header, to determine the file type. // -------------------------------------------------------------------- byte[] header=new byte[160]; try { if(fp.Read(header, 0, header.Length)!=header.Length) { fp.Close(); header=null; Proj.pj_ctx_set_errno(ctx, -38); return gilist; } } catch { fp.Close(); header=null; Proj.pj_ctx_set_errno(ctx, -38); return gilist; } fp.Seek(0, SeekOrigin.Begin); // -------------------------------------------------------------------- // Determine file type. // -------------------------------------------------------------------- if(Encoding.ASCII.GetString(header, 0, 6)=="HEADER"&& Encoding.ASCII.GetString(header, 96, 6)=="W GRID"&& Encoding.ASCII.GetString(header, 144, 16)=="TO NAD83 ") { pj_gridinfo_init_ntv1(ctx, fp, gilist); } else if(Encoding.ASCII.GetString(header, 0, 8)=="NUM_OREC"&& Encoding.ASCII.GetString(header, 48, 7)=="GS_TYPE") { pj_gridinfo_init_ntv2(ctx, fp, gilist); } else if(gridname.Length>4&&gridname.EndsWith("gtx", StringComparison.CurrentCultureIgnoreCase)) { pj_gridinfo_init_gtx(ctx, fp, gilist); } else if(Encoding.ASCII.GetString(header, 0, 9)=="CTABLE V2") { CTABLE ct=nad_ctable2_init(ctx, fp); gilist.format="ctable2"; gilist.ct=ct; Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "Ctable2 {0} {1}x{2}: LL=({3},{4}) UR=({5},{6})", ct.id, ct.lim.lam, ct.lim.phi, ct.ll.lam*Proj.RAD_TO_DEG, ct.ll.phi*Proj.RAD_TO_DEG, (ct.ll.lam+(ct.lim.lam-1)*ct.del.lam)*Proj.RAD_TO_DEG, (ct.ll.phi+(ct.lim.phi-1)*ct.del.phi)*Proj.RAD_TO_DEG); } else { CTABLE ct=nad_ctable_init(ctx, fp); if(ct==null) { Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "CTABLE ct is NULL."); } else { gilist.format="ctable"; gilist.ct=ct; Proj.pj_log(ctx, PJ_LOG.DEBUG_MAJOR, "Ctable {0} {1}x{2}: LL=({3},{4}) UR=({5},{6})", ct.id, ct.lim.lam, ct.lim.phi, ct.ll.lam*Proj.RAD_TO_DEG, ct.ll.phi*Proj.RAD_TO_DEG, (ct.ll.lam+(ct.lim.lam-1)*ct.del.lam)*Proj.RAD_TO_DEG, (ct.ll.phi+(ct.lim.phi-1)*ct.del.phi)*Proj.RAD_TO_DEG); } } fp.Close(); return gilist; }
//********************************************************************** // pj_gc_apply_gridshift() //********************************************************************** public static int pj_gc_apply_gridshift(PJ defn, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { int i; if (defn.catalog == null) { defn.catalog = pj_gc_findcatalog(defn.ctx, defn.catalog_name); if (defn.catalog == null) { return(defn.ctx.last_errno); } } defn.ctx.last_errno = 0; for (i = 0; i < point_count; i++) { long io = i * point_offset; LP input; input.phi = y[io]; input.lam = x[io]; // make sure we have appropriate "after" shift file available if (defn.last_after_grid == null || input.lam < defn.last_after_region.ll_long || input.lam > defn.last_after_region.ur_long || input.phi < defn.last_after_region.ll_lat || input.phi > defn.last_after_region.ll_lat) { defn.last_after_grid = pj_gc_findgrid(defn.ctx, defn.catalog, true, input, defn.datum_date, ref defn.last_after_region, ref defn.last_after_date); } PJ_GRIDINFO gi = defn.last_after_grid; Debug.Assert(gi.child == null); // load the grid shift info if we don't have it. if (gi.ct.cvs == null && !pj_gridinfo_load(defn.ctx, gi)) { Proj.pj_ctx_set_errno(defn.ctx, -38); return(-38); } LP output_after = nad_cvt(input, inverse, gi.ct); if (output_after.lam == Libc.HUGE_VAL) { if (defn.ctx.debug_level >= PJ_LOG.DEBUG_MAJOR) { Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, "pj_apply_gridshift(): failed to find a grid shift table for"); Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, " location ({0:F7}dW,{1:F7}dN)", x[io] * Proj.RAD_TO_DEG, y[io] * Proj.RAD_TO_DEG); } continue; } if (defn.datum_date == 0.0) { y[io] = output_after.phi; x[io] = output_after.lam; continue; } // make sure we have appropriate "before" shift file available if (defn.last_before_grid == null || input.lam < defn.last_before_region.ll_long || input.lam > defn.last_before_region.ur_long || input.phi < defn.last_before_region.ll_lat || input.phi > defn.last_before_region.ll_lat) { defn.last_before_grid = pj_gc_findgrid(defn.ctx, defn.catalog, false, input, defn.datum_date, ref defn.last_before_region, ref defn.last_before_date); } gi = defn.last_before_grid; Debug.Assert(gi.child == null); // load the grid shift info if we don't have it. if (gi.ct.cvs == null && !pj_gridinfo_load(defn.ctx, gi)) { Proj.pj_ctx_set_errno(defn.ctx, -38); return(-38); } LP output_before = nad_cvt(input, inverse, gi.ct); if (output_before.lam == Libc.HUGE_VAL) { if (defn.ctx.debug_level >= PJ_LOG.DEBUG_MAJOR) { Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, "pj_apply_gridshift(): failed to find a grid shift table for"); Proj.pj_log(defn.ctx, PJ_LOG.DEBUG_MAJOR, " location ({0:F7}dW,{1:F7}dN)", x[io] * Proj.RAD_TO_DEG, y[io] * Proj.RAD_TO_DEG); } continue; } double mix_ratio = (defn.datum_date - defn.last_before_date) / (defn.last_after_date - defn.last_before_date); y[io] = mix_ratio * output_after.phi + (1.0 - mix_ratio) * output_before.phi; x[io] = mix_ratio * output_after.lam + (1.0 - mix_ratio) * output_before.lam; } return(0); }