Uber H3 index 地图索引思考
H3 是 uber 设计的六边形空间索引,go 语言操作包是 h3-go,可以通过经纬度获取所在的 h3 六边形边界,每个经纬度对应的六边形都是确定的,每个六边形唯一对应了一个 h3index。在业务开发中,我们可以通过 h3index 来对地理空间中的对象做聚合,本质还是将经纬度查询转换成了 h3index 的查询。
维度
不同维度的 h3index 表示不同大小的地理空间,我们可以查看官方介绍来了解对应关系。h3 有 16 个维度,维度越高,表示的面积越小,也就越是精确。下图的对应关系,表示不同维度的h3index 表示地球的组成。
拿维度 15 来说,已经是 h3 能表示的最小单元了。用这个维度来表示地球的话,需要存储 569,707,381,193,162 个该维度的 h3index 索引。后面又列举了其中六边形(hexagon)和五边形(pentagon)的组成个数。为什么组成中还会有五变形呢? 我们应该从设计的根源上去探索解释。
使用 h3-go 包,我们通过指定经纬度、以及维度就可以获取到对应的 h3index 索引。还可以通过 h3index 获取所在六边形的组成点。下面代码中 FromGeo
就返回了对应的 h3index。
geo := GeoCoord{Latitude: 37.775938728915946,Longitude: -122.41795063018799,
}
resolution := 9
fmt.Printf("%#x\n", FromGeo(geo, resolution))
想比较 h3index 能表示的最小单元格个数而言,我们最关心的还是单元格所能表示的面积大小,不同维度的 h3index 究竟能表示多大的范围。
H3 设计
将真实世界的 3 维地理空间映射到 2 维的网格体系,是每个地图索引都需要面对的问题。H3 通过球心投影的方式将地球抽象成一个空间正20 面体,但地球本身也不是极致的圆形,抽象肯定会牺牲一部分准确性。
正20面体的每个面都是一个三角形平面,抽象到地图上的话,每个平面应该都是二维的平面。然后就变成了一个圆形的正20面体,就正好可以接受地图的投影。而且,如果把正 20 面体的每个面进行编码,我们可以通过选择正 20 面体,将合适的面对应到合适的地理区域上。
正 20 面体展开到二维平面,就是地球的投影,下面就是其中的一种展开方式。因为有陆地和海洋的区别,而我们要分析的地理数据基本都是在陆地上的。所以,正 20 面体通过合理的转动,可以将它的顶点都落在海洋的区域。为什么要特别处理这些顶点数据呢?
H3网格索引没有直接利用正 20 面体,而是在每个面的基础上继续做了网格拆分。为什么最终选择使用正六边形作为拆分的单元呢,还有没有别的多边形可以选择呢?其实还可以有三角形和正方形,GeoHash 采用的就是正方面的网格,那么正方形网格和正六边形有什么区别吗?
假设一个 n 面体,多面体的内角和计算公式为 (n-2)*180
,假设 m 个正多边形在任意顶点出拼接形成一个 360 度才能形成网格,最终计算的表达式:(n-2)*180/n*m=360
。如果 n 表示三角形,就需要有 6 个面拼接;如果 n 是正方形,就需要 4 个面拼接,如果 n 是六边形,就需要 3 个面拼接。
如果按照三角形或者四边形的拆分方式,某一个网格单元和临边的网格单元的中心距离是不相同的,如上图。但采用正六边形,网格单元和临边的其它网格单元是相同的。那么,在网格体系中,我们如何获取某个网格单元周边的网格呢?
H3 网格独立于现实中的区域,真实世界的区域边界往往会发生一些变化,区域的边界形态各异,经常还会发生变动调整,如果保持现实地理的区域跟踪,保证区域边界数据动态更新难度可想而知。试想一下,如果我们根据北京的每个区、每个街道进行数据统计,复杂性是非常大的。
而 H3 是在地球基础上的网格系统,它不用关心现实的街道、区域边界变化,无论现实中的地域如何变化,H3 都不会发生变化,也就是将地图上的某一个区域固定化了。如果要统计某个区域的维度信息,只需要先获取该区域的所有 H3 网格,汇总所有网格的信息就可以了。
在 0 级索引下,每个面包含了 10 个单元,如上图,其中边界线上的一些单元会被多个平面共同包含,这个面总共包含了 5.5 个六边形和 3 个三角形。上文提到的,正20面体所包含的 110 个六边形和 12 个五边形就是这么计算的出来的。正20面体包含有 20 个面、30条边、12 个顶点,其中的 12 个顶点就正好对应了这 12 个五边形。
上图形象的描述了,2个等级 H3 网格的大小关系,高级别的 H3 网格大概需要 7 个接近一个低级别的网格,两个级别之间不是完全的包含关系。
参考文章:
- Uber’s Hexagonal Hierarchical Spatial Index
- h3geo文档
相关文章:

Uber H3 index 地图索引思考
H3 是 uber 设计的六边形空间索引,go 语言操作包是 h3-go,可以通过经纬度获取所在的 h3 六边形边界,每个经纬度对应的六边形都是确定的,每个六边形唯一对应了一个 h3index。在业务开发中,我们可以通过 h3index 来对地理…...
多线程的几种状态
Java-多线程的几种状态🔎1.NEW( 系统中线程还未创建,只是有个Thread对象)🔎2.RUNNABLE( (就绪状态. 又可以分成正在工作中和即将开始工作)🔎3.TERMINATED(系统中的线程已经执行完了,Thread对象还在)🔎4.TIMED_WAITING(指定时间等待…...
【算法题】1574. 删除最短的子数组使剩余数组有序
题目: 给你一个整数数组 arr ,请你删除一个子数组(可以为空),使得 arr 中剩下的元素是 非递减 的。 一个子数组指的是原数组中连续的一个子序列。 请你返回满足题目要求的最短子数组的长度。 示例 1: …...

理解对数——金融问题中的自然对数(以e为底的对数)
第3章 金融问题(Financial Matters)——金融问题中的自然对数If thou lend moneyto any ofMy people. ...thou shalt not beto him as a creditor;neither shall yelay upon him interest.(如果你借钱给我的任何人。 ……你不应该是他的债权人;也不可向他加息。)——…...
vue2进阶学习之路
HTML、CSS和JavaScript基础 在学习Vue2之前,需要掌握HTML、CSS和JavaScript的基础知识。包括HTML的标签、CSS的布局和样式、JavaScript的变量类型、条件语句、循环语句等。 Vue2的基础知识 掌握Vue2的基本概念和语法,包括Vue2实例、数据绑定、指令、组件…...

决策树ID3算法
1. 决策树ID3算法的信息论基础 机器学习算法其实很古老,作为一个码农经常会不停的敲if, else if, else,其实就已经在用到决策树的思想了。只是你有没有想过,有这么多条件,用哪个条件特征先做if,哪个条件特征后做if比较优呢&#…...

C++模板基础(一)
函数模板(一) ● 使用 template 关键字引入模板: template void fun(T) {…} – 函数模板的声明与定义 – typename 关键字可以替换为 class ,含义相同 – 函数模板中包含了两对参数:函数形参 / 实参;模板形…...
生产者消费者模型线程池(纯代码)
目录 生产者消费者模型 条件变量&&互斥锁(阻塞队列) makefile Task.hpp BlockQueue.hpp BlockQueueTest.cc 信号量&&互斥锁(环形队列) makefile RingQueue.hpp RingQueueTest.cc 线程池(封…...
K8s 应用的网络可观测性: Cilium VS DeepFlow
随着分布式服务架构的流行,特别是微服务等设计理念在现代应用普及开来,应用中的服务变得越来越分散,因此服务之间的通信变得越来越依赖网络,很有必要来谈谈实现微服务可观测性中越来越重要的一环——云原生网络的可观测。K8s 是微服务设计理念能落地的最重要的承载体,本文…...

3.29面试题
文章目录内存内存管理执行过程要点面试题内存 内存管理 由JVM管理 堆:new出来的对象(包括成员变量、数组元素、方法的地址)栈:局部变量(包括方法的参数)方法区:.class字节码文件(…...

操作系统漏洞发现
操作系统漏洞发现前言一、操作系统漏洞发现1.1 namp2. Goby3. Nessus二,进行渗透测试2.1 使用工具进行渗透1. metasploit2.2 EXP2.3 复现文章三,操作系统漏洞修复前言 不管是对于App来说,还是web站点来说,操作系统是必须的&#x…...

Linux gdb调试底层原理
TOC 前言 linux下gdb调试程序操作过程参考本人文章:gdb调试操作; 这里不再叙述; 本文主要内容是介绍GDB本地调试的底层调试原理,我们来看一下GDB是通过什么机制来控制被调试程序的执行顺序; 总结部分是断点调试的底层原理,可以直接跳转过去先看看大概…...
LC-1647. 字符频次唯一的最小删除次数(哈希+计数)
1647. 字符频次唯一的最小删除次数 难度中等56 如果字符串 s 中 不存在 两个不同字符 频次 相同的情况,就称 s 是 优质字符串 。 给你一个字符串 s,返回使 s 成为 优质字符串 需要删除的 最小 字符数。 字符串中字符的 频次 是该字符在字符串中的出现…...
HTTP状态码
100: 接受,正在继续处理 200: 请求成功,并返回数据 201: 请求已创建 202: 请求已接受 203: 请求成为,但未授权 204: 请求成功,没有内容 205: 请求成功,重置内容 206: 请求成功,返回部分内容 301: 永久性重定…...

【Linux】初见“which命令”,“find命令”以及linux执行命令优先级
文章目录1.which命令1.1 whereis命令1.2 locate命令1.3 搜索文件命令总结2.find命令2.1 find之exec用法2.2 管道符之xargs用法3 Linux常用命令4.命令执行优先级1.which命令 查找命令文件存放目录 搜索范围由环境变量PATH决定(echo $PATH) which命令格式࿱…...
update case when 多字段,多条件, mysql中case when用法
文章目录 前言 sql示例 普通写法: update case when写法 update case when 多字段写法 case when语法 case when 的坑 1、不符合case when条件但是字段被更新为null了 解决方法一:添加where条件 解决方法二:添加else 原样输出 2、同一条数据符…...

mysql隐式转换 “undefined“字符串匹配到mysql int类型0值字段
描述:mysql 用字符串搜索 能搜到int类型查询结果 mysql int类型条件用字符串查询 table: CREATE TABLE all_participate_records (id bigint unsigned NOT NULL AUTO_INCREMENT,created_at datetime(3) DEFAULT NULL,updated_at datetime(3) DEFAULT NULL,deleted…...
Redis八股文
1.Redis是什么? Redis 是一个基于 C 语言开发的开源数据库(BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的(内存数据库),读写速度非常快,被广泛应用于缓存方向。并且,…...
InnoDB——详细解释锁的应用,一致性读,自增长与外键
一致性非锁定读 一致性的非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制的方式读取当前执行时数据库中行的数据。 如果读取的行正在执行 行Delete或Update操作,这时读取操作不会因此去等待行上锁的释放。相反&…...

C++模板基础(四)
函数模板(四) ● 函数模板的实例化控制 – 显式实例化定义: template void fun(int) / template void fun(int) //header.h template<typename T> void fun(T x) {std::cout << x << std::endl; }//main.cpp #include&quo…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...

如何把工业通信协议转换成http websocket
1.现状 工业通信协议多数工作在边缘设备上,比如:PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发,当设备上用的是modbus从站时,采集设备数据需要开发modbus主站;当设备上用的是西门子PN协议时…...

C++11 constexpr和字面类型:从入门到精通
文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...
手动给中文分词和 直接用神经网络RNN做有什么区别
手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动)…...