powerbidaxpowerquery

Assistance Needed with DAX Measure for Calculating Sequential Distance in Power BI


I am currently facing difficulties creating a DAX measure(or python script, or something else) to calculate the local distance between the most recent activity and the previous one, with all the data already present in a single table. Despite extensive research, all the examples I’ve found involve two tables, which doesn't apply to my situation.

The table contains the following columns: the ID of the card that performed the activity, the activity ID, the location ID, the registration date (note that an activity may start on the 10th, for example, but the relevant date is the end date, so in this case, the date would actually be the 11th), start and end date/time, latitude, and longitude.

The goal is for the user to be able to filter the data by the card ID and then by the registration date. Once filtered, the data should be organised by date and time, and the measure I intend to create should calculate the distance from the location of the previous record.

I realise this might sound a bit confusing, so let me clarify with some example data:

ID_CARD ID_ACTIVITY ID_LOCATION CALDAY STARTTIME ENDTIME LATITUDE LONGITUDE
A XXXX-XXXX-XXXX-XXXXX16 X1 11/08/2024 11/08/2024 03:51 11/08/2024 04:02 41.00000 -8.00000
A XXXX-XXXX-XXXX-XXXXX1 X2 11/08/2024 11/08/2024 04:07 11/08/2024 04:17 41.00001 -8.00001
A XXXX-XXXX-XXXX-XXXXX2 X3 11/08/2024 11/08/2024 04:29 11/08/2024 04:59 41.00002 -8.00002
A XXXX-XXXX-XXXX-XXXXX3 X4 11/08/2024 11/08/2024 06:19 11/08/2024 06:44 41.00003 -8.00003
A XXXX-XXXX-XXXX-XXXXX4 X4 11/08/2024 11/08/2024 07:37 11/08/2024 08:10 41.00004 -8.00004
B XXXX-XXXX-XXXX-XXXXX5 X6 11/08/2024 11/08/2024 07:54 11/08/2024 08:58 41.00005 -8.00005
A XXXX-XXXX-XXXX-XXXXX6 X6 11/08/2024 11/08/2024 08:37 11/08/2024 08:40 41.00006 -8.00006
A XXXX-XXXX-XXXX-XXXXX7 X7 11/08/2024 11/08/2024 08:40 11/08/2024 09:45 41.00007 -8.00007
B XXXX-XXXX-XXXX-XXXXX8 X5 11/08/2024 11/08/2024 18:06 11/08/2024 18:32 41.00008 -8.00008
B XXXX-XXXX-XXXX-XXXXX9 XX7 11/08/2024 11/08/2024 18:33 11/08/2024 18:56 41.00009 -8.00009
B XXXX-XXXX-XXXX-XXXXX10 X7 11/08/2024 11/08/2024 18:57 11/08/2024 19:34 41.00010 -8.00010
B XXXX-XXXX-XXXX-XXXXX11 X9 11/08/2024 11/08/2024 19:35 11/08/2024 19:59 41.00011 -8.00011
B XXXX-XXXX-XXXX-XXXXX12 X2 11/08/2024 11/08/2024 20:01 11/08/2024 20:21 41.00012 -8.00012
B XXXX-XXXX-XXXX-XXXXX13 X3 11/08/2024 11/08/2024 20:23 11/08/2024 21:37 41.00013 -8.00013
A XXXX-XXXX-XXXX-XXXXX14 X8 11/08/2024 11/08/2024 20:57 11/08/2024 22:00 41.00014 -8.00014
A XXXX-XXXX-XXXX-XXXXX15 X9 11/08/2024 11/08/2024 22:03 11/08/2024 22:34 41.00015 -8.00015

Let's suppose that these are the data stored in the database. Now I will show what the user will see when filtering on the dashboard.

ID_CARD ID_ACTIVITY ID_LOCATION CALDAY STARTTIME ENDTIME LATITUDE LONGITUDE .distanceKM
A XXXX-XXXX-XXXX-XXXXX16 X1 11/08/2024 11/08/2024 03:51 11/08/2024 04:02 41.00000 -8.00000 0.0
A XXXX-XXXX-XXXX-XXXXX1 X2 11/08/2024 11/08/2024 04:07 11/08/2024 04:17 41.00001 -8.00001 0.2
A XXXX-XXXX-XXXX-XXXXX2 X3 11/08/2024 11/08/2024 04:29 11/08/2024 04:59 41.00002 -8.00002 0.4
A XXXX-XXXX-XXXX-XXXXX3 X4 11/08/2024 11/08/2024 06:19 11/08/2024 06:44 41.00003 -8.00003 0.7
A XXXX-XXXX-XXXX-XXXXX4 X4 11/08/2024 11/08/2024 07:37 11/08/2024 08:10 41.00004 -8.00004 0.0
A XXXX-XXXX-XXXX-XXXXX6 X6 11/08/2024 11/08/2024 08:37 11/08/2024 08:40 41.00006 -8.00006 1.0
A XXXX-XXXX-XXXX-XXXXX7 X7 11/08/2024 11/08/2024 08:40 11/08/2024 09:45 41.00007 -8.00007 0.9
A XXXX-XXXX-XXXX-XXXXX14 X8 11/08/2024 11/08/2024 20:57 11/08/2024 22:00 41.00014 -8.00014 0.2
A XXXX-XXXX-XXXX-XXXXX15 X9 11/08/2024 11/08/2024 22:03 11/08/2024 22:34 41.00015 -8.00015 0.5

This is what the user will see when applying the filters; the .distanceKM will be calculated in the order of the records. I hope this is enough for you to understand my question. I look forward to the resolution of this issue or even a suggestion.

P.S.: I had to create random data to protect confidential information, but I think the idea is clear.


Solution

  • Try a Measure similar to:

    .distanceKM = 
      var thisLat = MAX('YourTable'[LATITUDE])
      var thisLng = MAX('YourTable'[LONGITUDE])
      var prevRow = OFFSET(-1, DISTINCT(ALLSELECTED('YourTable')), ORDERBY([ENDTIME], ASC))
      var prevLat = CALCULATE(MAX('YourTable'[LATITUDE]), prevRow)
      var prevLng = CALCULATE(MAX('YourTable'[LONGITUDE]), prevRow)
    
      // from 2nd answer at https://stackoverflow.com/a/21623206/21688343
      var r = 6371 // Earth's radius in KM
      var p = DIVIDE( PI(), 180 )
      var a = 0.5 - DIVIDE(COS( (thisLat - prevLat) * p), 2) + COS(prevLat * p) * COS(thisLat * p) * DIVIDE(1 - COS((thisLng - prevLng) * p), 2)
      var dist = 2 * r * ASIN(SQRT(a))
    
      return IF(NOT ISBLANK(prevLat), dist, 0)
    

    The main part is using OFFSET to get the previous row. See https://pbidax.wordpress.com/2022/12/23/introducing-dax-window-functions-part-2/ for a good write-up on it.