BigQuery 中的 HAVERSINE 距离?
HAVERSINE distance in BigQuery?
我正在寻找一种在 BigQuery 中获取 HAVERSINE() 的方法。例如,如何获取距离任意点最近的气象站?
2018 年更新:BigQuery 现在支持原生地理函数。
ST_DISTANCE: Returns the shortest distance in meters between two non-empty GEOGRAPHYs.
纽约到西雅图的距离:
#standardSQL
WITH geopoints AS (
SELECT ST_GEOGPOINT(lon,lat) p, name, state
FROM `bigquery-public-data.noaa_gsod.stations`
)
SELECT ST_DISTANCE(
(SELECT p FROM geopoints WHERE name='PORT AUTH DOWNTN MANHATTAN WA'),
(SELECT p FROM geopoints WHERE name='SEATTLE')
)
3866381.55
遗留 SQL 解决方案(标准待定):
SELECT lat, lon, name,
(111.045 * DEGREES(ACOS(COS(RADIANS(40.73943)) * COS(RADIANS(lat)) * COS(RADIANS(-73.99585) - RADIANS(lon)) + SIN(RADIANS(40.73943)) * SIN(RADIANS(lat))))) AS distance
FROM [bigquery-public-data:noaa_gsod.stations]
HAVING distance>0
ORDER BY distance
LIMIT 4
(基于http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/)
2019 年更新:BigQuery 现在有一个原生 ST_DISTANCE()
函数,比 Haversine 更准确。
例如:
#standardSQL
CREATE TEMP FUNCTION RADIANS(x FLOAT64) AS (
ACOS(-1) * x / 180
);
CREATE TEMP FUNCTION RADIANS_TO_KM(x FLOAT64) AS (
111.045 * 180 * x / ACOS(-1)
);
CREATE TEMP FUNCTION HAVERSINE(lat1 FLOAT64, long1 FLOAT64,
lat2 FLOAT64, long2 FLOAT64) AS (
RADIANS_TO_KM(
ACOS(COS(RADIANS(lat1)) * COS(RADIANS(lat2)) *
COS(RADIANS(long1) - RADIANS(long2)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))))
);
SELECT
lat,
lon,
name,
HAVERSINE(40.73943, -73.99585, lat, lon) *1000 AS haversine_distance
, ST_DISTANCE(
ST_GEOGPOINT(-73.99585, 40.73943)
, ST_GEOGPOINT(lon,lat)) bqgis_distance
FROM `bigquery-public-data.noaa_gsod.stations`
WHERE lat IS NOT NULL AND lon IS NOT NULL
ORDER BY 1 DESC
LIMIT 4;
使用standard SQL你可以定义一个SQL函数来封装逻辑。例如,
#standardSQL
CREATE TEMP FUNCTION RADIANS(x FLOAT64) AS (
ACOS(-1) * x / 180
);
CREATE TEMP FUNCTION RADIANS_TO_KM(x FLOAT64) AS (
111.045 * 180 * x / ACOS(-1)
);
CREATE TEMP FUNCTION HAVERSINE(lat1 FLOAT64, long1 FLOAT64,
lat2 FLOAT64, long2 FLOAT64) AS (
RADIANS_TO_KM(
ACOS(COS(RADIANS(lat1)) * COS(RADIANS(lat2)) *
COS(RADIANS(long1) - RADIANS(long2)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))))
);
SELECT
lat,
lon,
name,
HAVERSINE(40.73943, -73.99585, lat, lon) AS distance_in_km
FROM `bigquery-public-data.noaa_gsod.stations`
WHERE lat IS NOT NULL AND lon IS NOT NULL
ORDER BY distance_in_km
LIMIT 4;
我正在寻找一种在 BigQuery 中获取 HAVERSINE() 的方法。例如,如何获取距离任意点最近的气象站?
2018 年更新:BigQuery 现在支持原生地理函数。
ST_DISTANCE: Returns the shortest distance in meters between two non-empty GEOGRAPHYs.
纽约到西雅图的距离:
#standardSQL
WITH geopoints AS (
SELECT ST_GEOGPOINT(lon,lat) p, name, state
FROM `bigquery-public-data.noaa_gsod.stations`
)
SELECT ST_DISTANCE(
(SELECT p FROM geopoints WHERE name='PORT AUTH DOWNTN MANHATTAN WA'),
(SELECT p FROM geopoints WHERE name='SEATTLE')
)
3866381.55
遗留 SQL 解决方案(标准待定):
SELECT lat, lon, name,
(111.045 * DEGREES(ACOS(COS(RADIANS(40.73943)) * COS(RADIANS(lat)) * COS(RADIANS(-73.99585) - RADIANS(lon)) + SIN(RADIANS(40.73943)) * SIN(RADIANS(lat))))) AS distance
FROM [bigquery-public-data:noaa_gsod.stations]
HAVING distance>0
ORDER BY distance
LIMIT 4
(基于http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/)
2019 年更新:BigQuery 现在有一个原生 ST_DISTANCE()
函数,比 Haversine 更准确。
例如:
#standardSQL
CREATE TEMP FUNCTION RADIANS(x FLOAT64) AS (
ACOS(-1) * x / 180
);
CREATE TEMP FUNCTION RADIANS_TO_KM(x FLOAT64) AS (
111.045 * 180 * x / ACOS(-1)
);
CREATE TEMP FUNCTION HAVERSINE(lat1 FLOAT64, long1 FLOAT64,
lat2 FLOAT64, long2 FLOAT64) AS (
RADIANS_TO_KM(
ACOS(COS(RADIANS(lat1)) * COS(RADIANS(lat2)) *
COS(RADIANS(long1) - RADIANS(long2)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))))
);
SELECT
lat,
lon,
name,
HAVERSINE(40.73943, -73.99585, lat, lon) *1000 AS haversine_distance
, ST_DISTANCE(
ST_GEOGPOINT(-73.99585, 40.73943)
, ST_GEOGPOINT(lon,lat)) bqgis_distance
FROM `bigquery-public-data.noaa_gsod.stations`
WHERE lat IS NOT NULL AND lon IS NOT NULL
ORDER BY 1 DESC
LIMIT 4;
使用standard SQL你可以定义一个SQL函数来封装逻辑。例如,
#standardSQL
CREATE TEMP FUNCTION RADIANS(x FLOAT64) AS (
ACOS(-1) * x / 180
);
CREATE TEMP FUNCTION RADIANS_TO_KM(x FLOAT64) AS (
111.045 * 180 * x / ACOS(-1)
);
CREATE TEMP FUNCTION HAVERSINE(lat1 FLOAT64, long1 FLOAT64,
lat2 FLOAT64, long2 FLOAT64) AS (
RADIANS_TO_KM(
ACOS(COS(RADIANS(lat1)) * COS(RADIANS(lat2)) *
COS(RADIANS(long1) - RADIANS(long2)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))))
);
SELECT
lat,
lon,
name,
HAVERSINE(40.73943, -73.99585, lat, lon) AS distance_in_km
FROM `bigquery-public-data.noaa_gsod.stations`
WHERE lat IS NOT NULL AND lon IS NOT NULL
ORDER BY distance_in_km
LIMIT 4;