个人博客:无奈何杨(wnhyang)
个人语雀:wnhyang
共享语雀:在线知识共享
Github:wnhyang - Overview
简介
放在前面,怕有些人看不下去错过,整合了身份证/手机号/IP
/GPS
解析的工具将在下一篇文章推出,并将全部数据和代码放在Github
和Gitee
上。
接着前一篇的的身份证/手机号解析服务,其四者难度对比是这样的,身份证 < 手机号 < IP
< GPS
。也比较好理解,毕竟是从点到线再到面,难度可想而知。GPS
用词可能不当,我还是解释一下,这里的意思是指通过经纬度解析到行政区划/地理信息,也可以讲是逆地理编码。
身份证和手机号都相对简单,数据本身不大,而且规则也比较清晰,所以比较容易。相比之下IP
和GPS
的难度就大了太多了,IP
从v4
到v6
已经是数据量的极大扩展,而且这些数据很难获取,对于ISP
拥有的IP
范围匹配又是很大的难题,是需要算法设计的。好像我一直都是这样,简单的事做着没意思,困难的事情又不知道怎么做,还没开始就准备放弃了。就跟打游戏一样,碾压式的赢没有快感,被碾压的输又没有乐趣,只有均式相持拼尽全力的赢才是畅快。回到正题,身份证和手机都是有规律的,不管是数据的管理和搜素算法都可以自己做。而对于IP
,首先数据量就比较大,数据存储、读取、检索都是考虑的点。这还只是谈到了IP
解析,GPS
的解析就更复杂了。把IP
数据比做标尺上的刻度的话,GPS
数据就是组成标尺分子原子,只是个小小比喻不用在意😂毕竟数据量从有限的线升级为无限的面,而且经纬度是针对的是地球这个不规则的球体,人类又将不规则的大陆海洋划分为更多密密麻麻的不规则多边形,所有的不规则多边形又是由众多经纬度的点连线划分出来的,想想就知道有多复杂了。
IP解析
首先就要谈谈这个IP解析,真的搞的我头都要炸了。
历程,1、为了简单方便,直接寻找性能和准确性都不错的api
调用;2、发现这些api
要么有限制,要么要认证,数据还不是很满意,而且性能不能达到我的要求(30ms
以内);3、发现一些免费有趣的api
,开始跑偏,玩了起来;4、发现狮子的魂/ip2region这个项目,其提供了IP
数据存储和检索的离线解析方案,由此想尝试自己的离线IP
解析;5、数据从哪里搞呢,本身狮子的魂/ip2region项目中已经提供了一份数据,但数据比较简单,不大够用,其实项目已经提供了数据修改更新的方法,但认为手动补充有比较麻烦,所以又琢磨起数据源。
总是一直在折腾,最终定一下一个方案,基于狮子的魂/ip2region项目,在原有项目数据基础上补充纯真网络,中国历史最悠久的IP地理位置库的数据,来做离线IP
解析。
补充:下面是整个历程中探索链接,我真的有一个个IP
解析API
接口测试,只不过效果不满意罢了。
而且过程中一些API
接口网站真不错,挺有意思的。ipinfo.io这个国外的IP网站提供有IP
段的数据,免费可下载。
各种好用免费IP类API 接口大全分享 - APISpace
- IP-API:提供 IP 地址相关信息查询的 API,包括国家、城市、经纬度、时区等。
- ipstack:提供全球 IP 地址位置信息的 API,包括国家、城市、地区、邮政编码等。
- ipinfo.io:提供全球 IP 地址相关信息的 API,包括国家、城市、运营商等。
- freegeoip.app:提供免费的 IP 地理位置查询 API,返回国家、城市、经纬度等信息。
- IPify:提供简单易用的 IP 地址查询 API,返回 IP 地址、地理位置和 ASN 等信息。
- ipdata:提供全球 IP 地址相关信息的 API,包括国家、城市、邮编、经纬度等。
获取IP地址信息的API合集_ip地址查询api-CSDN博客
韩小韩Web API接口站 - 免费Web API数据接口调用服务平台
超百个免费api接口,分享给你「建议收藏」 - 全栈程序员必看
GPS解析
简介已经说明了GPS
解析到地区是一件很难的事情,需要图算法的应用。而且还需要一些地理知识,我也是了解一块才知道,各种地球坐标系、球面距离算法之类的等等。
逆地理编码在线API(精度高、丰富数据、普通用户限制、付费)
地理/逆地理编码-基础 API 文档-开发指南-Web服务 API | 高德地图API
如果不是https://map.tianditu.gov.cn/此域名后缀是gov
,我真没想到这是政府网站,既然这样,果断抛弃商业的百度地图、高德地图、腾讯地图。
使用在线API
的好处就是简单,数据非常丰富,但缺点就是普通服务有限制,升级为专业付费用户要钱呐💰
另外使用别人的服务总是不可控的,毕竟有时候连自己都管不住,怎么还能奢求管得了别人,所以能自主还是尽量自主。
一些离线方案
判断经纬度在省市区县乡镇内(复杂多边形内) – 基于空间查询算法_给定经纬度查询地名的算法实现-CSDN博客
基于阿里地图围栏数据,Java离线经纬度逆地址解析省份城市_java 离线经纬度翻译区划-CSDN博客
基于高德离线地图数据实现GPS经纬度逆解析城市信息_地址描述转经纬度 离线-CSDN博客
GitHub - deng0515001/lnglat2Geo: 经纬度转省市区县乡镇离线包,采用空间查询算法,速度快(单线程5w次/s),省市区县100%准确率。
以上文章我也都没看全,实在是无能为力,但我试了lnglat2Geo
这个项目,好像效果也不是很好,内存消耗极大,速度也不行。最终也是放弃了这里的方案。
距离计算,比较择优(脱离区划、数据灵活、离线)
复杂的不规则多边形区域搜索算法我是很难搞明白了,但我有简单的笨招啊!
上图展示了区域划分和一些城市的位置,如果有了这些城市的经纬度信息,那么就存在通过这些数据中搜索经纬度到城市的方法,最终不可能完全匹配,但是可以通过计算距离,得到最近的城市,最后以最近的城市返回数据。
此方法往大了讲是将二维图算法转为了有限的线形搜索算法。从方法介绍上可以了解,此方法以直线距离为准,已经脱离区域划分。
此方法实际上是将地图划分为非常多的点,这些点就是主要的城市、区县的中心经纬度,由这些点向外画圈,计算的就是需要解析的经纬度到这些点的距离,然后以最近的点返回。当然不能是将所有的点依次计算比较,所以首先要做的是将这些点分组,最简单的分组当然是将经纬度四舍五入拼接作为key
,这些点集合作为value
,然后再与这些点集合依次计算比较得到结果。当然还可以优化。
这种方式看似很笨,但数据是可自由更改添加的,那么密集的点将覆盖整个平面,虽然数据量有所增加,但准确性会更有保障。
以下是实现此方法的要素:
数据来源,可以是这里的中国城市坐标(最全最完整) - 岁月淡忘了谁 - 博客园,当然还可以是开放的地图平台,手动一个个搜集汇聚起来。
依赖的球面距离计算公式,参考Calculate Distance by Latitude and Longitude using Java,方法中单位可选英里(M)、千米(K)、海里(N)。当然还可以使用com.geodesy
的geodesy
的方法计算,此包提供了更多坐标系的选择,更加灵活丰富。
/**
* <a href="https://www.geodatasource.com/developers/java">...</a>
* 经纬度距离计算
*
* @author wnhyang
* @date 2024/4/28
**/
public class DistanceCalculator {
public static void main(String[] args) throws java.lang.Exception {
System.out.println(distance(-96.80322, 32.9697, -98.53506, 29.46786, "M") + " Miles\n");
System.out.println(distance(-96.80322, 32.9697, -98.53506, 29.46786, "K") + " Kilometers\n");
System.out.println(distance(-96.80322, 32.9697, -98.53506, 29.46786, "N") + " Nautical Miles\n");
}
/**
* 计算两点之间的距离
*
* @param lon1 经度1
* @param lat1 纬度1
* @param lon2 经度2
* @param lat2 纬度2
* @return 距离 单位英里
*/
public static double distance(double lon1, double lat1, double lon2, double lat2) {
return distance(lon1, lat1, lon2, lat2, "M");
}
/**
* 计算两点之间的距离
*
* @param lon1 经度1
* @param lat1 纬度1
* @param lon2 经度2
* @param lat2 纬度2
* @param unit 单位
* @return 距离
*/
public static double distance(double lon1, double lat1, double lon2, double lat2, String unit) {
if ((lat1 == lat2) && (lon1 == lon2)) {
return 0;
} else {
double theta = lon1 - lon2;
double dist = Math.sin(Math.toRadians(lat1)) * Math.sin(Math.toRadians(lat2)) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.cos(Math.toRadians(theta));
dist = Math.acos(dist);
dist = Math.toDegrees(dist);
dist = dist * 60 * 1.1515;
if ("K".equals(unit)) {
dist = dist * 1.609344;
} else if ("N".equals(unit)) {
dist = dist * 0.8684;
}
return (dist);
}
}
}
最后预告一下
整合了身份证/手机号/IP
/GPS
解析的工具将在下一篇文章推出,并将全部数据和代码放在Github
和Gitee
上。
虽然有的就只是用人家的工具,我还是腆着脸把它放出来了😂
简单的事情最容易掉以轻心,要认真做,困难的事情最容易放弃,要竭尽全力去做。
写在最后
拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。
个人博客:无奈何杨(wnhyang)
个人语雀:wnhyang
共享语雀:在线知识共享
Github:wnhyang - Overview