using System; using System.Collections.Generic; using System.Text; namespace APT.Utility { public static class GeohashHelper { #region Direction enum public enum Direction { Top = 0, Right = 1, Bottom = 2, Left = 3 } #endregion private const string Base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; private static readonly int[] Bits = new[] { 16, 8, 4, 2, 1 }; private static readonly string[][] Neighbors = { new[] { "p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Top "bc01fg45238967deuvhjyznpkmstqrwx", // Right "14365h7k9dcfesgujnmqp0r2twvyx8zb", // Bottom "238967debc01fg45kmstqrwxuvhjyznp", // Left }, new[] { "bc01fg45238967deuvhjyznpkmstqrwx", // Top "p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Right "238967debc01fg45kmstqrwxuvhjyznp", // Bottom "14365h7k9dcfesgujnmqp0r2twvyx8zb", // Left } }; private static readonly string[][] Borders = { new[] {"prxz", "bcfguvyz", "028b", "0145hjnp"}, new[] {"bcfguvyz", "prxz", "0145hjnp", "028b"} }; /// /// 计算相邻 /// /// /// /// public static String CalculateAdjacent(String hash, Direction direction) { hash = hash.ToLower(); char lastChr = hash[hash.Length - 1]; int type = hash.Length % 2; var dir = (int)direction; string nHash = hash.Substring(0, hash.Length - 1); if (Borders[type][dir].IndexOf(lastChr) != -1) { nHash = CalculateAdjacent(nHash, (Direction)dir); } return nHash + Base32[Neighbors[type][dir].IndexOf(lastChr)]; } /// /// 细化间隔 /// /// /// /// public static void RefineInterval(ref double[] interval, int cd, int mask) { if ((cd & mask) != 0) { interval[0] = (interval[0] + interval[1]) / 2; } else { interval[1] = (interval[0] + interval[1]) / 2; } } /// /// 解码 /// /// /// public static double[] Decode(String geohash) { bool even = true; double[] lat = { -90.0, 90.0 }; double[] lon = { -180.0, 180.0 }; foreach (char c in geohash) { int cd = Base32.IndexOf(c); for (int j = 0; j < 5; j++) { int mask = Bits[j]; if (even) { RefineInterval(ref lon, cd, mask); } else { RefineInterval(ref lat, cd, mask); } even = !even; } } return new[] { (lat[0] + lat[1]) / 2, (lon[0] + lon[1]) / 2 }; } /// /// 编码 /// /// 纬度 /// 经度 /// 精度 /// public static String Encode(double latitude, double longitude, int precision = 12) { bool even = true; int bit = 0; int ch = 0; string geohash = ""; double[] lat = { -90.0, 90.0 }; double[] lon = { -180.0, 180.0 }; if (precision < 1 || precision > 20) precision = 12; while (geohash.Length < precision) { double mid; if (even) { mid = (lon[0] + lon[1]) / 2; if (longitude > mid) { ch |= Bits[bit]; lon[0] = mid; } else lon[1] = mid; } else { mid = (lat[0] + lat[1]) / 2; if (latitude > mid) { ch |= Bits[bit]; lat[0] = mid; } else lat[1] = mid; } even = !even; if (bit < 4) bit++; else { geohash += Base32[ch]; bit = 0; ch = 0; } } return geohash; } /// /// 获取九个格子 顺序 本身 上、下、左、右、 左上、 右上、 左下、右下 /// /// /// public static String[] getGeoHashExpand(String geohash) { try { String geohashTop = CalculateAdjacent(geohash, Direction.Top);//上 String geohashBottom = CalculateAdjacent(geohash, Direction.Bottom);//下 String geohashLeft = CalculateAdjacent(geohash, Direction.Left);//左 String geohashRight = CalculateAdjacent(geohash, Direction.Right);//右 String geohashTopLeft = CalculateAdjacent(geohashLeft, Direction.Top);//左上 String geohashTopRight = CalculateAdjacent(geohashRight, Direction.Top);//右上 String geohashBottomLeft = CalculateAdjacent(geohashLeft, Direction.Bottom);//左下 String geohashBottomRight = CalculateAdjacent(geohashRight, Direction.Bottom);//右下 String[] expand = { geohash, geohashTop, geohashBottom, geohashLeft, geohashRight, geohashTopLeft, geohashTopRight, geohashBottomLeft, geohashBottomRight }; return expand; } catch (Exception e) { return null; } } } }