【经纬度转地址实现方案】根据给定的经纬度,查询对应城市,通过建立经纬度geohash-行政区映射表,实现快速查询

文章目录

    • 背景
    • 目标
    • 方案设计:
    • 表结构设计:
    • 方案实现
      • 1.高德API获取行政区边界点
      • 2.外包矩形中心作为中心点
      • 3.坐标点经纬度转换为geohash
    • 测试
      • 建表语句
      • 测试造数
      • 测试用例
      • 测试结果
    • 总结
      • 总结

背景

最近遇到一个需求,需要查询给定的经纬度坐标点,是否在指定的城市内。 分析需求,需要获取城市区域的边界,再判断目标坐标点是否在边界内。单个坐标点的查询可以通过mysql空间函数来实现,要是遇到一批坐标点都要转城市,那就要重新考虑方案了。咨询了隔壁组的数据开发大佬,提出了可以开发一张经纬度转行政区的维表,后续查询直接关联表就好了,于是有了这篇文章。

目标

用户传入一批经纬度,可以快速匹配出对应的行政区,包括省份、城市、区域。

方案设计:

image-20241121104242096

  1. 调用高德API 获取所有行政区以及对应的边界点

  2. 坐标点经纬度 -> geohash,将每个边界点转换为6位精度的geohash

  3. 取这些Geohash中的中心点的Geohash,作为区域代表

  4. 整合数据,得到行政区和区域中心点Geohash的映射表

  5. 使用经纬度转行政区表,进行查询

表结构设计:

全国行政区表

以下是根据您提供的内容整理的表格结构,包括字段名、字段类型、字段描述和说明:

字段名字段类型字段描述说明
province_codeSTRING省编号用于标识省份的唯一编号
province_nameSTRING省名称省份的全称
city_codeSTRING市编号用于标识城市的唯一编号
city_nameSTRING市名称若为直辖市则是显示市辖区城市的全称,对于直辖市则显示为市辖区
dist_codeSTRING区编号用于标识区/县/市辖区的唯一编号,也是主键
dist_nameSTRING区名称区/县/市辖区的全称
full_nameSTRING行政单位全名由省名称、市名称(或市辖区)和区名称组合而成的全名
is_direct_cityBIGINT是否直辖市或特别行政区1表示是直辖市或特别行政区,0表示否

经纬度转行政区维表

字段名字段类型字段描述说明
geohashSTRINGgeohash(长度为6位)将经纬度转换为geohash与此字段关联,用于地理位置的近似表示
full_nameSTRING解析结果全名(省-市-区格式)表示行政单位的全名,格式为省份-城市-区域
province_codeSTRING省编码用于标识省份的唯一编码
province_nameSTRING省名称省份的全称
city_codeSTRING市编码用于标识城市的唯一编码
city_nameSTRING市名称城市的全称
district_codeSTRING区编码用于标识区/县/市辖区的唯一编码
district_nameSTRING区名称区/县/市辖区的全称
precisionSTRINGgeohash长度5位精确到2400米,6位精确到610米

方案实现

1.高德API获取行政区边界点

详细接口可以参考高德官网链接:https://lbs.amap.com/api/webservice/guide/api/district

2.外包矩形中心作为中心点

import java.util.ArrayList;
import java.util.List;

class Point {
    double lng; // 经度
    double lat; // 纬度

    Point(double lng, double lat) {
        this.lng = lng;
        this.lat = lat;
    }
}

public class BoundingBoxCenterCalculator {

    public static Point calculateBoundingBoxCenter(List<Point> boundaryPoints) {
        double minLng = Double.MAX_VALUE;
        double minLat = Double.MAX_VALUE;
        double maxLng = Double.MIN_VALUE;
        double maxLat = Double.MIN_VALUE;

        // 找到最小和最大经纬度值
        for (Point point : boundaryPoints) {
            if (point.lng < minLng) minLng = point.lng;
            if (point.lng > maxLng) maxLng = point.lng;
            if (point.lat < minLat) minLat = point.lat;
            if (point.lat > maxLat) maxLat = point.lat;
        }

        // 计算外接矩形的中心点
        double centerLng = (minLng + maxLng) / 2;
        double centerLat = (minLat + maxLat) / 2;

        return new Point(centerLng, centerLat);
    }

    public static void main(String[] args) {
        // 假设你已经从高德API或其他服务中获取了行政区的边界点
        List<Point> boundaryPoints = new ArrayList<>();
        // 示例点(需要替换为实际获取的边界点)
        boundaryPoints.add(new Point(116.397128, 39.916527)); // 示例点1
        boundaryPoints.add(new Point(116.406921, 39.904989)); // 示例点2
        // ... 添加更多边界点

        // 计算中心点
        Point centerPoint = calculateBoundingBoxCenter(boundaryPoints);
        System.out.println("中心点坐标: 经度=" + centerPoint.lng + ", 纬度=" + centerPoint.lat);
    }
}

3.坐标点经纬度转换为geohash

Geohash 是一种用于地理编码的算法,可以将经纬度坐标编码成一个较短的字符串,便于存储、传输和搜索。下面是一个在 Java 中将经纬度坐标转换成 Geohash 的实现。这个实现使用了一个流行的 Java 库 geohash

  1. 项目中添加 geohash 库的依赖。对于 Maven 项目,可以在 pom.xml 文件中添加以下依赖:

    <dependency>
        <groupId>ch.hsr</groupId>
        <artifactId>geohash</artifactIda>
        <version>1.3.0</version>
    </dependency>
    
  2. 使用示例:使用 GeoHash.withCharacterPrecision(centerPoint.lat, centerPoint.lng, precision); 方法将经纬度编码成 Geohash。

import ch.hsr.geohash.GeoHash;
import ch.hsr.geohash.GeoHashUtil;

public class GeoHashExample {
    public static void main(String[] args) {
        double latitude = 48.858844;  // 示例纬度
        double longitude = 2.294351;  // 示例经度

        // 设置Geohash的精度,范围从1到12,精度越高字符串越长
        int precision = 12; 

        // 计算Geohash
        GeoHash geoHash = GeoHash.withCharacterPrecision(latitude, longitude,precision));
        String geohashString = geoHash.toString();

        System.out.println("Geohash: " + geohashString);
    }
}

测试

建表语句

CREATE TABLE t_administrative_units (
    province_code VARCHAR(10) NOT NULL COMMENT '省编号,用于标识省份的唯一编号',
    province_name VARCHAR(50) NOT NULL COMMENT '省名称,省份的全称',
    city_code VARCHAR(10) NOT NULL COMMENT '市编号,用于标识城市的唯一编号',
    city_name VARCHAR(50) NOT NULL COMMENT '市名称,若为直辖市则是显示市辖区,城市的全称',
    dist_code VARCHAR(10) NOT NULL COMMENT '区编号,用于标识区/县/市辖区的唯一编号,也是主键',
    dist_name VARCHAR(50) NOT NULL COMMENT '区名称,区/县/市辖区的全称',
    full_name VARCHAR(150) GENERATED ALWAYS AS (CONCAT(province_name, city_name, dist_name)) STORED COMMENT '行政单位全名,由省名称、市名称(或市辖区)和区名称组合而成的全名',
    is_direct_city BIGINT(1) NOT NULL COMMENT '是否直辖市或特别行政区,1表示是,0表示否',
    PRIMARY KEY (dist_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='行政单位信息表';



CREATE TABLE t_geohash_to_address (
    geohash varchar(6) NOT NULL COMMENT 'geohash(长度为6位),将经纬度转换为geohash与此字段关联,用于地理位置的近似表示',
    full_name VARCHAR(150) NOT NULL COMMENT '解析结果全名(省-市-区格式),表示行政单位的全名',
    province_code VARCHAR(10) NOT NULL COMMENT '省编码,用于标识省份的唯一编码',
    province_name VARCHAR(50) NOT NULL COMMENT '省名称,省份的全称',
    city_code VARCHAR(10) NOT NULL COMMENT '市编码,用于标识城市的唯一编码',
    city_name VARCHAR(50) NOT NULL COMMENT '市名称,城市的全称',
    district_code VARCHAR(10) NOT NULL COMMENT '区编码,用于标识区/县/市辖区的唯一编码',
    district_name VARCHAR(50) NOT NULL COMMENT '区名称,区/县/市辖区的全称',
    `precision` int(2) NOT NULL COMMENT 'geohash长度,5位精确到2400米,6位精确到610米)',
    PRIMARY KEY (geohash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='geohash-行政区映射维表';

测试造数

行政区数据表

INSERT INTO t_administrative_units (province_code, province_name, city_code, city_name, dist_code, dist_name, is_direct_city) VALUES
('110000', '北京市', '110000', '北京市', '110101', '东城区', 1),
('120000', '天津市', '120000', '天津市', '120101', '和平区', 1),
('320000', '江苏省', '320100', '南京市', '320106', '鼓楼区', 0),
('320000', '江苏省', '320500', '苏州市', '320506', '吴中区', 0),
('330000', '浙江省', '330100', '杭州市', '330102', '上城区', 0),
('330000', '浙江省', '330200', '宁波市', '330203', '海曙区', 0),
('440000', '广东省', '440100', '广州市', '440106', '天河区', 0),
('440000', '广东省', '440300', '深圳市', '440304', '福田区', 0),
('500000', '重庆市', '500000', '重庆市', '500101', '万州区', 1),
('650000', '新疆维吾尔自治区', '650100', '乌鲁木齐市', '650102', '天山区', 0);

经纬度geohash-行政区维表

INSERT INTO t_geohash_to_address (geohash, full_name, province_code, province_name, city_code, city_name, district_code, district_name, `precision`) VALUES
('wx4g0c', '北京市-北京市-东城区', '110000', '北京市', '110000', '北京市', '110101', '东城区', 6),
('wtwy3r', '上海市-上海市-黄浦区', '310000', '上海市', '310000', '上海市', '310101', '黄浦区', 6),
('u4uru3', '广东省-广州市-天河区', '440000', '广东省', '440100', '广州市', '440106', '天河区', 6),
('u4uuxq', '广东省-深圳市-福田区', '440000', '广东省', '440300', '深圳市', '440304', '福田区', 6),
('wm3wxr', '浙江省-杭州市-西湖区', '330000', '浙江省', '330100', '杭州市', '330106', '西湖区', 6);

测试用例

package cn.suwg;

import ch.hsr.geohash.GeoHash;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: suwg
 * @Date: 2024/11/21
 */
public class GeoHashToAddress {


    public static Point calculateBoundingBoxCenter(List<Point> boundaryPoints) {
        double minLng = Double.MAX_VALUE;
        double minLat = Double.MAX_VALUE;
        double maxLng = Double.MIN_VALUE;
        double maxLat = Double.MIN_VALUE;

        // 找到最小和最大经纬度值
        for (Point point : boundaryPoints) {
            if (point.lng < minLng) minLng = point.lng;
            if (point.lng > maxLng) maxLng = point.lng;
            if (point.lat < minLat) minLat = point.lat;
            if (point.lat > maxLat) maxLat = point.lat;
        }

        // 计算外接矩形的中心点
        double centerLng = (minLng + maxLng) / 2;
        double centerLat = (minLat + maxLat) / 2;

        return new Point(centerLng, centerLat);
    }

    public static void main(String[] args) {
        // 假设你已经从高德API或其他服务中获取了行政区的边界点
        List<Point> boundaryPoints = new ArrayList<>();
        // 示例点(需要替换为实际获取的边界点)
        boundaryPoints.add(new Point(116.397128, 39.916527)); // 示例点1
        boundaryPoints.add(new Point(116.406921, 39.904989)); // 示例点2
        // ... 添加更多边界点

        // 计算中心点
        Point centerPoint = calculateBoundingBoxCenter(boundaryPoints);
        System.out.println("中心点坐标: 经度=" + centerPoint.lng + ", 纬度=" + centerPoint.lat);


        // 计算geoHash
        int precision = 6;
        // 计算Geohash
        GeoHash geoHash = GeoHash.withCharacterPrecision(centerPoint.lat, centerPoint.lng, precision);
        String geohashStr = geoHash.toBase32();
        System.out.println("GeoHash: " + geoHash.toBase32());
    }
}

class Point {
    double lng; // 经度
    double lat; // 纬度

    Point(double lng, double lat) {
        this.lng = lng;
        this.lat = lat;
    }
}

测试结果

image-20241121180438116

查询数据库结果

image-20241121180352103

总结

总结

​ 本文介绍了一个将经纬度坐标快速匹配到对应行政区的实现方案。该方案主要包括以下步骤:通过高德API获取所有行政区信息和边界点,将行政区数据存储到t_administrative_units(行政单位信息表),计算行政区的中心点,转成geohash编码,并记录geohash和行政区的映射关系到t_geohash_to_address,本方案适用于热力图、行车轨迹灯不要求精确位置的场景。

​ 该方案的优势在于

  • 高效性:通过GeoHash编码,将经纬度坐标转换为较短的字符串,便于存储和搜索,提高了匹配效率。
  • 准确性:使用外接矩形的中心点作为行政区的代表点,能快速计算出中心点
  • 可扩展性:方案中的表结构和代码设计都考虑到了扩展性,可以方便地添加更多的行政区或调整精度。

​ 该方案的不足在于

  • 粗粒度:针对高精度的经纬度定位行政区的需求,本方案精度不够

  • 近似统计:使用外接矩形中心点的方式,可能因行政区的形状不规则而导致中心点偏差较大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/921410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解锁业务成功:大数据和 AI 如何协作以释放战略洞察

在当今这个数据主导的时代&#xff0c;大数据与AI的协同作用对于寻求竞争优势的组织而言愈发关键。大数据以其庞大的数据量、多样化的数据类型以及高速的数据生成能力&#xff0c;为AI算法提供了丰富的原材料&#xff0c;助力其挖掘出有价值的洞见&#xff0c;推动明智决策的制…

LINUX系统编程之——环境变量

目录 环境变量 1、基本概念 2、查看环境变量的方法 三、查看PATH环境变量的內容 1&#xff09;不带路径也能运行的自己的程序 a、将自己的程序直接添加到PATH指定的路径下 b、将程序所在的路径添加到PATH环境中 四、环境变量与本地变量 1、本地变量创建 2、环境变量创…

QT:QListView实现table自定义代理

介绍 QListVIew有两种切换形式&#xff0c;QListView::IconMode和QListView::ListMode&#xff0c;通过setViewMode()进行设置切换。因为QListView可以像QTreeView一样显示树形结构&#xff0c;也可以分成多列。这次目标是将ListView的ListMode形态显示为table。使用代理&…

IDEA2023 创建SpringBoot项目(一)

一、Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。 二、快速开发 1.打开IDEA选择 File->New->Project 2、…

初级数据结构——树

目录 前言一、树的基本概念二、二叉树三、树的表示方法四、树的遍历树的代码模版五、经典例题[2236. 判断根结点是否等于子结点之和](https://leetcode.cn/problems/root-equals-sum-of-children/description/)代码题解 六、总结结语 前言 从这一期开始数据结构开始有那么一点…

Unity 编辑器下 Android 平台 Addressable 加载模型粉红色,类似材质丢失

Unity 编辑器下 Android 平台 Addressable 加载模型粉红色&#xff0c;类似材质丢失 Addressable Play Mode Script加载模式 选择 Use Existiing Build 1.Unity 切换到 PC 平台&#xff0c;执行 Addressable Build 运行&#xff0c;加载 bundle 内的预制体 显示正常 2.Unit…

视频去重工具

视频去重工具 工具截图 下载 回复&#xff1a;“0028”&#xff0c;即可自动获取

javascrip页面交互

元素的三大系列 offset系列 offset初相识 offset系列属性 作用 element.offsetParent 返回作为该元素带有定位的父级元素&#xff0c;如果父级没有定位&#xff0c;则返回body element.offsetTop 返回元素相对于有定位父元素上方的偏移量 element.offsetLeft 返回元素…

win10中使用ffmpeg和MediaMTX 推流rtsp视频

在win10上测试下ffmpeg推流rtsp视频&#xff0c;需要同时用到流媒体服务器MediaMTX 。ffmpeg推流到流媒体服务器MediaMTX &#xff0c;其他客户端从流媒体服务器拉流。 步骤如下&#xff1a; 1 下载MediaMTX github: Release v1.9.3 bluenviron/mediamtx GitHub​​​​​…

el-select 和el-tree二次封装

前言 本文章是本人在开发过程中&#xff0c;遇到使用树形数据&#xff0c;动态单选或多选的需求&#xff0c;element中没有这种组件&#xff0c;故自己封装一个&#xff0c;欢迎多多指教 开发环境&#xff1a;element-UI、vue2 组件效果 单选 多选 组件引用 <treeselec…

STM32-- keil常见报错与解决办法

调试问题 1. keil在线调试需要点击好几次运行才可以运行&#xff0c;要是直接下载程序直接就不运行。 解决&#xff1a;target里面的use microlib要勾选&#xff0c;因为使用了printf。 keil在线调试STM32&#xff0c;点三次运行才能跑到main的问题解决。 keil在线调试STM32…

RNN简单理解;为什么出现Transformer:传统RNN的问题;Attention(注意力机制)和Self-Attention(自注意力机制)区别;

目录 RNN简单理解 RNN n to n Transformer N to M LSTM 为什么出现Transformer:传统RNN的问题 信息丢失的后果 Rnn是顺序执行的效率不高:顺序执行 Attention(注意力机制)和Self-Attention(自注意力机制)区别 一、计算对象不同 二、应用场景不同 三、功能差异…

51c深度学习~合集8

我自己的原文哦~ https://blog.51cto.com/whaosoft/12491632 #patchmix 近期中南大学的几位研究者做了一项对比学习方面的工作——「Inter-Instance Similarity Modeling for Contrastive Learning」&#xff0c;主要用于解决现有对比学习方法在训练过程中忽略样本间相似关系…

Kafka:分布式消息系统的核心原理与安装部署

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

刷算法题时遇到的一些不常用但好用的API

1.需要统计数据&#xff0c;同时希望数据是排序的&#xff0c;可以使用TreeMap结构。 2.按照ASCII&#xff0c;A的ASCII值比a小。而字典排序底层也有基于ASCII&#xff0c;因此无论是字典排序还是ASCII排序&#xff0c;A都在a前面。 3.使用DecimalFormat尝试将浮点数四舍五入…

2024-11-19 kron积

若A[a11 a12; a21 a22]; B[b11 b12; b21 b22]; 则C[a11*b11 a12*b11 a21*b11 a22*b11; a11*b12 a12*b12 a21*b12 a22*b12; a11*b21 a12*b21 a21*b21 a22*b21; a11*b22 a12*b22 a21*b22 a22*b22] 用MATLAB实现 方法1&#xff1a; A [a11 a12; a21 a22]; B [b11 b12; b21 b22]…

工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程

一.背景 公司是非煤采矿业&#xff0c;核心业务是采选&#xff0c;大型设备多&#xff0c;安全风险因素多。当下政府重视安全&#xff0c;头部技术企业的安全解决方案先进但价格不低&#xff0c;作为民营企业对安全投入的成本很敏感。利用我本身所学&#xff0c;准备搭建公司的…

(7) 探索Python函数的无限可能:从递归到Lambda的奇妙之旅

欢迎进入Python编程的奇幻世界!在这个课程中,我们将一起探索编程的乐趣,通过生动有趣的方式,培养编程的逻辑思维和创造力,该课程适合有一定基础的中学及以上学生及成年人。 以下是我们课程的大纲: 【Python:趣味编程,探索未来】 目录 1. 前言2. 认识我们的“魔法咒语”…

【深度学习|目标跟踪】DeepSort 详解

DeepSort详解 1、Sort回顾2、DeepSort的状态向量3、DeepSort的外观特征4、DeepSort的track状态5、DeepSort的代价矩阵以及门控矩阵6、DeepSort的级联匹配 1、Sort回顾 查看这篇博客 2、DeepSort的状态向量 Sort中的卡尔曼滤波使用的目标的状态向量是一个7维的向量&#xff0c…

MetaGPT实现多动作Agent

异步编程学习链接 智能体 LLM观察思考行动记忆 多智能体 智能体环境SOP评审路由订阅经济 教程地址 多动作的agent的本质是react&#xff0c;这包括了think&#xff08;考虑接下来该采取啥动作&#xff09;act&#xff08;采取行动&#xff09; 在MetaGPT的examples/write_…