ES 使用geo point 查询离目标地址最近的数据

news2025/3/17 11:07:00

        需求描述:项目中需要通过经纬度坐标查询目标地所在的行政区。

        解决思路大致有种,使用es和mysql分别查询。   

    1、使用es进行查询

        将带有经纬度坐标的省市区数据存入es中,mappings字段使用geo point类型,索引及查询dsl如下。

        geo point文档地址:
                Geo-distance query | Elasticsearch Guide [8.6] | Elastic

                Sort search results | Elasticsearch Guide [8.6] | Elastic

        mappings结构:

PUT /sys_district
{
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 1
    }
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      },
      "parent_id": {
        "type": "long"
      },
      "name": {
        "type": "keyword"
      },
      "zipcode": {
        "type": "integer"
      },
      "pinyin": {
        "type": "keyword"
      },
      "location": {
        "type": "geo_point" // 如果用于地理坐标,可以考虑使用 geo_point 类型
      },
      "level": {
        "type": "byte" 
      },
      "sort": {
        "type": "byte"
      }
    }
  }
}

        dsl语句:

# 搜索坐标点附近的数据
GET sys_district/_search
{
  "from": 0,
  "size": 3,
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": [
        {
          "geo_distance": {
            # 半径内距离限制
            "distance": "100km",
            "location": {
                # 目的地坐标
              "lat": 34.4328,
              "lon": 115.88
            }
          }
        },
        {
          "term": {
            "level": "3"
          }
        }
      ]
    }
  },
# 排序
  "sort" : [
    {
      "_geo_distance" : {
        "location" : {
          "lat" :  34.4328,
          "lon" :115.88
        },
        "order" : "asc",
        "unit" : "km"
      }
    }
  ]
}

        获取举例最近的排序不能漏了

 2、使用mysql进行查询

        将带有经纬度坐标的省市区数据存入mysql中,使用mysql直接计算,表结构及查询sql如下。

        表结构:

CREATE TABLE `sys_district` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
	`parent_id` INT(10) UNSIGNED NOT NULL COMMENT '父栏目',
	`name` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
	`zipcode` INT(10) UNSIGNED NOT NULL DEFAULT '0',
	`pinyin` VARCHAR(100) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
	`lng` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
	`lat` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
	`level` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
	`sort` TINYINT(3) UNSIGNED NOT NULL DEFAULT '50' COMMENT '排序',
	`location` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
	PRIMARY KEY (`id`) USING BTREE
)
COMMENT='(公共)区域数据'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

        查询sql: 

SELECT * FROM sys_district WHERE ABS(lat - 34.4328) + ABS(lng - 115.88) = (SELECT MIN(ABS(lng - 115.88) + ABS(lat - 34.4328)) FROM sys_district ) LIMIT 1;

        使用mysql计算可优化的地方在于,新版本mysql提供了空间几何字段类型POINT,优化后新表结构如下。

CREATE TABLE `sys_district` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
	`parent_id` INT(10) UNSIGNED NOT NULL COMMENT '父栏目',
	`name` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
	`zipcode` INT(10) UNSIGNED NOT NULL DEFAULT '0',
	`pinyin` VARCHAR(100) NOT NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
	`lng` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
	`lat` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
	`geom` POINT NOT NULL COMMENT 'geo',
	`level` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
	`sort` TINYINT(3) UNSIGNED NOT NULL DEFAULT '50' COMMENT '排序',
	`location` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
	PRIMARY KEY (`id`) USING BTREE,
	SPATIAL INDEX `geom` (`geom`)
)
COMMENT='(公共)区域数据'
COLLATE='utf8mb3_general_ci'
ENGINE=InnoDB
;

        字段设置:



ALTER TABLE `sys_district`
	ADD COLUMN `geom` POINT NULL AFTER `lat`;


UPDATE sys_district SET geom = ST_PointFromText(CONCAT('POINT(', lng, ' ', lat, ')')) ;


ALTER TABLE sys_district ADD SPATIAL INDEX(geom);

        查询sql如下:

        ST_PointFromText(CONCAT('POINT(', lng, ' ', lat, ')')) 将表中的经度和纬度转换为几何点。

  ST_Distance_Sphere(geom, ST_PointFromText(CONCAT('POINT(', 120.15, ' ', 30.28, ')'))) 计算每个点与目标点之间的距离(单位为米)。

  ORDER BY distance 按距离从小到大排序

SELECT id, name, lng, lat,
       ST_Distance_Sphere(geom, ST_PointFromText(CONCAT('POINT(', 120.15, ' ', 30.28, ')'))) AS distance
FROM sys_district
ORDER BY distance
LIMIT 3;

        3、其他方式

        如果带查询的数据项不变化,类似于行政区划的坐标,还可以把这些数据加载到内存中进行计算。

        3.1 Java-使用 Haversine 公式来计算(不依赖三方库)

        创建表示位置的类

public class Location {
    private double lon;
    private double lat;

    public Location(double lon, double double lat) {
        this.lon = lon;
        this.lat = lat;
    }

    // Getter 和 Setter 方法
    
}

        使用 Haversine 公式计算两点间的距离

public class DistanceCalculator {

    private static final int EARTH_RADIUS = 6371; // 地球半径,单位为公里

    /**
     * 计算两个经纬度点之间的距离
     */
    public static double calculateDistance(Location loc1, Location loc2) {
        double lat1 = Math.toRadians(loc1.getLat());
        double lon1 = Math.toRadians(loc1.getLon());
        double lat2 = Math.toRadians(loc2.getLat());
        double lon2 = Math.toRadians(loc2.getLon());

        double dlat = lat2 - lat1;
        double dlon = lon2 - lon1;

        double a = Math.sin(dlat / 2) * Math.sin(dlat / 2) +
                   Math.cos(lat1) * Math.cos(lat2) *
                   Math.sin(dlon / 2) * Math.sin(dlon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        return EARTH_RADIUS * c; // 返回单位为公里
    }
}

        查找最近的数据点

public class NearestLocationFinder {

    public static LocationData findNearestLocation(List<LocationData> locations, Location targetLocation) {
        LocationData nearest = null;
        double minDistance = Double.MAX_VALUE;

        for (LocationData location : locations) {
            Location currentLocation = new Location(location.getLocation().getLon(), location.getLocation().getLat());
            double distance = DistanceCalculator.calculateDistance(currentLocation, targetLocation);

            if (distance < minDistance) {
                minDistance = distance;
                nearest = location;
            }
        }

        return nearest;
    }
}

        调用方法

public class Main {
    public static void main(String[] args) {
        // 已加载所有的位置数据
        List<LocationData> locations = loadData();

        // 输入的经纬度
        Location targetLocation = new Location(115.65, 34.43);

        // 查找最近的位置
        LocationData nearest = NearestLocationFinder.findNearestLocation(locations, targetLocation);

        System.out.println("最近的位置是: " + nearest.getName());
    }

    // 加载数据

    private static List<LocationData> loadData() {
        
        return new ArrayList<>();
    }
}

        4、Java-使用JTS STRtree(依赖三方库)

        maven依赖

<dependency>
    <groupId>org.locationtech.jts</groupId>
    <artifactId>jts-core</artifactId>
    <version>1.18.2</version>
</dependency>

         调用方法

public class NearestPointFinder {

    public static void main(String[] args) {
        // 创建一个包含所有位置信息的列表
        List<LocationData> locations = loadData();

        // 输入的经纬度
        double lon = 115.65, lat = 34.43;

        // 使用JTS的STRtree加速查询
        STRtree tree = new STRtree();
        GeometryFactory geometryFactory = new GeometryFactory();

        for (LocationData location : locations) {
            Point point = geometryFactory.createPoint(new Coordinate(location.getLocation().getLon(), location.getLocation().getLat()));
            tree.insert(point.getEnvelopeInternal(), location);
        }

        Point targetPoint = geometryFactory.createPoint(new Coordinate(lon, lat));
        LocationData nearest = (LocationData) tree.nearestNeighbour(targetPoint.getEnvelopeInternal(), null);

        System.out.println("最近的位置是: " + nearest.getName());
    }

    private static List<LocationData> loadData() {
        // 加载位置数据
        return new ArrayList<>();
    }
}

        还有其他的一些三方库:H3 by Uber、GeoTools、Spatial4j等。

总结:没有最好的,只有最适合的,按需设计。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

本地部署OpenManus及原理介绍

概述&#xff1a; 最近Minaus特别火&#xff0c;随后开源社区就有项目尝试复刻Minaus&#xff0c;项目名称为OpenManus&#xff0c;原理是用推理模型为决策者&#xff0c;将我们输入的问题进行分解后调用本地工具执行。 OpenManus安装&#xff1a; 本人在Ubuntu桌面版本上安装…

高效手机检测:视觉分析技术的优势

在当今社会&#xff0c;手机已成为人们日常生活和工作中不可或缺的工具。然而&#xff0c;在某些特定场合&#xff0c;如考场、工作场所等&#xff0c;手机的使用却可能带来负面影响。因此&#xff0c;如何有效监测和防止在这些场合偷用手机的行为&#xff0c;成为了一个亟待解…

Spring Boot配置类原理、Spring Boot核心机制理解,以及实现自动装置的底层原理

目的:从底层源码角度分析 Spring Boot 配置类以及自动装载的底层原理 文章目录 1. Spring Boot 配置类实现自动装载1.1 @Configuration注解1.2 @Configuration 注解完成 bean 注入流程图1.3 @ConfigurationProperties注解赋值2. Spring Boot的核心机制:自动装配2.1 @SpringBo…

01-Canvas-使用fabric初始

fabric官网&#xff1a; https://fabric5.fabricjs.com/demos/ 创建画布并绘制 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…

树莓派 连接 PlutoSDR 教程

在树莓派5上安装PlutoSDR&#xff08;ADALM-Pluto&#xff09;的驱动程序&#xff0c;主要需要安装相关的库和工具&#xff0c;以便与PlutoSDR通信&#xff0c;比如libiio和libad9361&#xff0c;并确保系统能够识别设备。由于树莓派5运行的是基于Linux的系统&#xff08;通常是…

Git使用(二)--如何配置 GitHub 远程仓库及本地 Git 环境

在日常的开发过程中&#xff0c;使用版本控制工具 Git 是一个非常重要的技能&#xff0c;特别是对于管理和协作开发。通过 GitHub&#xff0c;我们可以轻松地进行代码版本管理和共享。这篇博客将带您一步步学习如何配置 Git 环境并将本地仓库与 GitHub 远程仓库连接起来。 一、…

在Pycharm配置conda虚拟环境的Python解释器

〇、前言 今天在配置python解释器时遇到了这样的问题 经过一下午自行摸索、上网搜寻后&#xff0c;终于找到的解决的方案&#xff0c;遂将该方法简要的记录下来&#xff0c;以备后用&#xff0c;并希望能帮助到有同样问题或需求的朋友:) 我所使用的软件的版本如下&#xff0c;假…

零基础keil:设置注释快捷键

1.打开快捷键设置&#xff1a; 在Keil中&#xff0c;选择菜单栏中的“Settings”&#xff0c;然后选择“Shortcuts”来打开快捷键设置界面。 2.选择注释命令&#xff1a; 在快捷键设置界面中&#xff0c;找到与注释相关的命令&#xff0c;如“Comment Selection”&#xff0…

Java中关于Optional的 orElse 操作,以及 orElse 与 orElseGet 的区别

文章目录 1. 大概说明2. 详细分析2.1 .orElse 操作2.2 .orElse 的作用&#xff1a;避免空指针异常2.3 为什么要用&#xff1f;2.4 orElseGet如何使用2.5 orElse和orElseGet的区别 1. 大概说明 这篇文章的目的是为了说明&#xff1a; orElse 如何使用orElseGet 如何使用两者的…

TCP/IP协议中三次握手(Three-way Handshake)与四次挥手(Four-way Wave)

TCP/IP协议中三次握手&#xff08;Three-way Handshake&#xff09;与四次挥手&#xff08;Four-way Wave&#xff09; 一、TCP三次握手&#xff08;Three-way Handshake&#xff09;二、TCP四次挥手&#xff08;Four-way Wave&#xff09;三、常见问题解答总结为什么三次握手不…

python学智能算法(八)|决策树

【1】引言 前序学习进程中&#xff0c;已经对KNN邻近算法有了探索&#xff0c;相关文章链接为&#xff1a; python学智能算法&#xff08;七&#xff09;|KNN邻近算法-CSDN博客 但KNN邻近算法有一个特点是&#xff1a;它在分类的时候&#xff0c;不能知晓每个类别内事物的具…

【QT:控件】

目录 控件状态&#xff1a;​编辑 geometry : window frame windowlcon: qrc机制 qrc的使用方式&#xff1a; window opacity cursor font: ToolTip focusPolicy: styleSheet: 按钮类控件&#xff1a; PushButton: 给按钮添加图标&#xff1a; 给按钮添加快捷键…

Python(最新版)集成开发环境PyCharm下载安装详细教程

Python 下载和安装 1.进入Python官网 Download Python | Python.org&#xff0c;点击Downloads&#xff0c;这里以Windows为例 2.选择下载Python 3.13.2 Windows 64位的版本。注意&#xff1a;不能在Windows 7 或更早的版本上使用。 3.打开文件&#xff0c;会自动出现安装界…

uniapp 实现的步进指示器组件

采用 uniapp 实现的一款步进指示器组件&#xff0c;展示业务步骤进度等内容&#xff0c;对外提供“前进”、“后退”方法&#xff0c;让用户可高度自定义所需交互&#xff0c;适配 web、H5、微信小程序&#xff08;其他平台小程序未测试过&#xff0c;可自行尝试&#xff09; 可…

大模型-提示词调优

什么是提示词 提示词&#xff08;Prompt&#xff09;在大模型应用中扮演着关键角色&#xff0c;它是用户输入给模型的一段文本指令 。简单来说&#xff0c;就是我们向大模型提出问题、请求或描述任务时所使用的文字内容。例如&#xff0c;当我们想让模型写一篇关于春天的散文&a…

继承知识点—详细

一&#xff1a;普通写法 package extend_;public class Extends01 {public static void main(String[] args) {Pubil pubil new Pubil();pubil.name"小明";pubil.age18;pubil.testing();pubil.setScore(60);pubil.showInfo();System.out.println("-----------…

设备管理VTY(Telnet、SSH)

实验目的&#xff1a;物理机远程VTY通过telnet协议登录AR1,ssh协议登录AR2和sw 注意配置Cloud1&#xff1a; 注意&#xff01;&#xff01;博主的物理机VMnet8--IP&#xff1a;192.168.160.1&#xff0c;所以AR1路由0/0/0端口才添加IP&#xff1a;192.168.160.3&#xff0c;每个…

Linux 中 Git 使用指南:从零开始掌握版本控制

目录 1. 什么是 Git&#xff1f; Git 的核心功能&#xff1a; 2. Git 的安装 Ubuntu/Debian 系统&#xff1a; 验证安装&#xff1a; 3.gitee库 4. Git 的首次配置 配置用户名和邮箱&#xff1a; 查看配置&#xff1a; 5. Git 的基本使用 初始化仓库 添加文件到暂存区…

CSS -属性值的计算过程

目录 一、抛出两个问题1.如果我们学过优先级关系&#xff0c;那么请思考如下样式为何会生效2.如果我们学习过继承&#xff0c;那么可以知道color是可以被子元素继承使用的&#xff0c;那么请思考下述情景为何不生效 二、属性值计算过程1.确定声明值2.层叠冲突3.使用继承4.使用默…

百度贴吧IP和ID是什么意思?怎么查看

在百度贴吧这一充满活力的网络社区中&#xff0c;IP和ID是两个频繁出现的概念。它们各自承载着不同的意义和作用&#xff0c;对于贴吧用户而言&#xff0c;了解这两个概念有助于更好地参与社区互动、保护个人隐私以及维护社区秩序。本文将详细解析百度贴吧中IP和ID的含义&#…