1. 程式人生 > >Java+MySQL實現附近功能

Java+MySQL實現附近功能

其實對於那種地理位置不會變的兩個主體之間的距離,最好是直接將結果靜態化。也就是直接寫死在配置裡。

比如,找自己家附近的地鐵站。

這種情況下,一般而言“家”這個主體是不會輕易“跑來跑去”的。每次查詢都計算一次距離沒什麼意義。最好是直接將距離持久化後直接查詢。

另一種情況:

獲取APP使用者所在位置附近的地鐵站

這種情況下,使用者的地理位置是變動的。所以每次都得實時計算實際距離。

思路

將地球當做一個標準的球體,使用球面距離公式來計算球面兩點間大圓的弧長。

  • 球面距離
public static double getDistance2
(double long1, double lat1, double long2, double lat2) { lat1 = rad(lat1); lat2 = rad(lat2); double a = lat1 - lat2; double b = rad(long1 - long2); double sa2 = Math.sin(a / 2.0); double sb2 = Math.sin(b / 2.0); return 2 * EARTH_MEAN_RADIUS_KM * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2)); }

知道兩點之間的經緯度就可以。

當然,這種計算不得不放在資料庫裡,然後根據距離排序返回。將上面的公式帶入到SQL裡就可以。

附近地鐵站示例

  • 建地鐵站示例表
CREATE TABLE station
(
  id   INT AUTO_INCREMENT    PRIMARY KEY,
  name VARCHAR(20) NULL  COMMENT '地鐵站名',
  lng  DOUBLE      NULL  COMMENT '經度',
  lat  DOUBLE      NULL  COMMENT '維度'
);
  • SQL示例
SET @targetLat = 31.175702
;
SET @targetLng = 121.519095; SELECT s.id , s.name , s.lng , s.lat , ROUND( 6378.138 * 2 * ASIN( SQRT( POW( SIN( ( @targetLat * PI() / 180 - s.lat * PI() / 180 ) / 2 ) , 2 ) + COS( @targetLat * PI( ) / 180 ) * COS( s.lat * PI( ) / 180 ) * POW( SIN( ( @targetLng * PI() / 180 - s.lng * PI() / 180 ) / 2 ) , 2 ) ) ) * 1000 ) AS distance FROM station s ORDER BY distance ASC , s.id LIMIT 20;

其中的targetLattargetLng 就是使用者的地理位置。

這樣的確可以達到目的。但是,這是對所有資料先計算了一次和使用者的距離後再排序。

地鐵站的數量太大的時候這種操作可就不太優雅了。不僅不夠優雅,而且效率是很嚇人的。

優化

其實,可以在計算距離之前就將很多資料先過濾掉。

沒必要在 計算上海地鐵站距離的時候將美國的地鐵站距離也計算一遍吧。

這在大多數應用中都可以先將一些不需要的資料過濾掉。

比如在資料是區分城市的情況下就可以將SQL改為下面這樣:

SET @targetLat = 31.175702;
SET @targetLng = 121.519095;
SET @cityId=605;
SELECT
    s.id ,
    s.name ,
    s.lng ,
    s.lat ,
    ROUND(
        6378.138 * 2 * ASIN(
            SQRT(
                POW(
                    SIN( ( @targetLat * PI() / 180 - s.lat * PI() / 180 ) / 2 ) , 2 )
                +
                COS( @targetLat * PI( ) / 180 ) * COS( s.lat * PI( ) / 180 )
                * POW( SIN( ( @targetLng * PI() / 180 - s.lng * PI() / 180 ) / 2 ) , 2 )
            )
        ) * 1000
    ) AS distance
FROM station s
where [email protected] # 先將待計算的資料過濾的一部分
ORDER BY distance ASC , s.id
LIMIT 20;

上面的改進就是先將待計算的資料在計算之前就剔除大部分。找一個長沙地鐵站,沒有必要在上海先找一遍吧。

當然,這種情況比較特殊一點,因為你事先能知道使用者所處的城市。

另一種改進就是:

以使用者所在位置為圓心,畫一個半徑為R的圓,然後反推出這個圓圈的外接四邊形的經緯度範圍。在計算距離之前先將外接四邊形經經緯度之外的資料過濾掉。

指定一個理想的半徑R,先過濾掉不可能符合條件的資料。

  • 反推外接四邊形範圍
/**
 * 獲取距離指定經緯度的點{@code radius} KM 的外接四邊形(嚴格來說應該是外接立方體)四個頂點的經緯度
 *
 * @param lng    經度
 * @param lat    緯度
 * @param radius 半徑,單位:KM
 * @return <lng1,lng2,lat1,lat2>
 */
public static Tuple4<Double> calcBoxByDistFromPt(double lng, double lat, double radius) {
    SpatialContext context = SpatialContext.GEO;
    Rectangle rectangle = context.getDistCalc()//
            .calcBoxByDistFromPt(//
                    context.makePoint(lng, lat), //
                    radius * com.spatial4j.core.distance.DistanceUtils.KM_TO_DEG, context, null//
            );
    return new Tuple4<>(rectangle.getMinX(), rectangle.getMaxX(), rectangle.getMinY(), rectangle.getMaxY());
}

這裡用到的工具類maven座標如下:

<dependency>
    <groupId>com.spatial4j</groupId>
    <artifactId>spatial4j</artifactId>
    <version>0.5</version>
</dependency>

此時的SQL可以改成這樣:

SET @targetLat = 31.175702;
SET @targetLng = 121.519095;
SELECT
    s.id ,
    s.name ,
    s.lng ,
    s.lat ,
    ROUND(
        6378.138 * 2 * ASIN(
            SQRT(
                POW(
                    SIN( ( @targetLat * PI() / 180 - s.lat * PI() / 180 ) / 2 ) , 2 )
                +
                COS( @targetLat * PI( ) / 180 ) * COS( s.lat * PI( ) / 180 )
                * POW( SIN( ( @targetLng * PI() / 180 - s.lng * PI() / 180 ) / 2 ) , 2 )
            )
        ) * 1000
    ) AS distance
FROM station s
WHERE
    ( s.lng BETWEEN ${lng1} AND ${lng2} )
    AND ( s.lat BETWEEN ${lat1} AND ${lat2} )
ORDER BY distance ASC , s.id
LIMIT 20;

上面的 lng1,lng2,lat1,lat2 就是外接四邊形的範圍。

引用資料

示例原始碼

相關推薦

Java+MySQL實現附近功能

思路 優化 引用資料 示例原始碼 其實對於那種地理位置不會變的兩個主體之間的距離,最好是直接將結果靜態化。也就是直接寫死在配置裡。 比如,找自己家附近的地鐵站。 這種情況下,一般而言“家”這個主體是不會輕易“跑來跑去”的。每次查詢都計算

SpringBoot(五)Java基於MySQL實現附近的人

“附近的人”這個功能估計都不陌生,與之類似的功能最開始是在各大地圖應用上接觸過,比如搜附近的電影院,附近的超市等等。然而真正讓附近的人火遍大江南北的應該是微信"附近的人"這個功能,記得微信剛出的時候,坊間還有一句"寂寞女聊玩微信,寂寞男人搜附近"的說法。 v準備工作 建立測試資料庫

mysql實現row_number() 功能

分享圖片 ffffff where rom ESS image 排名 分享 number 查詢test表按group_id分組取sort_id前100個 SELECT id AS ‘原數據ID‘,group_id AS ‘分組ID‘,sort_id AS ‘排序條件‘,n

Java mysql 實現JDBC百萬級數據插入

需要 ransac 情況 必須 nod table 導入 cut space 因為公司項目需要做一個excle快速導入到mysql功能,之前已經解決Java讀取excle文件,但是因為文件有100w+的數據,插入mysql數據庫很慢,1小時10w條,必須要做優化,後面寫了批

學生資訊管理系統--(Java+MySQL實現

基於Java swing+MySQL實現學生資訊管理系統:主要實現JDBC對學生資訊進行增刪改查,應付一般課設足矣,分享給大家。(由於篇幅原因,程式碼未全部列出,如有需要留下郵箱) 1、開發環境:jdk7+MySQL5+win7 程式碼結構:model-dao

Java Quartz實現定時功能

     功能描述:在開發程式中很多時候會出現希望在某個時間點、一段時間後執行某個動作,此時即需要實現定時功能,也就是希望程式能夠自己進行監督,從而達到在希望的時間觸發相應的事件。 一、在maven中加入Quartz的依賴 <dependencies>

java map實現排序功能

public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("2", "ccccc"); m

Java+MySQL實現學生資訊管理系統

基於Java swing+MySQL實現學生資訊管理系統:主要實現JDBC對學生資訊進行增刪改查,應付一般課設足矣,分享給大家。 原始碼: https://github.com/ZhuangM/student.git 1、 開發環境:jdk7+MySQL5+win7

spring+springmvc+mybatis+mysql實現登入功能(上)

注:classpath 指的為target資料夾,classpath*為有多個classpath時使用。 1.在idea中建立maven 工程。 具體過程:File -》new project -》選擇maven  勾選create from archetype,選中以w

mysql實現nextVal功能

首先建立表: CREATE TABLE `sys_sequence` ( `NAME` varchar(50) NOT NULL, `CURRENT_VALUE` int(11) NOT NULL DEFAULT '0', `INCREMENT` int

java程式碼實現抽獎功能

package com.aa; import java.util.LinkedList; import java.util.List; public class GetGift {     // 獎品倉庫     private List<Gift> gifts

java 實現微信搜尋附近功能

一、需求:使用者訪問頁面時獲取使用者的經緯度,傳輸至後端,和後端文章表中的經緯度進行匹配,若存在當前使用者經緯度附近的文章,則返回該文章 二、資料庫表設計(簡要): 1.文章表:article 2欄

java實現爬蟲功能

ack 訪問 base aid for tail tor obj 執行 /** * 爬取新聞信息,封裝成實體bean */public class GetNews { public List<News> getNews() { // 存儲新聞對象 List&

android-servlet-mysql實現登錄註冊功能

final 選擇 alt sta 姓名 delete byte[] dsta pstmt 安卓項目圖: 安卓端Get請求服務端登錄代碼: import java.io.BufferedReader;import java.io.InputStream;import

[實操筆記]MySQL主從同步功能實現

就會 class tails 修改 高可用性 innodb leg 讀寫 mil 寫在前邊: 這兩天來了個需求,配置部署兩臺服務器的MySQL數據同步,折騰了兩天查了很多相關資料,一直連不上,後來發現其實是數據庫授權的ip有問題,我們用的服務器是機房中的虛擬機加上反向代理出

Java Mail 實現第三方郵件發送功能

string .class 郵件 AD get als protocol subject fall   1 創建一個用於發送郵件的類 1 package com.latiny.service; 2 3 import java.io.IOExce

php+mysql實現英漢查詢詞典的功能

php mysql 查詢 詞典 1.建立數據庫 create database worddb; 2.創建表 create table words( id int auto_increment primary key, en_word varchar(128) not null,

java實現ping功能

ssa sync mes star with try ack poi election 轉載 自 http://blog.sina.com.cn/s/blog_4b00fd1b0100by7z.html 一、純Java實現ICMP的ping命令 import j

JAVA基礎--歌手打分功能實現

ext test count mil 最終 int 功能實現 最大值 lean 問題:在歌唱比賽中,共有10位評委進行打分,在計算歌手得分時,去掉一個最高分,去掉一個最低分,然後剩余的8位評委的分數進行平均,就是該選手的最終得分。輸入每個評委的評分,求某選手的得分。 分析:

Java Break和continue實現goto功能

ring 發現 並且 技術 pub goto height java oid continue實驗 1 public class test { 2 static int i =