13 | GEO是什么?

2024-04-20 21:32
文章标签 13 geo

本文主要是介绍13 | GEO是什么?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • Redis核心技术与实战
    • 实践篇
      • 13 | GEO是什么?还可以定义新的数据类型吗?
        • 面向 LBS 应用的 GEO 数据类型
        • GEO 的底层结构
        • GeoHash 的编码方法
        • 如何操作 GEO 类型?
        • 如何自定义数据类型?
        • Redis 的基本对象结构
        • 开发一个新的数据类型


Redis核心技术与实战

实践篇

13 | GEO是什么?还可以定义新的数据类型吗?

面向 LBS 应用的 GEO 数据类型

LBS(Location-Based Service,位置信息服务) 应用访问的数据是和人或物关联的一组经纬度信息,而且要能查询相邻的经纬度范围,GEO 非常适合应用在 LBS 服务的场景中。

GEO 的底层结构

GEO 类型的底层数据结构就是用 Sorted Set 来实现的。

以叫车应用为例,用 Sorted Set 来保存车辆的经纬度信息时,Sorted Set 的元素是车辆 ID,元素的权重分数是经纬度信息,如下图所示:

在这里插入图片描述

Sorted Set 元素的权重分数是一个浮点数(float 类型),而一组经纬度包含的是经度和纬度两个值,没法直接保存为一个浮点数。

GeoHash 的编码方法

为了能高效地对经纬度进行比较,Redis 采用了业界广泛使用的 GeoHash 编码方法,这个方法的基本原理就是 “二分区间,区间编码”

对一组经纬度进行 GeoHash 编码时,先对经度和纬度分别编码,然后再把经纬度各自的编码组合成一个最终编码。

经度和纬度的单独编码过程:

  1. 对于一个地理位置信息来说,它的经度范围是[-180,180]。GeoHash 编码会把一个经度值编码成一个 N 位的二进制值,来对经度范围[-180,180]做 N 次的二分区操作,其中 N 可以自定义。
  2. 在进行第一次二分区时,经度范围[-180,180]会被分成两个子区间:[-180,0) 和[0,180](左、右分区)。此时,查看要编码的经度值落在了左分区还是右分区。如果是落在左分区,用 0 表示;如果落在右分区,用 1 表示。
  3. 当做完 N 次的二分区后,经度值就可以用一个 N bit 的数来表示。

示例

假设要编码的经度值是 116.37,用 5 位编码值,编码过程如下表所示:

在这里插入图片描述

纬度的编码方式和经度一样,只是纬度的范围是[-90,90],编码过程如下表所示:

在这里插入图片描述

当一组经纬度值都编完码后,再把它们的各自编码值组合在一起,组合的规则是:最终编码值的偶数位上依次是经度的编码值,奇数位上依次是纬度的编码值,其中,偶数位从 0 开始,奇数位从 1 开始

以经纬度(116.37,39.86)为例,最终编码如下图所示:

在这里插入图片描述

用了 GeoHash 编码后,原来无法用一个权重分数表示的一组经纬度(116.37,39.86)就可以用 1110011101 这一个值来表示,可以保存为 Sorted Set 的权重分数。

使用 GeoHash 编码后,相当于把整个地理空间划分成了一个个方格,每个方格对应了 GeoHash 中的一个分区。

示例

把经度区间[-180,180]做一次二分区,把纬度区间[-90,90]做一次二分区,就会得到 4 个分区。

在这里插入图片描述

这 4 个分区对应了 4 个方格,每个方格覆盖了一定范围内的经纬度值,分区越多,每个方格能覆盖到的地理空间就越小,也就越精准。

使用 Sorted Set 范围查询得到的相近编码值,在实际的地理空间上,也是相邻的方格,这就可以实现 LBS 应用“搜索附近的人或物”的功能。

有的编码值虽然在大小上接近,但实际对应的方格却距离比较远。

在这里插入图片描述

为了避免查询不准确问题,可以同时查询给定经纬度所在的方格周围的 4 个或 8 个方格。

如何操作 GEO 类型?
  • GEOADD 命令:用于把一组经纬度信息和相对应的一个 ID 记录到 GEO 类型集合中;
    把 ID 号为 33 的车辆的当前经纬度位置存入 GEO 集合中:
GEOADD cars:locations 116.034579 39.030452 33
  • GEORADIUS 命令:会根据输入的经纬度位置,查找以这个经纬度为中心的一定范围内的其他元素。
    查找以经纬度(116.054579,39.030452 )为中心的 5 公里内的车辆信息
GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10

使用 ASC 选项,指定按照距离这个中心位置从近到远的方式来排序,COUNT 选项指定返回信息的数量。

如何自定义数据类型?

Redis 键值对中的每一个值都是用 RedisObject 保存的。

RedisObject 包括元数据和指针。其中,元数据的一个功能就是用来区分不同的数据类型,指针用来指向具体的数据类型的值。

Redis 的基本对象结构

RedisObject 的内部组成包括了 type、encoding、lru 和 refcount 4 个元数据,以及 1 个*ptr指针。

  • type:表示值的类型;
  • encoding:是值的编码方式,用来表示 Redis 中实现各个基本类型的底层数据结构,例如 SDS、压缩列表、哈希表、跳表等;
  • lru:记录了这个对象最后一次被访问的时间,用于淘汰过期的键值对;
  • refcount:记录了对象的引用计数;
  • *ptr:是指向数据的指针。

在这里插入图片描述

开发一个新的数据类型

首先,需要为新数据类型定义好它的底层结构、type 和 encoding 属性值,然后再实现新数据类型的创建、释放函数和基本命令。

以开发一个名字叫作 NewTypeObject 的新数据类型为例,解释下具体的 4 个操作步骤。

在这里插入图片描述

1. 第一步:定义新数据类型的底层结构

用 newtype.h 文件来保存这个新类型的定义,具体定义的代码如下所示:

struct NewTypeObject {struct NewTypeNode *head; size_t len; 
}NewTypeObject;

NewTypeNode 结构是自定义的新类型的底层结构。设计两个成员变量:一个是 Long 类型的 value 值,用来保存实际数据;一个是*next指针,指向下一个 NewTypeNode 结构。

struct NewTypeNode {long value;struct NewTypeNode *next;
};

2. 第二步:在 RedisObject 的 type 属性中,增加这个新类型的定义

定义在 Redis 的 server.h 文件中。

#define OBJ_STRING 0    /* String object. */
#define OBJ_LIST 1      /* List object. */
#define OBJ_SET 2       /* Set object. */
#define OBJ_ZSET 3      /* Sorted set object. */
…
#define OBJ_NEWTYPE 7

3. 第三步:开发新类型的创建和释放函数

Redis 把数据类型的创建和释放函数都定义在了 object.c 文件中。

robj *createNewTypeObject(void){NewTypeObject *h = newtypeNew(); robj *o = createObject(OBJ_NEWTYPE,h);return o;
}

newtypeNew 函数用来为新数据类型初始化内存结构。这个初始化过程主要是用 zmalloc 做底层结构分配空间,以便写入数据。

NewTypeObject *newtypeNew(void){NewTypeObject *n = zmalloc(sizeof(*n));n->head = NULL;n->len = 0;return n;
}

newtypeNew 函数涉及到新数据类型的具体创建,而 Redis 默认会为每个数据类型定义一个单独文件,实现这个类型的创建和命令操作,例如,t_string.c 和 t_list.c 分别对应 String 和 List 类型。按照 Redis 的惯例,把 newtypeNew 函数定义在名为 t_newtype.c 的文件中。

createObject 是 Redis 本身提供的 RedisObject 创建函数,它的参数是数据类型的 type 和指向数据类型实现的指针*ptr。

robj *createObject(int type, void *ptr) {robj *o = zmalloc(sizeof(*o));o->type = type;o->ptr = ptr;...return o;
}

对于释放函数来说,它是创建函数的反过程,是用 zfree 命令把新结构的内存空间释放掉。

4. 第四步:开发新类型的命令操作

  • 在 t_newtype.c 文件中增加命令操作的实现。
void ntinsertCommand(client *c){//基于客户端传递的参数,实现在NewTypeObject链表头插入元素
}
  • 在 server.h 文件中,声明已经实现的命令,以便在 server.c 文件引用这个命令。
void ntinsertCommand(client *c)
  • 在 server.c 文件中的 redisCommandTable 里面,把新增命令和实现函数关联起来。例如,新增的 ntinsert
    命令由 ntinsertCommand 函数实现,然后就可以用 ntinsert 命令给 NewTypeObject 数据类型插入元素。
struct redisCommand redisCommandTable[] = { 
...
{"ntinsert",ntinsertCommand,2,"m",...}
}

这篇关于13 | GEO是什么?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921369

相关文章

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

13 transition数组的动画使用

划重点 动画:transitiontransition-group :数组动画数组的 添加 / 删除 豆腐粉丝汤 清淡又健康 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><me

Redis地理数据类型GEO

通常要计算两个地理位置的距离不是很方便,这里可以直接通过Redis提供的GEO操作来完成地理位置相关的计算 1)添加地理位置 语法:geoadd key longitude latitude member [longitude latitude member] ...字段说明:key:存放地理位置的集合名称longitude:地理坐标的经度latitude:地理坐标的纬度member:表示这

【CTF Web】BUUCTF Upload-Labs-Linux Pass-13 Writeup(文件上传+PHP+文件包含漏洞+PNG图片马)

Upload-Labs-Linux 1 点击部署靶机。 简介 upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。 注意 1.每一关没有固定的通关方法,大家不要自限思维! 2.本项目提供的writeup只是起一个参考作用,希望大家可以分享出自己的通关思路

Chapter 13 普通组件的注册使用

欢迎大家订阅【Vue2+Vue3】入门到实践 专栏,开启你的 Vue 学习之旅! 文章目录 前言一、组件创建二、局部注册三、全局注册 前言 在 Vue.js 中,组件是构建应用程序的基本单元。本章详细讲解了注册和使用 Vue 的普通组件的两种方式:局部注册和全局注册。 本篇文章参考黑马程序员 一、组件创建 ①定义 Vue 组件是一种具有特定功能的 Vue 实

VMware Fusion Pro 13 Mac版虚拟机 安装Win11系统教程

Mac分享吧 文章目录 Win11安装完成,软件打开效果一、VMware安装Windows11虚拟机1️⃣:准备镜像2️⃣:创建虚拟机3️⃣:虚拟机设置4️⃣:安装虚拟机5️⃣:解决连不上网问题 安装完成!!! Win11安装完成,软件打开效果 一、VMware安装Windows11虚拟机 首先确保自己的mac开启了网络共享。不然虚拟机连不上👀的 1️⃣:准备镜像

华为 HCIP-Datacom H12-821 题库 (13)

有需要题库的可以看主页置顶 1.可以携带外部路由的 tag 标签信息的是以下哪一类 LSA? A、4 类 LSA B、5 类 LSA  C、3 类 LSA  D、2 类 LSA 答案:B 解析: 暂无解析 2..两台路由器直连,并设定网络类型为 p2p 建立OSPF 邻居。那么两台路由器传输 OSPF 报文的目的 IP 地址是以下哪一项? A、使用组播地址 224.0.0.6 B

[情商-13]:语言的艺术:何为真实和真相,所谓真相,就是别人想让你知道的真相!洞察谎言与真相!

目录 前言: 一、说话的真实程度分级 二、说谎动机分级:善意谎言、中性谎言、恶意谎言 三、小心:所谓真相:只说对自己有利的真相 四、小心:所谓真相:就是别人想让你知道的真相 五、小心:所谓善解人意:就是别人只说你想要听到的话 前言: 何为真实和真相,所谓真相,就是别人想让你知道的真相!洞察谎言与真相! 人与人交流话语中,处处充满了不真实,完全真实的只是其中一小部分,这

C++笔试强训12、13、14

文章目录 笔试强训12一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训13一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训14一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训12 一、选择题 1-5题 引用:是一个别名,与其被引用的实体公用一份内存空间,编译器不会给引用变量单独开辟新的空间。A错误 故选A。 A

java基础总结13-面向对象9(对象转型)

对象转型分为两种:一种叫向上转型(父类对象的引用或者叫基类对象的引用指向子类对象,这就是向上转型),另一种叫向下转型。转型的意思是:如把float类型转成int类型,把double类型转成float类型,把long类型转成int类型,这些都叫转型。把一种形式转成另外一种形式就叫转型。除了基础数据类型的转型之外(基础数据类型的转型:大的可以转成小的,小的也可以转成大的。),对象领域里面也有对象之