static string[] pj_gc_read_csv_line(projCtx ctx, StreamReader fid) { while (!fid.EndOfStream) { string line = fid.ReadLine(); if (line == null) { break; } line = line.TrimStart(); int earlyNULChar = line.IndexOf('\0'); if (earlyNULChar >= 0) { line = line.Substring(0, earlyNULChar); } // skip blank and comment lines if (line.Length == 0) { continue; } if (line[0] == '#') { continue; } return(line.Split(csv_line_splitter, StringSplitOptions.RemoveEmptyEntries)); } return(null); }
//********************************************************************** // 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; }
public static LP pj_inv_gauss(projCtx ctx, LP slp, GAUSS en) { const int MAX_ITER = 20; LP elp; elp.phi = 0; elp.lam = slp.lam / en.C; double num = Math.Pow(Math.Tan(0.5 * slp.phi + Proj.FORTPI) / en.K, 1.0 / en.C); int i = MAX_ITER; for (; i > 0; i--) { elp.phi = 2.0 * Math.Atan(num * srat(en.e * Math.Sin(slp.phi), -0.5 * en.e)) - Proj.HALFPI; if (Math.Abs(elp.phi - slp.phi) < TOL14) { break; } slp.phi = elp.phi; } // convergence failed if (i == 0) { Proj.pj_ctx_set_errno(ctx, -17); } return(elp); }
// distance and azimuth from point 1 to point 2 static VECT vect(projCtx ctx, double dphi, double c1, double s1, double c2, double s2, double dlam) { VECT v; double cdl = Math.Cos(dlam); if (Math.Abs(dphi) > 1.0 || Math.Abs(dlam) > 1.0) { v.r = Proj.aacos(ctx, s1 * s2 + c1 * c2 * cdl); } else { // more accurate for smaller distances double dp = Math.Sin(0.5 * dphi); double dl = Math.Sin(0.5 * dlam); v.r = 2.0 * Proj.aasin(ctx, Math.Sqrt(dp * dp + c1 * c2 * dl * dl)); } if (Math.Abs(v.r) > TOL9) { v.Az = Math.Atan2(c2 * Math.Sin(dlam), c1 * s2 - s1 * c2 * cdl); } else { v.r = v.Az = 0.0; } return(v); }
//********************************************************************** // 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_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); }
//*********************************************************************** // pj_gc_readentry() // // Read one catalog entry from the file. // // Format: // gridname,ll_long,ll_lat,ur_long,ur_lat,priority,date //*********************************************************************** static bool pj_gc_readentry(projCtx ctx, StreamReader fid, out PJ_GridCatalog.Entry entry) { entry = new PJ_GridCatalog.Entry(); string[] tokens = pj_gc_read_csv_line(ctx, fid); if (tokens == null || tokens.Length < 5) { if (tokens.Length != 0) { Proj.pj_log(ctx, PJ_LOG.ERROR, "Short line in grid catalog."); } return(true); } entry.definition = tokens[0]; entry.region.ll_long = Proj.dmstor_ctx(ctx, tokens[1]); entry.region.ll_lat = Proj.dmstor_ctx(ctx, tokens[2]); entry.region.ur_long = Proj.dmstor_ctx(ctx, tokens[3]); entry.region.ur_lat = Proj.dmstor_ctx(ctx, tokens[4]); if (tokens.Length > 5) { int.TryParse(tokens[5], out entry.priority); // defaults to zero } if (tokens.Length > 6) { entry.date = pj_gc_parsedate(ctx, tokens[6]); } return(false); }
public static LP pj_gauss(projCtx ctx, LP elp, GAUSS en) { LP slp; slp.phi=2.0*Math.Atan(en.K*Math.Pow(Math.Tan(0.5*elp.phi+Proj.FORTPI), en.C)*srat(en.e*Math.Sin(elp.phi), en.ratexp))-Proj.HALFPI; slp.lam=en.C*elp.lam; return slp; }
//*********************************************************************** //* pj_ctx_set_errno() * //* * //* Also sets the global errno. * //*********************************************************************** public static void pj_ctx_set_errno(projCtx ctx, int errno) { ctx.last_errno = errno; if (errno != 0) { pj_errno = errno; } }
static int debug_count_apply_gridshift_3 = 0; // TODO #endregion Fields #region Methods //********************************************************************** // pj_apply_gridshift() // // This is the externally callable interface - part of the // public API - though it is not used internally any more and I // doubt it is used by any other applications. But we preserve // it to honour our public api. //********************************************************************** public static int pj_apply_gridshift(projCtx ctx, string nadgrids, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { int grid_count=0; PJ_GRIDINFO[] gridlist=pj_gridlist_from_nadgrids(ctx, nadgrids, out grid_count); if(gridlist==null||grid_count==0) return ctx.last_errno; return pj_apply_gridshift_3(ctx, gridlist, grid_count, inverse, point_count, point_offset, x, y, z); }
public static LP pj_gauss(projCtx ctx, LP elp, GAUSS en) { LP slp; slp.phi = 2.0 * Math.Atan(en.K * Math.Pow(Math.Tan(0.5 * elp.phi + Proj.FORTPI), en.C) * srat(en.e * Math.Sin(elp.phi), en.ratexp)) - Proj.HALFPI; slp.lam = en.C * elp.lam; return(slp); }
//*********************************************************************** //* pj_ctx_alloc() * //*********************************************************************** public static projCtx pj_ctx_alloc() { projCtx ret=new projCtx(); projCtx ctx=pj_get_default_ctx(); ret.app_data=ctx.app_data; ret.debug_level=ctx.debug_level; ret.logger=ctx.logger; return ret; }
//*********************************************************************** //* pj_ctx_alloc() * //*********************************************************************** public static projCtx pj_ctx_alloc() { projCtx ret = new projCtx(); projCtx ctx = pj_get_default_ctx(); ret.app_data = ctx.app_data; ret.debug_level = ctx.debug_level; ret.logger = ctx.logger; return(ret); }
//********************************************************************** // pj_apply_gridshift() // // This is the externally callable interface - part of the // public API - though it is not used internally any more and I // doubt it is used by any other applications. But we preserve // it to honour our public api. //********************************************************************** public static int pj_apply_gridshift(projCtx ctx, string nadgrids, bool inverse, int point_count, int point_offset, double[] x, double[] y, double[] z) { int grid_count = 0; PJ_GRIDINFO[] gridlist = pj_gridlist_from_nadgrids(ctx, nadgrids, out grid_count); if (gridlist == null || grid_count == 0) { return(ctx.last_errno); } return(pj_apply_gridshift_3(ctx, gridlist, grid_count, inverse, point_count, point_offset, x, y, z)); }
public static PJ_GRIDINFO[] pj_gridlist_from_nadgrids(projCtx ctx, string nadgrids, out int grid_count) { PJ_GRIDINFO[] gridlist = null; int grid_max = 0; Proj.pj_errno = 0; grid_count = 0; lock (staticLockDummy) { // -------------------------------------------------------------------- // Loop processing names out of nadgrids one at a time. // -------------------------------------------------------------------- string s = nadgrids; for (int i = 0; i < s.Length;) { bool required = true; string name; if (s[i] == '@') { required = false; i++; } int end_char = i; while (end_char < s.Length && s[end_char] != ',') { end_char++; } name = s.Substring(i, end_char - i); i = end_char; if (i < s.Length && s[i] == ',') { i++; } if (!pj_gridlist_merge_gridfile(ctx, name, ref gridlist, ref grid_count, ref grid_max) && required) { Proj.pj_ctx_set_errno(ctx, -38); return(null); } Proj.pj_errno = 0; } return(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); } }
public static PJ_GRIDINFO[] pj_gridlist_from_nadgrids(projCtx ctx, string nadgrids, out int grid_count) { PJ_GRIDINFO[] gridlist=null; int grid_max=0; Proj.pj_errno=0; grid_count=0; lock(staticLockDummy) { // -------------------------------------------------------------------- // Loop processing names out of nadgrids one at a time. // -------------------------------------------------------------------- string s=nadgrids; for(int i=0; i<s.Length; ) { bool required=true; string name; if(s[i]=='@') { required=false; i++; } int end_char=i; while(end_char<s.Length&&s[end_char]!=',') end_char++; name=s.Substring(i, end_char-i); i=end_char; if(i<s.Length&&s[i]==',') i++; if(!pj_gridlist_merge_gridfile(ctx, name, ref gridlist, ref grid_count, ref grid_max)&&required) { Proj.pj_ctx_set_errno(ctx, -38); return null; } Proj.pj_errno=0; } return 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_c_findgrid() //********************************************************************** public static PJ_GRIDINFO pj_gc_findgrid(projCtx ctx, PJ_GridCatalog catalog, bool after, LP location, double date, ref PJ_Region optimal_region, ref double grid_date) { for (int i = 0; i < catalog.entries.Count; i++) { PJ_GridCatalog.Entry entry = catalog.entries[i]; if ((after && entry.date < date) || (!after && entry.date > date)) { continue; } if (location.lam < entry.region.ll_long || location.lam > entry.region.ur_long || location.phi < entry.region.ll_lat || location.phi > entry.region.ur_lat) { continue; } if (entry.available == -1) { continue; } grid_date = entry.date; if (entry.gridinfo == null) { int grid_count; PJ_GRIDINFO[] gridlist = pj_gridlist_from_nadgrids(ctx, entry.definition, out grid_count); if (grid_count == 1) { entry.gridinfo = gridlist[0]; } } return(entry.gridinfo); } grid_date = 0.0; optimal_region = new PJ_Region(); return(null); }
//*********************************************************************** // pj_gc_parsedate() // // Parse a date into a floating point year value. Acceptable // values are "yyyy.fraction" and "yyyy-mm-dd". Anything else // returns 0.0. //*********************************************************************** public static double pj_gc_parsedate(projCtx ctx, string date_string) // TODO => Proj. { try { if (date_string.Length == 10 && date_string[4] == '-' && date_string[7] == '-') { int year = int.Parse(date_string.Substring(0, 4)); int month = int.Parse(date_string.Substring(5, 2)); int day = int.Parse(date_string.Substring(8, 2)); // simplified calculation so we don't need to know all about months return(year + ((month - 1) * 31 + (day - 1)) / 372.0); } return(double.Parse(date_string)); // TDO neutral culture? } catch { return(0); } }
public static LP pj_inv_gauss(projCtx ctx, LP slp, GAUSS en) { const int MAX_ITER=20; LP elp; elp.phi=0; elp.lam=slp.lam/en.C; double num=Math.Pow(Math.Tan(0.5*slp.phi+Proj.FORTPI)/en.K, 1.0/en.C); int i=MAX_ITER; for(; i>0; i--) { elp.phi=2.0*Math.Atan(num*srat(en.e*Math.Sin(slp.phi), -0.5*en.e))-Proj.HALFPI; if(Math.Abs(elp.phi-slp.phi)<TOL14) break; slp.phi=elp.phi; } // convergence failed if(i==0) Proj.pj_ctx_set_errno(ctx, -17); return elp; }
//*********************************************************************** // pj_gc_readcatalog() // // Read a grid catalog from a .csv file. //*********************************************************************** public static PJ_GridCatalog pj_gc_readcatalog(projCtx ctx, string catalog_name) { FileStream fs = Proj.pj_open_lib(ctx, catalog_name, FileAccess.Read); if (fs == null) { return(null); } using (StreamReader fid = new StreamReader(fs)) { // discard title line fid.ReadLine(); PJ_GridCatalog catalog; try { catalog = new PJ_GridCatalog(); } catch { return(null); } catalog.catalog_name = catalog_name; catalog.entries = new List <PJ_GridCatalog.Entry>(); PJ_GridCatalog.Entry entry; while (!pj_gc_readentry(ctx, fid, out entry)) { catalog.entries.Add(entry); } fid.Close(); return(catalog); } }
//********************************************************************** // 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); } }
//********************************************************************** // pj_gc_findcatalog() //********************************************************************** public static PJ_GridCatalog pj_gc_findcatalog(projCtx ctx, string name) { lock (gridlock) { for (int i = 0; i < grid_catalog_list.Count; i++) { if (grid_catalog_list[i].catalog_name == name) { return(grid_catalog_list[i]); } } } PJ_GridCatalog catalog = pj_gc_readcatalog(ctx, name); if (catalog == null) { return(null); } lock (gridlock) grid_catalog_list.Add(catalog); return(catalog); }
//********************************************************************** // 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_ctx_free() * //*********************************************************************** public static void pj_ctx_free(projCtx ctx) { }
//********************************************************************** // 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_set_ctx() * //* * //* Note we do not deallocate the old context! * //*********************************************************************** public static void pj_set_ctx(PJ pj, projCtx ctx) { pj.ctx = ctx; }
//*********************************************************************** //* pj_ctx_get_errno() * //*********************************************************************** public static int pj_ctx_get_errno(projCtx ctx) { return ctx.last_errno; }
//********************************************************************** // 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_ctx_set_logger() * //*********************************************************************** public static void pj_ctx_set_logger(projCtx ctx, projCtx.Logger logger) { ctx.logger=logger; }
//********************************************************************** // 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_ctx_get_app_data() * //*********************************************************************** public static object pj_ctx_get_app_data(projCtx ctx) { return(ctx.app_data); }
//*********************************************************************** //* pj_set_ctx() * //* * //* Note we do not deallocate the old context! * //*********************************************************************** public static void pj_set_ctx(PJ pj, projCtx ctx) { pj.ctx=ctx; }
//*********************************************************************** //* pj_ctx_set_errno() * //* * //* Also sets the global errno. * //*********************************************************************** public static void pj_ctx_set_errno(projCtx ctx, int errno) { ctx.last_errno=errno; if(errno!=0) pj_errno=errno; }
//*********************************************************************** //* pj_ctx_set_debug() * //*********************************************************************** public static void pj_ctx_set_debug(projCtx ctx, PJ_LOG debug) { ctx.debug_level=debug; }
//*********************************************************************** //* pj_ctx_set_app_data() * //*********************************************************************** public static void pj_ctx_set_app_data(projCtx ctx, object app_data) { ctx.app_data=app_data; }
//********************************************************************** // 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); }
//********************************************************************** // 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; } }
//********************************************************************** // 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); }
//********************************************************************** // 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; }
// law of cosines static double lc(projCtx ctx, double b, double c, double a) { return(Proj.aacos(ctx, 0.5 * (b * b + c * c - a * a) / (b * c))); }
//*********************************************************************** //* pj_ctx_get_app_data() * //*********************************************************************** public static object pj_ctx_get_app_data(projCtx ctx) { return ctx.app_data; }
//********************************************************************** // 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; } }
//********************************************************************** // 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; }
//********************************************************************** // 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; }
//********************************************************************** // 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)); }
//********************************************************************** // pj_gc_findcatalog() //********************************************************************** public static PJ_GridCatalog pj_gc_findcatalog(projCtx ctx, string name) { lock(gridlock) { for(int i=0; i<grid_catalog_list.Count; i++) if(grid_catalog_list[i].catalog_name==name) return grid_catalog_list[i]; } PJ_GridCatalog catalog=pj_gc_readcatalog(ctx, name); if(catalog==null) return null; lock(gridlock) grid_catalog_list.Add(catalog); return catalog; }
//********************************************************************** // pj_c_findgrid() //********************************************************************** public static PJ_GRIDINFO pj_gc_findgrid(projCtx ctx, PJ_GridCatalog catalog, bool after, LP location, double date, ref PJ_Region optimal_region, ref double grid_date) { for(int i=0; i<catalog.entries.Count; i++) { PJ_GridCatalog.Entry entry=catalog.entries[i]; if((after&&entry.date<date)||(!after&&entry.date>date)) continue; if(location.lam<entry.region.ll_long||location.lam>entry.region.ur_long|| location.phi<entry.region.ll_lat||location.phi>entry.region.ur_lat) continue; if(entry.available==-1) continue; grid_date=entry.date; if(entry.gridinfo==null) { int grid_count; PJ_GRIDINFO[] gridlist=pj_gridlist_from_nadgrids(ctx, entry.definition, out grid_count); if(grid_count==1) entry.gridinfo=gridlist[0]; } return entry.gridinfo; } grid_date=0.0; optimal_region=new PJ_Region(); return null; }
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_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; }
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_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_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_gc_unloadall() // // Deallocate all the grid catalogs (but not the referenced // grids). //********************************************************************** public static void pj_gc_unloadall(projCtx ctx) { grid_catalog_list.Clear(); }
//********************************************************************** // 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_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); }
//********************************************************************** // 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; }