211 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
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"}
 | 
						|
                                                     };
 | 
						|
        /// <summary>
 | 
						|
        /// 计算相邻
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="hash"></param>
 | 
						|
        /// <param name="direction"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        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)];
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// 细化间隔
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="interval"></param>
 | 
						|
        /// <param name="cd"></param>
 | 
						|
        /// <param name="mask"></param>
 | 
						|
        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;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 解码
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="geohash"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        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 };
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// 编码
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="latitude">纬度</param>
 | 
						|
        /// <param name="longitude">经度</param>
 | 
						|
        /// <param name="precision">精度</param>
 | 
						|
        /// <returns></returns>
 | 
						|
        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;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 获取九个格子 顺序 本身 上、下、左、右、 左上、 右上、 左下、右下
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="geohash"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        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;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
}
 |