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;
其中的targetLat
和 targetLng
就是使用者的地理位置。
這樣的確可以達到目的。但是,這是對所有資料先計算了一次和使用者的距離後再排序。
地鐵站的數量太大的時候這種操作可就不太優雅了。不僅不夠優雅,而且效率是很嚇人的。
優化
其實,可以在計算距離之前就將很多資料先過濾掉。
沒必要在 計算上海地鐵站距離的時候將美國的地鐵站距離也計算一遍吧。
這在大多數應用中都可以先將一些不需要的資料過濾掉。
比如在資料是區分城市的情況下就可以將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 =