Skip to content

Commit f4ef848

Browse files
Add unit argument to haversine distnace macro, and apply conversion for kms (#340)
1 parent 049974c commit f4ef848

9 files changed

+124
-11
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Notes:
9595

9696
### Date/Time
9797
#### date_spine ([source](macros/datetime/date_spine.sql))
98-
This macro returns the sql required to build a date spine. The spine will include the `start_date` (if it is aligned to the `datepart`), but it will not include the `end_date`.
98+
This macro returns the sql required to build a date spine. The spine will include the `start_date` (if it is aligned to the `datepart`), but it will not include the `end_date`.
9999

100100
**Usage:**
101101

@@ -114,9 +114,12 @@ This macro returns the sql required to build a date spine. The spine will includ
114114
#### haversine_distance ([source](macros/geo/haversine_distance.sql))
115115
This macro calculates the [haversine distance](http://daynebatten.com/2015/09/latitude-longitude-distance-sql/) between a pair of x/y coordinates.
116116

117+
Optionally takes a `unit` string parameter ('km' or 'mi') which defaults to miles (imperial system).
118+
117119
**Usage:**
120+
118121
```
119-
{{ dbt_utils.haversine_distance(lat1=<float>,lon1=<float>,lat2=<float>,lon2=<float>) }}
122+
{{ dbt_utils.haversine_distance(lat1=<float>,lon1=<float>,lat2=<float>,lon2=<float>, unit='mi'<string>) }}
120123
```
121124
---
122125
### Schema Tests
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lat_1,lon_1,lat_2,lon_2,output
2+
48.864716,2.349014,52.379189,4.899431,430
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lat_1,lon_1,lat_2,lon_2,output
2+
48.864716,2.349014,52.379189,4.899431,267

integration_tests/dbt_project.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ seeds:
5454
sql:
5555
data_events_20180103:
5656
+schema: events
57-
57+
5858
schema_tests:
5959
data_test_sequential_timestamps:
6060
+column_types:
61-
my_timestamp: timestamp
61+
my_timestamp: timestamp

integration_tests/macros/tests.sql

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11

22
{% macro test_assert_equal(model, actual, expected) %}
3-
43
select count(*) from {{ model }} where {{ actual }} != {{ expected }}
54

65
{% endmacro %}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 2
2+
3+
models:
4+
- name: test_haversine_distance_km
5+
tests:
6+
- assert_equal:
7+
actual: actual
8+
expected: expected
9+
- name: test_haversine_distance_mi
10+
tests:
11+
- assert_equal:
12+
actual: actual
13+
expected: expected
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
with data as (
2+
select * from {{ ref('data_haversine_km') }}
3+
),
4+
final as (
5+
select
6+
output as expected,
7+
cast(
8+
{{
9+
dbt_utils.haversine_distance(
10+
lat1='lat_1',
11+
lon1='lon_1',
12+
lat2='lat_2',
13+
lon2='lon_2',
14+
unit='km'
15+
)
16+
}} as numeric
17+
) as actual
18+
from data
19+
)
20+
select
21+
expected,
22+
round(actual,0) as actual
23+
from final
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
with data as (
2+
select * from {{ ref('data_haversine_mi') }}
3+
),
4+
final as (
5+
select
6+
output as expected,
7+
cast(
8+
{{
9+
dbt_utils.haversine_distance(
10+
lat1='lat_1',
11+
lon1='lon_1',
12+
lat2='lat_2',
13+
lon2='lon_2',
14+
unit='mi'
15+
)
16+
}} as numeric
17+
) as actual
18+
from data
19+
20+
union all
21+
22+
select
23+
output as expected,
24+
cast(
25+
{{
26+
dbt_utils.haversine_distance(
27+
lat1='lat_1',
28+
lon1='lon_1',
29+
lat2='lat_2',
30+
lon2='lon_2',
31+
)
32+
}} as numeric
33+
) as actual
34+
from data
35+
)
36+
select
37+
expected,
38+
round(actual,0) as actual
39+
from final

macros/geo/haversine_distance.sql

+38-6
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,49 @@ This calculates the distance between two sets of latitude and longitude.
33
The formula is from the following blog post:
44
http://daynebatten.com/2015/09/latitude-longitude-distance-sql/
55

6-
The arguments should be float type.
6+
The arguments should be float type.
77
#}
88

9-
{% macro haversine_distance(lat1,lon1,lat2,lon2) -%}
10-
{{ return(adapter.dispatch('haversine_distance', packages = dbt_utils._get_utils_namespaces())(lat1,lon1,lat2,lon2)) }}
9+
{% macro degrees_to_radians(degrees) -%}
10+
acos(-1) * {{degrees}} / 180
11+
{%- endmacro %}
12+
13+
{% macro haversine_distance(lat1, lon1, lat2, lon2, unit='mi') -%}
14+
{{ return(adapter.dispatch('haversine_distance', packages = dbt_utils._get_utils_namespaces())(lat1,lon1,lat2,lon2,unit)) }}
1115
{% endmacro %}
1216

13-
{% macro default__haversine_distance(lat1,lon1,lat2,lon2) -%}
17+
{% macro default__haversine_distance(lat1, lon1, lat2, lon2, unit='mi') -%}
18+
{%- if unit == 'mi' %}
19+
{% set conversion_rate = 1 %}
20+
{% elif unit == 'km' %}
21+
{% set conversion_rate = 1.60934 %}
22+
{% else %}
23+
{{ exceptions.raise_compiler_error("unit input must be one of 'mi' or 'km'. Got " ~ unit) }}
24+
{% endif %}
1425

15-
2 * 3961 * asin(sqrt((sin(radians(({{lat2}} - {{lat1}}) / 2))) ^ 2 +
26+
2 * 3961 * asin(sqrt(pow((sin(radians(({{ lat2 }} - {{ lat1 }}) / 2))), 2) +
1627
cos(radians({{lat1}})) * cos(radians({{lat2}})) *
17-
(sin(radians(({{lon2}} - {{lon1}}) / 2))) ^ 2))
28+
pow((sin(radians(({{ lon2 }} - {{ lon1 }}) / 2))), 2))) * {{ conversion_rate }}
29+
30+
{%- endmacro %}
31+
32+
33+
34+
{% macro bigquery__haversine_distance(lat1, lon1, lat2, lon2, unit='mi') -%}
35+
{% set radians_lat1 = dbt_utils.degrees_to_radians(lat1) %}
36+
{% set radians_lat2 = dbt_utils.degrees_to_radians(lat2) %}
37+
{% set radians_lon1 = dbt_utils.degrees_to_radians(lon1) %}
38+
{% set radians_lon2 = dbt_utils.degrees_to_radians(lon2) %}
39+
{%- if unit == 'mi' %}
40+
{% set conversion_rate = 1 %}
41+
{% elif unit == 'km' %}
42+
{% set conversion_rate = 1.60934 %}
43+
{% else %}
44+
{{ exceptions.raise_compiler_error("unit input must be one of 'mi' or 'km'. Got " ~ unit) }}
45+
{% endif %}
46+
2 * 3961 * asin(sqrt(pow(sin(({{ radians_lat2 }} - {{ radians_lat1 }}) / 2), 2) +
47+
cos({{ radians_lat1 }}) * cos({{ radians_lat2 }}) *
48+
pow(sin(({{ radians_lon2 }} - {{ radians_lon1 }}) / 2), 2))) * {{ conversion_rate }}
1849

1950
{%- endmacro %}
51+

0 commit comments

Comments
 (0)