欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > ES 使用geo point 查询离目标地址最近的数据

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

2025/3/15 13:27:54 来源:https://blog.csdn.net/DavidSoCool/article/details/146230013  浏览:    关键词:ES 使用geo point 查询离目标地址最近的数据

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

        解决思路大致有种,使用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等。

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词