掘金 后端 ( ) • 2024-05-06 11:44

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


简介

放在前面,怕有些人看不下去错过,整合了身份证/手机号/IP/GPS解析的工具将在下一篇文章推出,并将全部数据和代码放在GithubGitee上。

接着前一篇的的身份证/手机号解析服务,其四者难度对比是这样的,身份证 < 手机号 < IP < GPS。也比较好理解,毕竟是从点到线再到面,难度可想而知。GPS用词可能不当,我还是解释一下,这里的意思是指通过经纬度解析到行政区划/地理信息,也可以讲是逆地理编码。

身份证和手机号都相对简单,数据本身不大,而且规则也比较清晰,所以比较容易。相比之下IPGPS的难度就大了太多了,IPv4v6已经是数据量的极大扩展,而且这些数据很难获取,对于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数据接口调用服务平台

IP地址API接口 - useragent info

教书先生API - 提供免费接口调用平台

接口大全-免费API,收集所有免费的API

超百个免费api接口,分享给你「建议收藏」 - 全栈程序员必看

文档 | 用户指南 | IPPlus

Just a moment...

纯真网络,中国历史最悠久的IP地理位置库

狮子的魂/ip2region

GPS解析

简介已经说明了GPS解析到地区是一件很难的事情,需要图算法的应用。而且还需要一些地理知识,我也是了解一块才知道,各种地球坐标系、球面距离算法之类的等等。

逆地理编码在线API(精度高、丰富数据、普通用户限制、付费)

地理/逆地理编码-基础 API 文档-开发指南-Web服务 API | 高德地图API

天地图·在线地图

如果不是https://map.tianditu.gov.cn/此域名后缀是gov,我真没想到这是政府网站,既然这样,果断抛弃商业的百度地图、高德地图、腾讯地图。

使用在线API的好处就是简单,数据非常丰富,但缺点就是普通服务有限制,升级为专业付费用户要钱呐💰

另外使用别人的服务总是不可控的,毕竟有时候连自己都管不住,怎么还能奢求管得了别人,所以能自主还是尽量自主。

一些离线方案

判断经纬度在省市区县乡镇内(复杂多边形内) -- 基于空间查询算法_给定经纬度查询地名的算法实现-CSDN博客

GitHub - hsp8712/addrparser: Tool for parsing longitude/latitude to region info in china 根据经纬度解析省市区信息工具包

基于阿里地图围栏数据,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.geodesygeodesy的方法计算,此包提供了更多坐标系的选择,更加灵活丰富。

/**
 * <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解析的工具将在下一篇文章推出,并将全部数据和代码放在GithubGitee上。

虽然有的就只是用人家的工具,我还是腆着脸把它放出来了😂

简单的事情最容易掉以轻心,要认真做,困难的事情最容易放弃,要竭尽全力去做。

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview