//********************************************************************** // nad_ctable_load() // // Load the data portion of a ctable formatted grid. //********************************************************************** public static bool nad_ctable_load(projCtx ctx, CTABLE ct, Stream fid) { try { fid.Seek(80 + 16 + 16 + 8 + 4, SeekOrigin.Begin); // 80+16+16+8+4 ?= sizeof(struct CTABLE) // read all the actual shift values int a_size = ct.lim.lam * ct.lim.phi; ct.cvs = new LP[a_size]; BinaryReader br = new BinaryReader(fid); for (int i = 0; i < a_size; i++) { ct.cvs[i].lam = br.ReadSingle(); ct.cvs[i].phi = br.ReadSingle(); } } catch { ct.cvs = null; #if DEBUG Console.Error.WriteLine("ctable loading failed on fread() - binary incompatible?"); #endif Proj.pj_ctx_set_errno(ctx, -38); return(false); } return(true); }
//********************************************************************** // nad_init() // // Read a datum shift file in any of the supported binary formats. //********************************************************************** public static CTABLE nad_init(projCtx ctx, string name) { ctx.last_errno = 0; // -------------------------------------------------------------------- // Open the file using the usual search rules. // -------------------------------------------------------------------- Stream fid = Proj.pj_open_lib(ctx, name, FileAccess.Read); if (fid == null) { return(null); } CTABLE ct = nad_ctable_init(ctx, fid); if (ct != null) { if (!nad_ctable_load(ctx, ct, fid)) { nad_free(ct); ct = null; } } fid.Close(); return(ct); }
//********************************************************************** // nad_free() // // Free a CTABLE grid shift structure produced by nad_init(). //********************************************************************** public static void nad_free(CTABLE ct) { if (ct != null) { ct.cvs = null; } }
//********************************************************************** // nad_ctable2_load() // // Load the data portion of a ctable2 formatted grid. //********************************************************************** public static bool nad_ctable2_load(projCtx ctx, CTABLE ct, Stream fid) { try { fid.Seek(160, SeekOrigin.Begin); // read all the actual shift values int a_size = ct.lim.lam * ct.lim.phi; ct.cvs = new LP[a_size]; BinaryReader br = new BinaryReader(fid); if (IS_LSB) { for (int i = 0; i < a_size; i++) { ct.cvs[i].lam = br.ReadSingle(); ct.cvs[i].phi = br.ReadSingle(); } } else { byte[] buf = br.ReadBytes(a_size * 8); swap_words(buf, 0, 4, a_size * 2); using (BinaryReader br2 = new BinaryReader(new MemoryStream(buf))) { for (int i = 0; i < a_size; i++) { ct.cvs[i].lam = br2.ReadSingle(); ct.cvs[i].phi = br2.ReadSingle(); } } } return(true); } catch { ct.cvs = null; #if DEBUG Proj.pj_log(ctx, PJ_LOG.ERROR, "ctable2 loading failed on fread() - binary incompatible?"); #endif Proj.pj_ctx_set_errno(ctx, -38); return(false); } }
//********************************************************************** // nad_ctable_init() // // Read the header portion of a "ctable" format grid. //********************************************************************** public static CTABLE nad_ctable_init(projCtx ctx, Stream fid) { try { CTABLE ct = new CTABLE(); byte[] tmpID = new byte[80]; // read the table header fid.Read(tmpID, 0, 80); BinaryReader br = new BinaryReader(fid); ct.ll.lam = br.ReadDouble(); ct.ll.phi = br.ReadDouble(); ct.del.lam = br.ReadDouble(); ct.del.phi = br.ReadDouble(); ct.lim.lam = br.ReadInt32(); ct.lim.phi = br.ReadInt32(); br.ReadInt32(); // FLP* cvs // do some minimal validation to ensure the structure isn't corrupt if (ct.lim.lam < 1 || ct.lim.lam > 100000 || ct.lim.phi < 1 || ct.lim.phi > 100000) { Proj.pj_ctx_set_errno(ctx, -38); return(null); } ct.id = Encoding.ASCII.GetString(tmpID); ct.id = ct.id.Trim(); // trim white space and newlines off id ct.cvs = null; return(ct); } catch { Proj.pj_ctx_set_errno(ctx, -38); return(null); } }
//********************************************************************** // nad_ctable2_init() // // Read the header portion of a "ctable2" format grid. //********************************************************************** public static CTABLE nad_ctable2_init(projCtx ctx, Stream fid) { try { CTABLE ct = new CTABLE(); byte[] header = new byte[160]; // read the table header fid.Read(header, 0, 160); if (!IS_LSB) { swap_words(header, 96, 8, 4); swap_words(header, 128, 4, 2); } MemoryStream mem = new MemoryStream(header); BinaryReader br = new BinaryReader(mem); byte[] signature = new byte[16]; byte[] tmpID = new byte[80]; signature = br.ReadBytes(16); string sig = Encoding.ASCII.GetString(signature); if (sig.Substring(0, 9) != "CTABLE V2") { Proj.pj_log(ctx, PJ_LOG.ERROR, "ctable2 - wrong header!"); Proj.pj_ctx_set_errno(ctx, -38); return(null); } // read the table header tmpID = br.ReadBytes(80); ct.ll.lam = br.ReadDouble(); ct.ll.phi = br.ReadDouble(); ct.del.lam = br.ReadDouble(); ct.del.phi = br.ReadDouble(); ct.lim.lam = br.ReadInt32(); ct.lim.phi = br.ReadInt32(); // do some minimal validation to ensure the structure isn't corrupt if (ct.lim.lam < 1 || ct.lim.lam > 100000 || ct.lim.phi < 1 || ct.lim.phi > 100000) { Proj.pj_ctx_set_errno(ctx, -38); return(null); } ct.id = Encoding.ASCII.GetString(tmpID); ct.id = ct.id.Trim(); // trim white space and newlines off id ct.cvs = null; return(ct); } catch { Proj.pj_ctx_set_errno(ctx, -38); return(null); } }
//********************************************************************** // 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_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_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_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) }
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); }
// Determine nad table correction value public static LP nad_intr(LP t, CTABLE ct) { LP val, frct; ILP indx; double m00, m10, m01, m11; int f00, f10, f01, f11; int index; int @in; t.lam /= ct.del.lam; t.phi /= ct.del.phi; indx.lam = (int)Math.Floor(t.lam); indx.phi = (int)Math.Floor(t.phi); frct.lam = t.lam - indx.lam; frct.phi = t.phi - indx.phi; val.lam = val.phi = Libc.HUGE_VAL; if (indx.lam < 0) { if (indx.lam == -1 && frct.lam > 0.99999999999) { indx.lam++; frct.lam = 0.0; } else { return(val); } } else { @in = indx.lam + 1; if (@in >= ct.lim.lam) { if (@in == ct.lim.lam && frct.lam < 1e-11) { indx.lam--; frct.lam = 1.0; } else { return(val); } } } if (indx.phi < 0) { if (indx.phi == -1 && frct.phi > 0.99999999999) { indx.phi++; frct.phi = 0.0; } else { return(val); } } else { @in = indx.phi + 1; if (@in >= ct.lim.phi) { if (@in == ct.lim.phi && frct.phi < 1e-11) { indx.phi--; frct.phi = 1.0; } else { return(val); } } } index = indx.phi * ct.lim.lam + indx.lam; f00 = index++; f10 = index; index += ct.lim.lam; f11 = index--; f01 = index; m11 = m10 = frct.lam; m00 = m01 = 1.0 - frct.lam; m11 *= frct.phi; m01 *= frct.phi; frct.phi = 1.0 - frct.phi; m00 *= frct.phi; m10 *= frct.phi; val.lam = m00 * ct.cvs[f00].lam + m10 * ct.cvs[f10].lam + m01 * ct.cvs[f01].lam + m11 * ct.cvs[f11].lam; val.phi = m00 * ct.cvs[f00].phi + m10 * ct.cvs[f10].phi + m01 * ct.cvs[f01].phi + m11 * ct.cvs[f11].phi; return(val); }
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); }
public static LP nad_cvt(LP @in, bool inverse, CTABLE ct) { const int MAX_TRY = 9; if (@in.lam == Libc.HUGE_VAL) { return(@in); } // normalize input to ll origin LP tb = @in; tb.lam -= ct.ll.lam; tb.phi -= ct.ll.phi; tb.lam = Proj.adjlon(tb.lam - Proj.PI) + Proj.PI; LP t = nad_intr(tb, ct); if (inverse) { LP del, dif; int i = MAX_TRY; if (t.lam == Libc.HUGE_VAL) { return(t); } t.lam = tb.lam + t.lam; t.phi = tb.phi - t.phi; do { del = nad_intr(t, ct); // This case used to return failure, but I have // changed it to return the first order approximation // of the inverse shift. This avoids cases where the // grid shift *into* this grid came from another grid. // While we aren't returning optimally correct results // I feel a close result in this case is better than // no result. NFW // To demonstrate use -112.5839956 49.4914451 against // the NTv2 grid shift file from Canada. if (del.lam == Libc.HUGE_VAL) { #if DEBUG Console.Error.WriteLine("Inverse grid shift iteration failed, presumably at grid edge.\nUsing first approximation."); #endif break; } t.lam -= dif.lam = t.lam - del.lam - tb.lam; t.phi -= dif.phi = t.phi + del.phi - tb.phi; } while((i--) != 0 && Math.Abs(dif.lam) > TOL12 && Math.Abs(dif.phi) > TOL12); if (i < 0) { #if DEBUG Console.Error.WriteLine("Inverse grid shift iterator failed to converge."); #endif t.lam = t.phi = Libc.HUGE_VAL; return(t); } @in.lam = Proj.adjlon(t.lam + ct.ll.lam); @in.phi = t.phi + ct.ll.phi; } else { if (t.lam == Libc.HUGE_VAL) { @in = t; } else { @in.lam -= t.lam; @in.phi += t.phi; } } return(@in); }