/// <summary> /// Lookup the coordinate and compare it against all known section markers, /// returns the best fit section /// </summary> /// <param name="coordinate"></param> /// <returns></returns> public static DlsSystem?FromLatLongCoordinate(LatLongCoordinate coordinate) { // This method estimates a township that is close to the coordinate. if (!TryInferTownshipForLatLongCoordinate(coordinate, out byte meridian, out byte range, out byte township)) { return(null); } //get all markers in township var markers = DlsSurveyCoordinateProvider.Instance.TownshipMarkers(township, range, meridian); if (markers == null) { return(null); } //each township is numbered as: there are a max of 4 markers per section with 2 floats = 144 coordinates // some markers are empty (that section does not exist) // 31|32|33|34|35|36 // 30|29|28|27|26|25 // 19|20|21|22|23|24 // 18|17|16|15|14|13 // 07|08|09|10|11|12 // 06|05|04|03|02|01 double bestDistance = double.MaxValue; DlsSystem?bestDls = null; //test each section in the town for (byte section = 1; section <= 36; section++) { var dlsBoundary = markers[section - 1]; if (dlsBoundary == null || dlsBoundary.Count == 0) { continue; //invalid section } //find the center for each lsd in the section for (byte legalSubdivision = 1; legalSubdivision <= 16; legalSubdivision++) { double testDistance; switch (dlsBoundary.Count) { case 4: testDistance = coordinate.RelativeDistanceTo(Interpolate4Point(legalSubdivision, dlsBoundary)); break; default: testDistance = double.MaxValue; //should never get here break; } if (testDistance < bestDistance) { bestDistance = testDistance; bestDls = new DlsSystem(legalSubdivision, section, township, range, meridian); } } } return(bestDls); }
/// <summary> /// Returns the NW, NE, SW, SE corners of the township /// </summary> /// <param name="township"></param> /// <param name="range"></param> /// <param name="meridian"></param> /// <returns></returns> public LatLongCorners TownshipBoundary(byte township, byte range, byte meridian) { //ask the boundary provider for a list of sections that border this township //each township is numbered as: // 31|32|33|34|35|36 // 30|29|28|27|26|25 // 19|20|21|22|23|24 // 18|17|16|15|14|13 // 07|08|09|10|11|12 // 06|05|04|03|02|01 //look at each coordinate pair until we find ones that are the extreme corners var key = (ushort)(meridian << 13 | range << 7 | township); if (!_offsets.TryGetValue(key, out var townshipFloats)) { return(null); } LatLongCoordinate?se = null, sw = null, ne = null, nw = null; int c = 0; for (int i = 0; i < 144; i++) { var lat = townshipFloats[c++]; var lon = townshipFloats[c++]; if (lat == 0 && lon == 0) { continue; } if (se == null || lat < se.Value.Latitude && lon > se.Value.Longitude) { se = new LatLongCoordinate(lat, lon); } if (sw == null || lat < sw.Value.Latitude && lon < sw.Value.Longitude) { sw = new LatLongCoordinate(lat, lon); } if (ne == null || lat > ne.Value.Latitude && lon > ne.Value.Longitude) { ne = new LatLongCoordinate(lat, lon); } if (nw == null || lat > nw.Value.Latitude && lon < nw.Value.Longitude) { nw = new LatLongCoordinate(lat, lon); } } return(new LatLongCorners(se, sw, nw, ne)); }
/// <summary> /// Makes a best guess at the Township that contains the given coordinate. /// </summary> /// <param name="coordinate">The lat long to use when looking up a township</param> /// <param name="meridian"></param> /// <param name="range"></param> /// <param name="township"></param> /// <returns></returns> private static bool TryInferTownshipForLatLongCoordinate(LatLongCoordinate coordinate, out byte meridian, out byte range, out byte township) { meridian = 0; range = 0; township = 0; var longitude = coordinate.Longitude; if (longitude > Meridians[0] || longitude < Meridians[Meridians.Length - 1]) { throw new CoordinateConversionException("Meridian is out of range"); } //determine the base meridian byte mrd = 0; for (byte k = 1; k < 8; k++) { if (longitude <= Meridians[k - 1] && longitude > Meridians[k]) { mrd = k; break; } } if (mrd == 0) { return(false); } var twp = (byte)(Math.Floor((coordinate.Latitude - BaseLatitude) / TownshipHeightInDegrees) + 1); if (twp <= 0) { return(false); } double townshipWidthInDegrees = 6 * GetSectionWidthInDegrees(twp); double meridianLongitude = Meridians[mrd - 1]; //subtract the meridian from the longitude and use the remainder to calculate the range number var rng = (byte)(Math.Floor((longitude - meridianLongitude) / townshipWidthInDegrees) + 1); meridian = mrd; range = rng; township = twp; return(true); }
private static LatLongCoordinate?LatLongCoordinate(float[] townshipFloats, int offset) { var lat = townshipFloats[offset]; var lon = townshipFloats[offset + 1]; LatLongCoordinate?coordinate; if (lat == 0 && lon == 0) { coordinate = null; } else { coordinate = new LatLongCoordinate(lat, lon); } return(coordinate); }
/// <summary> /// Converts the <see cref="LatLongCoordinate"/> instance to a BC NTS location. /// </summary> /// <param name="coordinate">The coordinate.</param> /// <returns></returns> public static BcNtsGridSystem FromLatLongCoordinates(LatLongCoordinate coordinate) { var longitude = Math.Abs(coordinate.Longitude); var latitude = Math.Abs(coordinate.Latitude); byte pq = 0; foreach (var keyValuePair in LatPq) { var q = keyValuePair.Key; if (latitude >= LatPq[q] && latitude < LatPq[q] + 4 && longitude >= LngPq[q] && longitude < LngPq[q] + 8) { pq = q; break; } } if (pq == 0) { throw new CoordinateConversionException("The geographic location is not in a BC primary quadrant."); } var lat = latitude - LatPq[pq]; var lng = longitude - LngPq[pq]; var lq = '\0'; foreach (var key in LatLq.Keys) { if (lat >= LatLq[key] && lat < LatLq[key] + 1 && lng >= LngLq[key] && lng < LngLq[key] + 2) { lq = key; break; } } if (lq == '\0') { throw new CoordinateConversionException("lq is invalid."); } lat -= LatLq[lq]; lng -= LngLq[lq]; byte six = 0; foreach (var n in LatSix.Keys) { if (lat >= LatSix[n] && lat < LatSix[n] + 0.25 && lng >= LngSix[n] && lng < LngSix[n] + 0.5) { six = n; break; } } if (six == 0) { throw new CoordinateConversionException("six is invalid"); } lat -= LatSix[six]; lng -= LngSix[six]; var zn = '\0'; foreach (var n in LatZn.Keys) { if (lat >= LatZn[n] && lat < LatZn[n] + BlockHeight && lng >= LngZn[n] && lng < LngZn[n] + BlockWidth) { zn = n; break; } } if (zn == '\0') { throw new CoordinateConversionException("Zone is invalid"); } lat -= LatZn[zn]; lng -= LngZn[zn]; //every unit is 1/120 high by 1/80 wide var y = (byte)Math.Floor(120 * lat); var x = (byte)Math.Floor(lng / 0.0125); var unit = (byte)(x + 1 + y * 10); lat -= y / 120.0; lng -= x * 0.0125; var qtr = '\0'; foreach (var n in LatQtr.Keys) { if (lat >= LatQtr[n] && lat < LatQtr[n] + QuarterUnitHeight && lng >= LngQtr[n] && lng < LngQtr[n] + QuarterUnitWidth) { qtr = n; break; } } if (qtr == '\0') { throw new CoordinateConversionException("Quarter is invalid."); } return(new BcNtsGridSystem(qtr, unit, zn, pq, lq, six)); }