/// <summary> /// /// </summary> /// <param name="t"></param> /// <param name="ct"></param> /// <returns></returns> private static PhiLam NadInterpolate(PhiLam t, NadTable ct) { PhiLam result, remainder; // find indices and normalize by the cell size (so fractions range from 0 to 1) int iPhi = (int) Math.Floor(t.Lambda /= ct.CellSize.Lambda); int iLam = (int) Math.Floor(t.Lambda /= ct.CellSize.Phi); // use the index to determine the remainder remainder.Phi = t.Phi - iPhi; remainder.Lambda = t.Lambda - iLam; int offLam = 0; // normally we look to the right and bottom neighbor cells int offPhi = 0; if(remainder.Phi < .5) offLam = -1; // look to cells above current cell if(remainder.Lambda < .5) offPhi = -1; // look to cells to the left of current cell PhiLam topLeft = GetValue(iPhi + offPhi, iLam + offLam, ct); PhiLam topRight = GetValue(iPhi + offPhi, iLam + offLam + 1, ct); PhiLam bottomLeft = GetValue(iPhi + offPhi + 1, iLam + offLam, ct); PhiLam bottomRight = GetValue(iPhi + offPhi + 1, iLam + offLam + 1, ct); // because the fractional weights are between cells, we need to adjust the // "remainder" so that it is now relative to the center of the top left // cell, taking into account that the definition of the top left cell // depends on whether the original remainder was larger than .5 remainder.Phi = (remainder.Phi > .5) ? remainder.Phi - .5 : remainder.Phi + .5; remainder.Lambda = (remainder.Lambda > .5) ? remainder.Lambda - .5 : remainder.Phi + .5; // The cell weight is equivalent to the area of a cell sized square centered // on the actual point that overlaps with the cell. // Since the overlap must add up to 1, any portion that does not overlap // on the left must overlap on the right, hence (1-remainder.Lambda) double mTL = remainder.Lambda*remainder.Phi; double mTR = (1 - remainder.Lambda)*remainder.Phi; double mBL = remainder.Lambda*(1 - remainder.Phi); double mBR = (1 - remainder.Lambda) * (1 - remainder.Phi); result.Lambda = mTL*topLeft.Lambda + mTR*topRight.Lambda + mBL*bottomLeft.Lambda + mBR*bottomRight.Lambda; result.Phi = mTL*topLeft.Phi + mTR*topRight.Phi + mBL*bottomLeft.Phi + mBR*bottomRight.Phi; return result; }
/// <summary> /// So that we can read from an embedded stream, this reads a NadTable /// from any stream, not just a filename. /// </summary> /// <param name="str">The stream to read</param> public void ReadStream(Stream str) { StreamReader sr = new StreamReader(str); _name = sr.ReadLine(); string numText = sr.ReadToEnd(); char[] separators = new[] {' ', ',',':', (char)10}; string[] values = numText.Split(separators, StringSplitOptions.RemoveEmptyEntries); _numLambdas = int.Parse(values[0]); _numPhis = int.Parse(values[1]); _lowerLeft.Lambda = double.Parse(values[3]) * DegToRad; _lowerLeft.Phi = double.Parse(values[4]) * DegToRad; _cellSize.Lambda = double.Parse(values[5]) * DegToRad; _cellSize.Phi = double.Parse(values[6]) * DegToRad; int p = 7; _cvs = new PhiLam[_numPhis][]; for(int i = 0; i < _numPhis; i++) { _cvs[i] = new PhiLam[_numLambdas]; int iCheck = int.Parse(values[p]); if (iCheck != i) { throw new ProjectionException(ProjectionMessages.IndexMismatch); } p++; double lam = long.Parse(values[p]) * USecToRad; _cvs[i][0].Lambda = lam; p++; double phi = long.Parse(values[p]) * USecToRad; _cvs[i][0].Phi = phi; p++; for (int j = 1; j < _numLambdas; j++ ) { lam += long.Parse(values[p]) * USecToRad; _cvs[i][j].Lambda = lam; p++; phi += long.Parse(values[p])*USecToRad; _cvs[i][j].Phi = phi; p++; } } }
private static PhiLam Convert(PhiLam input, bool inverse, NadTable table ) { if (input.Lambda == HUGE_VAL) return input; // Normalize input to ll origin PhiLam tb = input; tb.Lambda -= table.LowerLeft.Lambda; tb.Phi -= table.LowerLeft.Phi; tb.Lambda = Proj.Adjlon(tb.Lambda - Math.PI) + Math.PI; PhiLam t = NadInterpolate(tb, table); if(inverse) { PhiLam del, dif; int i = MAX_TRY; if (t.Lambda == HUGE_VAL) return t; t.Lambda = tb.Lambda + t.Lambda; t.Phi = tb.Phi - t.Phi; do { del = NadInterpolate(t, table); /* 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.Lambda == HUGE_VAL) { System.Diagnostics.Debug.WriteLine(ProjectionMessages.InverseShiftFailed); break; } t.Lambda -= dif.Lambda = t.Lambda - del.Lambda - tb.Lambda; t.Phi -= dif.Phi = t.Phi + del.Phi - tb.Phi; } while (i-- > 0 && Math.Abs(dif.Lambda) > TOL && Math.Abs(dif.Phi) > TOL); if (i < 0) { System.Diagnostics.Debug.WriteLine(ProjectionMessages.InvShiftConvergeFailed); t.Lambda = t.Phi = HUGE_VAL; return t; } input.Lambda = Proj.Adjlon(t.Lambda + table.LowerLeft.Lambda); input.Phi = t.Phi + table.LowerLeft.Phi; } else { if(t.Lambda == HUGE_VAL) { input = t; } else { input.Lambda -= t.Lambda; input.Phi += t.Phi; } } return input; }