Skip to content

Commit

Permalink
Add unit argument to haversine distnace macro, and apply conversion f…
Browse files Browse the repository at this point in the history
…or kms (#340)
  • Loading branch information
bastienboutonnet authored Apr 21, 2021
1 parent 049974c commit f4ef848
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 11 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Notes:

### Date/Time
#### date_spine ([source](macros/datetime/date_spine.sql))
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`.
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`.

**Usage:**

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

Optionally takes a `unit` string parameter ('km' or 'mi') which defaults to miles (imperial system).

**Usage:**

```
{{ dbt_utils.haversine_distance(lat1=<float>,lon1=<float>,lat2=<float>,lon2=<float>) }}
{{ dbt_utils.haversine_distance(lat1=<float>,lon1=<float>,lat2=<float>,lon2=<float>, unit='mi'<string>) }}
```
---
### Schema Tests
Expand Down
2 changes: 2 additions & 0 deletions integration_tests/data/geo/data_haversine_km.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lat_1,lon_1,lat_2,lon_2,output
48.864716,2.349014,52.379189,4.899431,430
2 changes: 2 additions & 0 deletions integration_tests/data/geo/data_haversine_mi.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lat_1,lon_1,lat_2,lon_2,output
48.864716,2.349014,52.379189,4.899431,267
4 changes: 2 additions & 2 deletions integration_tests/dbt_project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ seeds:
sql:
data_events_20180103:
+schema: events

schema_tests:
data_test_sequential_timestamps:
+column_types:
my_timestamp: timestamp
my_timestamp: timestamp
1 change: 0 additions & 1 deletion integration_tests/macros/tests.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

{% macro test_assert_equal(model, actual, expected) %}

select count(*) from {{ model }} where {{ actual }} != {{ expected }}

{% endmacro %}
Expand Down
13 changes: 13 additions & 0 deletions integration_tests/models/geo/schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2

models:
- name: test_haversine_distance_km
tests:
- assert_equal:
actual: actual
expected: expected
- name: test_haversine_distance_mi
tests:
- assert_equal:
actual: actual
expected: expected
23 changes: 23 additions & 0 deletions integration_tests/models/geo/test_haversine_distance_km.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
with data as (
select * from {{ ref('data_haversine_km') }}
),
final as (
select
output as expected,
cast(
{{
dbt_utils.haversine_distance(
lat1='lat_1',
lon1='lon_1',
lat2='lat_2',
lon2='lon_2',
unit='km'
)
}} as numeric
) as actual
from data
)
select
expected,
round(actual,0) as actual
from final
39 changes: 39 additions & 0 deletions integration_tests/models/geo/test_haversine_distance_mi.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
with data as (
select * from {{ ref('data_haversine_mi') }}
),
final as (
select
output as expected,
cast(
{{
dbt_utils.haversine_distance(
lat1='lat_1',
lon1='lon_1',
lat2='lat_2',
lon2='lon_2',
unit='mi'
)
}} as numeric
) as actual
from data

union all

select
output as expected,
cast(
{{
dbt_utils.haversine_distance(
lat1='lat_1',
lon1='lon_1',
lat2='lat_2',
lon2='lon_2',
)
}} as numeric
) as actual
from data
)
select
expected,
round(actual,0) as actual
from final
44 changes: 38 additions & 6 deletions macros/geo/haversine_distance.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,49 @@ This calculates the distance between two sets of latitude and longitude.
The formula is from the following blog post:
http://daynebatten.com/2015/09/latitude-longitude-distance-sql/

The arguments should be float type.
The arguments should be float type.
#}

{% macro haversine_distance(lat1,lon1,lat2,lon2) -%}
{{ return(adapter.dispatch('haversine_distance', packages = dbt_utils._get_utils_namespaces())(lat1,lon1,lat2,lon2)) }}
{% macro degrees_to_radians(degrees) -%}
acos(-1) * {{degrees}} / 180
{%- endmacro %}

{% macro haversine_distance(lat1, lon1, lat2, lon2, unit='mi') -%}
{{ return(adapter.dispatch('haversine_distance', packages = dbt_utils._get_utils_namespaces())(lat1,lon1,lat2,lon2,unit)) }}
{% endmacro %}

{% macro default__haversine_distance(lat1,lon1,lat2,lon2) -%}
{% macro default__haversine_distance(lat1, lon1, lat2, lon2, unit='mi') -%}
{%- if unit == 'mi' %}
{% set conversion_rate = 1 %}
{% elif unit == 'km' %}
{% set conversion_rate = 1.60934 %}
{% else %}
{{ exceptions.raise_compiler_error("unit input must be one of 'mi' or 'km'. Got " ~ unit) }}
{% endif %}

2 * 3961 * asin(sqrt((sin(radians(({{lat2}} - {{lat1}}) / 2))) ^ 2 +
2 * 3961 * asin(sqrt(pow((sin(radians(({{ lat2 }} - {{ lat1 }}) / 2))), 2) +
cos(radians({{lat1}})) * cos(radians({{lat2}})) *
(sin(radians(({{lon2}} - {{lon1}}) / 2))) ^ 2))
pow((sin(radians(({{ lon2 }} - {{ lon1 }}) / 2))), 2))) * {{ conversion_rate }}

{%- endmacro %}



{% macro bigquery__haversine_distance(lat1, lon1, lat2, lon2, unit='mi') -%}
{% set radians_lat1 = dbt_utils.degrees_to_radians(lat1) %}
{% set radians_lat2 = dbt_utils.degrees_to_radians(lat2) %}
{% set radians_lon1 = dbt_utils.degrees_to_radians(lon1) %}
{% set radians_lon2 = dbt_utils.degrees_to_radians(lon2) %}
{%- if unit == 'mi' %}
{% set conversion_rate = 1 %}
{% elif unit == 'km' %}
{% set conversion_rate = 1.60934 %}
{% else %}
{{ exceptions.raise_compiler_error("unit input must be one of 'mi' or 'km'. Got " ~ unit) }}
{% endif %}
2 * 3961 * asin(sqrt(pow(sin(({{ radians_lat2 }} - {{ radians_lat1 }}) / 2), 2) +
cos({{ radians_lat1 }}) * cos({{ radians_lat2 }}) *
pow(sin(({{ radians_lon2 }} - {{ radians_lon1 }}) / 2), 2))) * {{ conversion_rate }}

{%- endmacro %}

0 comments on commit f4ef848

Please sign in to comment.