需求:计算不同区域范围,X公里半径内实体店或场站覆盖率。

实现思路:


  • 为了便于理解,将地球看成一个基于经纬度线的坐标系。将经度和纬度看成二维坐标系中的两个纬度,横轴表示经度[-180o, 0o),(0o, 180o],纵轴表示纬度[-90o, 0o),(0o, 90o]。
  • 以最小纬度和经度对应坐标为第一个六角形中心点,在经度方向循环计算六角形各顶点(顶点开始,顺时针,命名:Point1,Point2,Point3,Point4,Point5,Point6)及中心点(Point0)坐标,直至六角形中心点经度大于等于最大经度。 存储六角形标记为(0,0),(0,1),(0,2) ......., 表示六边形位于第0行第N列
  • 第一行计算完成后,开始计算第2行,如下图,第2行(奇数行), 除第1个和最后一个点作特殊处理外,其它点的 Point3、Point4、Point5是重叠的,注意坐标的处理,否则会出现偏差
  • 在维度方向上按上一步循环。完成整个区域范围内六角边分割,注意:为了快速定位,还需计算出每个六角形中心点对应的Geohash,根据半径不一致,可选择不同的Geohash级别
  • 根据场站经纬度计算出30级 Cell ID 值 及 Geohash 码
  • 根据 Geohash 码找出附近的六角形,通过六顶点坐标 构造 IRegion, 判断场站是否包含在六边形内,如果不包含,再次计算出当前Geohash码周边8个Geohash框,再次计算
  • 至此完成此区域内场站命中的六角形。

关键代码


  • 根据中心点坐标、边长、偏差角度(中心点至顶点开始)计算下一个点坐标
  •         public const double Ea = ;     //   赤道半径(米)
    public const double Eb = ; // 极半径 (米) /// <summary>
    ///
    /// </summary>
    /// <param name="lat"></param>
    /// <param name="lng"></param>
    /// <param name="distance"></param>
    /// <param name="angle"></param>
    /// <returns></returns>
    public static Point GetPoint(double lat, double lng, double distance, double angle)
    { double dx = distance * * Math.Sin(angle * Math.PI / 180.0);
    double dy = distance * * Math.Cos(angle * Math.PI / 180.0); double ec = Eb + (Ea - Eb) * (90.0 - lat) / 90.0;
    double ed = ec * Math.Cos(lat * Math.PI / 180.0); double newLon = (dx / ed + lng * Math.PI / 180.0) * 180.0 / Math.PI;
    double newLat = (dy / ec + lat * Math.PI / 180.0) * 180.0 / Math.PI; return new Point(newLat, newLon);
    }
  • 计算场站所属六边形
  • /// <summary>
    ///
    /// </summary>
    /// <param name="destRows"></param>
    /// <param name="cell"></param>
    /// <param name="level"></param>
    /// <param name="staid"></param>
    /// <param name="hashMap"></param>
    /// <param name="geohashValue"></param>
    /// <returns></returns>
    private string GetPgID(DataTable dest, S2Cell cell,string geohashValue)
    {
    //先找当前geohash4的值
    string pgID = this.GetPGIDByHash(dest, cell, geohashValue);
    if (string.IsNullOrEmpty(pgID) == false)
    return pgID; //当前hash未命中时,找相邻8格
    List<string> hashLst = GeoHashService.Default.GetGeoHashExpand(geohashValue);
    foreach (string ghValue in hashLst)
    {
    pgID = this.GetPGIDByHash(dest, cell, ghValue);
    if (string.IsNullOrEmpty(pgID) == false)
    return pgID;
    } return string.Empty;
    } /// <summary>
    ///
    /// </summary>
    /// <param name="dest"></param>
    /// <param name="cell"></param>
    /// <param name="geohashValue"></param>
    /// <returns></returns>
    private string GetPGIDByHash(DataTable dest, S2Cell cell, string geohashValue)
    {
    DataRow[] destRows = dest.Select(string.Format("{0} = '{1}'", M_GEOHASH, geohashValue)); //城市均分的网格 foreach (DataRow dRow in destRows)
    {
    string pgID = Convert.ToString(dRow["ID"]);
    IS2Region cells = this.BuildPolygon(dRow); if (cells.Contains(cell) == true)
    {
    return pgID;
    }
    } return string.Empty;
    } /// <summary>
    /// 构造容器
    /// </summary>
    /// <param name="row"></param>
    /// <returns></returns>
    private IS2Region BuildPolygon(DataRow row)
    {
    List<S2Point> lst = new List<S2Point>();
    lst.Add(this.GetPoint(Convert.ToDouble(row["lat1"]), Convert.ToDouble(row["lng1"])));
    lst.Add(this.GetPoint(Convert.ToDouble(row["lat2"]), Convert.ToDouble(row["lng2"])));
    lst.Add(this.GetPoint(Convert.ToDouble(row["lat3"]), Convert.ToDouble(row["lng3"])));
    lst.Add(this.GetPoint(Convert.ToDouble(row["lat4"]), Convert.ToDouble(row["lng4"])));
    lst.Add(this.GetPoint(Convert.ToDouble(row["lat5"]), Convert.ToDouble(row["lng5"])));
    lst.Add(this.GetPoint(Convert.ToDouble(row["lat6"]), Convert.ToDouble(row["lng6"]))); S2Loop loop = new S2Loop(lst);
    loop.Normalize();
    return loop;
    }

示例效果


参考资料


S2算法应用的更多相关文章

  1. C#实现Google S2算法

    S2其实是来自几何数学中的一个数学符号 S²,它表示的是单位球.S2 这个库其实是被设计用来解决球面上各种几何问题的.值得提的一点是,除去 golang 官方 repo 里面的 geo/s2 完成度目 ...

  2. 高效的多维空间点索引算法 — Geohash 和 Google S2

    原文地址:https://www.jianshu.com/p/7332dcb978b2   引子 每天我们晚上加班回家,可能都会用到滴滴或者共享单车.打开 app 会看到如下的界面:     app ...

  3. 空间数据库系列二:空间索引S2与Z3分析对比

    S2与Z3对比分析 1. S2 2. Geohash 3. Geomesa Z3 4. S2对比geohash 4.1. geohash存在的问题 4.2. S2优势 4.3. 实际对比例子 5. 测 ...

  4. [luoguP1578] 奶牛浴场(DP)

    传送门 O(s2)算法 详见论文 王知昆--浅谈用极大化思想解决最大子矩形问题 我就复制你能把我怎么样QAQ #include <cstdio> #include <iostream ...

  5. 深入解密来自未来的缓存-Caffeine

    1.前言 读这篇文章之前希望你能好好的阅读: 你应该知道的缓存进化史 和 如何优雅的设计和使用缓存? .这两篇文章主要从一些实战上面去介绍如何去使用缓存.在这两篇文章中我都比较推荐Caffeine这款 ...

  6. 基于 Google-S2 的地理相册服务实现及应用

    马蜂窝技术原创内容,更多干货请关注公众号:mfwtech 随着智能手机存储容量的增大,以及相册备份技术的普及,我们可以随时随地用手机影像记录生活,在手机中存储几千张甚至上万张照片已经是很常见的事情.但 ...

  7. 分布式一致性算法--Paxos

    Paxos算法是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法.Paxos算法解决的问题是一个分布式系统如何就某个值(决议)达成一致.在工程实践意义上来说, ...

  8. C#实现Levenshtein distance最小编辑距离算法

    Levenshtein distance,中文名为最小编辑距离,其目的是找出两个字符串之间需要改动多少个字符后变成一致.该算法使用了动态规划的算法策略,该问题具备最优子结构,最小编辑距离包含子最小编辑 ...

  9. Hihocoder 太阁最新面经算法竞赛18

    Hihocoder 太阁最新面经算法竞赛18 source: https://hihocoder.com/contest/hihointerview27/problems 题目1 : Big Plus ...

随机推荐

  1. Bug02_MyBatis_org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    我出现问题的原因是: 映射xml文件名写错了. 查资料,可以有以下解决办法 按以下步骤一一执行: 1:检查xml文件所在的package名称是否和interface对应的package名称一一对应 2 ...

  2. codeforce 839A Arya and Bran(水题)

    Bran and his older sister Arya are from the same house. Bran like candies so much, so Arya is going ...

  3. socket 映射服务器--(可处理多客户端连接(fork),显示退出信息)

    server #include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket ...

  4. java网络编程ServerSocket类 和Socket类的常用构造方法及其方法

    Socket类Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号.Socket(String host, int po ...

  5. Python学习笔记第六周

    目录 一.基础概念 面向对象编程 1.面向对象的几个核心特点 1.class类 2.object对象 3.encapsulation封装 4.inheritance继承 5.polymorphism多 ...

  6. 20165228 2017-2018-2 《Java程序设计》第5周学习总结

    20165228 2017-2018-2 <Java程序设计>第5周学习总结 教材学习内容总结 内部类和匿名类 通过throw关键字抛出异常对象,终止方法的继续执行 使用try-catch ...

  7. 【c++基础】static修饰的函数作用与意义

    static修饰的函数作用与意义 static修饰的函数叫做静态函数,静态函数有两种,根据其出现的地方来分类: 如果这个静态函数出现在类里,那么它是一个静态成员函数: 静态成员函数的作用在于:调用这个 ...

  8. pytorch使用tensorboardX进行loss可视化

    最近pytorch出了visdom,也没有怎么去研究它,主要是觉得tensorboardX已经够用,而且用起来也十分的简单 pip install tensorboardX 然后在代码里导入 from ...

  9. POJO,JaveBean,VO,DTO

    POJO - POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称. 使用POJO名称是为了避免和EJ ...

  10. angular file change

    AngularJs: How to check for changes in file input fields? <input type="file" onchange=& ...