Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add unit argument and apply conversion for kms #340

Merged
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 %}