Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Don't miss out! 2025 Microsoft Fabric Community Conference, March 31 - April 2, Las Vegas, Nevada. Use code MSCUST for a $150 discount. Prices go up February 11th. Register now.

Reply
Joaomatos2002
Frequent Visitor

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

Hi,

 

I hope this message finds you well.

 

I am currently facing difficulties creating a DAX measure 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_CARDID_ACTIVITYID_LOCATIONCALDAYSTARTTIMEENDTIMElatitudelongitude
AXXXX-XXXX-XXXX-XXXXX16X111/08/202411/08/2024 03:5111/08/2024 04:0241.00000-8.00000
AXXXX-XXXX-XXXX-XXXXX1X211/08/202411/08/2024 04:0711/08/2024 04:1741.00001-8.00001
AXXXX-XXXX-XXXX-XXXXX2X311/08/202411/08/2024 04:2911/08/2024 04:5941.00002-8.00002
AXXXX-XXXX-XXXX-XXXXX3X411/08/202411/08/2024 06:1911/08/2024 06:4441.00003-8.00003
AXXXX-XXXX-XXXX-XXXXX4X411/08/202411/08/2024 07:3711/08/2024 08:1041.00004-8.00004
BXXXX-XXXX-XXXX-XXXXX5X611/08/202411/08/2024 07:5411/08/2024 08:5841.00005-8.00005
AXXXX-XXXX-XXXX-XXXXX6X611/08/202411/08/2024 08:3711/08/2024 08:4041.00006-8.00006
AXXXX-XXXX-XXXX-XXXXX7X711/08/202411/08/2024 08:4011/08/2024 09:4541.00007-8.00007
BXXXX-XXXX-XXXX-XXXXX8X511/08/202411/08/2024 18:0611/08/2024 18:3241.00008-8.00008
BXXXX-XXXX-XXXX-XXXXX9XX711/08/202411/08/2024 18:3311/08/2024 18:5641.00009-8.00009
BXXXX-XXXX-XXXX-XXXXX10X711/08/202411/08/2024 18:5711/08/2024 19:3441.00010-8.00010
BXXXX-XXXX-XXXX-XXXXX11X911/08/202411/08/2024 19:3511/08/2024 19:5941.00011-8.00011
BXXXX-XXXX-XXXX-XXXXX12X211/08/202411/08/2024 20:0111/08/2024 20:2141.00012-8.00012
BXXXX-XXXX-XXXX-XXXXX13X311/08/202411/08/2024 20:2311/08/2024 21:3741.00013-8.00013
AXXXX-XXXX-XXXX-XXXXX14X811/08/202411/08/2024 20:5711/08/2024 22:0041.00014-8.00014
AXXXX-XXXX-XXXX-XXXXX15X911/08/202411/08/2024 22:0311/08/2024 22:3441.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_CARDID_ACTIVITYID_LOCATIONCALDAYSTARTTIMEENDTIMElatitudelongitude

.distanceKM

AXXXX-XXXX-XXXX-XXXXX16X111/08/202411/08/2024 03:5111/08/2024 04:0241.00000-8.000000.0
AXXXX-XXXX-XXXX-XXXXX1X211/08/202411/08/2024 04:0711/08/2024 04:1741.00001-8.000010.2
AXXXX-XXXX-XXXX-XXXXX2X311/08/202411/08/2024 04:2911/08/2024 04:5941.00002-8.000020.4
AXXXX-XXXX-XXXX-XXXXX3X411/08/202411/08/2024 06:1911/08/2024 06:4441.00003-8.000030.7
AXXXX-XXXX-XXXX-XXXXX4X411/08/202411/08/2024 07:3711/08/2024 08:1041.00004-8.000040.0
AXXXX-XXXX-XXXX-XXXXX6X611/08/202411/08/2024 08:3711/08/2024 08:4041.00006-8.000061.0
AXXXX-XXXX-XXXX-XXXXX7X711/08/202411/08/2024 08:4011/08/2024 09:4541.00007-8.000070.9
AXXXX-XXXX-XXXX-XXXXX14X811/08/202411/08/2024 20:5711/08/2024 22:0041.00014-8.000140.2
AXXXX-XXXX-XXXX-XXXXX15X911/08/202411/08/2024 22:0311/08/2024 22:3441.00015-8.000150.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.

1 ACCEPTED SOLUTION
dufoq3
Super User
Super User

Hi @Joaomatos2002

Comment: are you sure that you have correct distance calculated in your sample?

 

 

 

DAX solution (power bi files attached. First as a simple measure, second with calculated columns as helper)

Output

dufoq3_0-1723633309806.png

 

Create as a new measure:

Distance KM = 
VAR _IDCard = SELECTEDVALUE(Table1[ID_CARD])
VAR _endTime = SELECTEDVALUE(Table1[ENDTIME])
VAR _lat = SELECTEDVALUE(Table1[latitude])
VAR _lon = SELECTEDVALUE(Table1[longitude])
VAR _PI_D180 = PI()/180

VAR _tbl = 
    TOPN(
        1,
        FILTER(
            ALL(Table1),
            Table1[ID_CARD] = _IDCard &&
            Table1[ENDTIME] < _endTime
        ),
        [ENDTIME],
        DESC
    )

VAR _latPrev = 
    COALESCE(
        CALCULATE(
            MIN(Table1[latitude]),
            _tbl
        ),
        _lat
    )

VAR _lonPrev = 
    COALESCE(
        CALCULATE(
            MIN(Table1[longitude]),
            _tbl
        ),
        _lon
    )

VAR _result = ACOS(SIN(_lat*_PI_D180)*SIN(_latPrev*_PI_D180)+COS(_lat*_PI_D180)*COS(_latPrev*_PI_D180)*COS((_lonPrev*_PI_D180)-(_lon*_PI_D180)))*6371

VAR _result2 = IF(ISINSCOPE(Table1[ID_CARD]), _result)

RETURN _result2

 

Power Query solution:

Output:

dufoq3_1-1723633644774.png

 

let
    fnShift = (tbl as table, col as text, shift as nullable number, optional newColName as text, optional _type as type) as table =>
        //v 3. parametri zadaj zaporne cislo ak chces posunut riadky hore, kladne ak dole, 4. je nepovinny (novy nazov stlpca), 5. je nepovinny typ
        let
            a = Table.Column(tbl, col),
            b = if shift = 0 or shift = null then a else if shift > 0
                then List.Repeat({null}, shift) & List.RemoveLastN(a, shift)
                else List.RemoveFirstN(a, shift * -1) & List.Repeat({null}, shift * -1),    
            c = Table.FromColumns(Table.ToColumns(tbl) & {b}, Table.ColumnNames(tbl) &
                ( if newColName <> null then {newColName} else
                    if shift = 0 then {col & "_Duplicate"} else
                    if shift > 0 then {col & "_PrevValue"} 
                    else              {col & "_NextValue"} )),
            d = Table.TransformColumnTypes(c, {List.Last(Table.ColumnNames(c)), if _type <> null then _type else type any})
        in
            d,

    fnDistance = 
        (lat1 as number, lon1 as number, lat2 as number, lon2 as number, optional km_or_mi as text)=>
        let
            lat1_rad = lat1/ 180 * Number.PI,
            lon1_rad = lon1 / 180 * Number.PI,
            lat2_rad = lat2/ 180 * Number.PI,
            lon2_rad = lon2 / 180 * Number.PI,
            unit = if km_or_mi = "mi" then 3959 else 6371, //Earth radius 3959 for miles, 6371 for kilometers
            distance = Number.Acos(Number.Sin(lat1_rad) * Number.Sin(lat2_rad) + Number.Cos(lat1_rad) * Number.Cos(lat2_rad) * Number.Cos(lon2_rad-lon1_rad)) * unit

        in
            distance,

    fnCalc = 
        (myTable as table)=>
        [
            // _Detail = GroupedRows{[ID_CARD="A"]}[fnCalc],
            _Detail = myTable,
            _SortedRows = Table.Sort(_Detail, {{"ENDTIME", Order.Ascending}}),
            _Ad_LatLong_PrevValues = fnShift(fnShift(_SortedRows, "latitude", 1, null, type number), "longitude", 1, null, type number),
            _Ad_Distance = Table.AddColumn(_Ad_LatLong_PrevValues, "distanceKM", each fnDistance([latitude], [longitude], [latitude_PrevValue]? ?? [latitude], [longitude_PrevValue]? ?? [longitude], "km"), type number),
            _RemovedColumns = Table.RemoveColumns(_Ad_Distance, {"latitude_PrevValue", "longitude_PrevValue"})
        ][_RemovedColumns],
    
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("hdRLjsMgEATQq4y8TiZU82c3cwpLke9/jQEmUtmQNF5YuCz1E79+Pref7bbt9blfXztC+4H6Ah4mPcSIu3x8GVs8hsgVIzVy+DbtqcN7eg2Pm4K1XDSrFo5ThEgLtKBajdmtbkmeIp9pCS1RrcbsTrNCwWiF4hwtS8uqlltasdhxDVOBoeVouW79frB8y4Nu+TFKxSdanpZX5xWWVno3L3eaV6AVVKuV2aNu9cLnKBfnaUVaUV3DthZ9IT9ZSMWEKbKn+5VoJdXKPdcm1irbKfKBWCaWVayfqBXmhx1DLpanHuwcMDrWW0fWsFrZT9HpOoOtA9AxWfUpMcVgigTE2DsgOmZXjapVHvZM8H8XXhibB/Tmgd49ko6NeyZSDG8Z2D3gdMyv9qxVHmcmlwPC9lGHx/EH", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [ID_CARD = _t, ID_ACTIVITY = _t, ID_LOCATION = _t, CALDAY = _t, STARTTIME = _t, ENDTIME = _t, latitude = _t, longitude = _t]),
    ChangedType = Table.TransformColumnTypes(Source,{{"ID_CARD", type text}, {"ID_ACTIVITY", type text}, {"ID_LOCATION", type text}, {"CALDAY", type date}, {"STARTTIME", type datetime}, {"ENDTIME", type datetime}}),
    ChangedTypeUS = Table.TransformColumnTypes(ChangedType,{{"latitude", type number}, {"longitude", type number}}, "en-US"),
    GroupedRows = Table.Group(ChangedTypeUS, {"ID_CARD"}, {{"fnCalc", fnCalc, type table}}),
    Combined = Table.Combine(GroupedRows[fnCalc])
in
    Combined

 


Note: Check this link to learn how to use my query.
Check this link if you don't know how to provide sample data.

View solution in original post

2 REPLIES 2
dufoq3
Super User
Super User

Hi @Joaomatos2002

Comment: are you sure that you have correct distance calculated in your sample?

 

 

 

DAX solution (power bi files attached. First as a simple measure, second with calculated columns as helper)

Output

dufoq3_0-1723633309806.png

 

Create as a new measure:

Distance KM = 
VAR _IDCard = SELECTEDVALUE(Table1[ID_CARD])
VAR _endTime = SELECTEDVALUE(Table1[ENDTIME])
VAR _lat = SELECTEDVALUE(Table1[latitude])
VAR _lon = SELECTEDVALUE(Table1[longitude])
VAR _PI_D180 = PI()/180

VAR _tbl = 
    TOPN(
        1,
        FILTER(
            ALL(Table1),
            Table1[ID_CARD] = _IDCard &&
            Table1[ENDTIME] < _endTime
        ),
        [ENDTIME],
        DESC
    )

VAR _latPrev = 
    COALESCE(
        CALCULATE(
            MIN(Table1[latitude]),
            _tbl
        ),
        _lat
    )

VAR _lonPrev = 
    COALESCE(
        CALCULATE(
            MIN(Table1[longitude]),
            _tbl
        ),
        _lon
    )

VAR _result = ACOS(SIN(_lat*_PI_D180)*SIN(_latPrev*_PI_D180)+COS(_lat*_PI_D180)*COS(_latPrev*_PI_D180)*COS((_lonPrev*_PI_D180)-(_lon*_PI_D180)))*6371

VAR _result2 = IF(ISINSCOPE(Table1[ID_CARD]), _result)

RETURN _result2

 

Power Query solution:

Output:

dufoq3_1-1723633644774.png

 

let
    fnShift = (tbl as table, col as text, shift as nullable number, optional newColName as text, optional _type as type) as table =>
        //v 3. parametri zadaj zaporne cislo ak chces posunut riadky hore, kladne ak dole, 4. je nepovinny (novy nazov stlpca), 5. je nepovinny typ
        let
            a = Table.Column(tbl, col),
            b = if shift = 0 or shift = null then a else if shift > 0
                then List.Repeat({null}, shift) & List.RemoveLastN(a, shift)
                else List.RemoveFirstN(a, shift * -1) & List.Repeat({null}, shift * -1),    
            c = Table.FromColumns(Table.ToColumns(tbl) & {b}, Table.ColumnNames(tbl) &
                ( if newColName <> null then {newColName} else
                    if shift = 0 then {col & "_Duplicate"} else
                    if shift > 0 then {col & "_PrevValue"} 
                    else              {col & "_NextValue"} )),
            d = Table.TransformColumnTypes(c, {List.Last(Table.ColumnNames(c)), if _type <> null then _type else type any})
        in
            d,

    fnDistance = 
        (lat1 as number, lon1 as number, lat2 as number, lon2 as number, optional km_or_mi as text)=>
        let
            lat1_rad = lat1/ 180 * Number.PI,
            lon1_rad = lon1 / 180 * Number.PI,
            lat2_rad = lat2/ 180 * Number.PI,
            lon2_rad = lon2 / 180 * Number.PI,
            unit = if km_or_mi = "mi" then 3959 else 6371, //Earth radius 3959 for miles, 6371 for kilometers
            distance = Number.Acos(Number.Sin(lat1_rad) * Number.Sin(lat2_rad) + Number.Cos(lat1_rad) * Number.Cos(lat2_rad) * Number.Cos(lon2_rad-lon1_rad)) * unit

        in
            distance,

    fnCalc = 
        (myTable as table)=>
        [
            // _Detail = GroupedRows{[ID_CARD="A"]}[fnCalc],
            _Detail = myTable,
            _SortedRows = Table.Sort(_Detail, {{"ENDTIME", Order.Ascending}}),
            _Ad_LatLong_PrevValues = fnShift(fnShift(_SortedRows, "latitude", 1, null, type number), "longitude", 1, null, type number),
            _Ad_Distance = Table.AddColumn(_Ad_LatLong_PrevValues, "distanceKM", each fnDistance([latitude], [longitude], [latitude_PrevValue]? ?? [latitude], [longitude_PrevValue]? ?? [longitude], "km"), type number),
            _RemovedColumns = Table.RemoveColumns(_Ad_Distance, {"latitude_PrevValue", "longitude_PrevValue"})
        ][_RemovedColumns],
    
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("hdRLjsMgEATQq4y8TiZU82c3cwpLke9/jQEmUtmQNF5YuCz1E79+Pref7bbt9blfXztC+4H6Ah4mPcSIu3x8GVs8hsgVIzVy+DbtqcN7eg2Pm4K1XDSrFo5ThEgLtKBajdmtbkmeIp9pCS1RrcbsTrNCwWiF4hwtS8uqlltasdhxDVOBoeVouW79frB8y4Nu+TFKxSdanpZX5xWWVno3L3eaV6AVVKuV2aNu9cLnKBfnaUVaUV3DthZ9IT9ZSMWEKbKn+5VoJdXKPdcm1irbKfKBWCaWVayfqBXmhx1DLpanHuwcMDrWW0fWsFrZT9HpOoOtA9AxWfUpMcVgigTE2DsgOmZXjapVHvZM8H8XXhibB/Tmgd49ko6NeyZSDG8Z2D3gdMyv9qxVHmcmlwPC9lGHx/EH", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [ID_CARD = _t, ID_ACTIVITY = _t, ID_LOCATION = _t, CALDAY = _t, STARTTIME = _t, ENDTIME = _t, latitude = _t, longitude = _t]),
    ChangedType = Table.TransformColumnTypes(Source,{{"ID_CARD", type text}, {"ID_ACTIVITY", type text}, {"ID_LOCATION", type text}, {"CALDAY", type date}, {"STARTTIME", type datetime}, {"ENDTIME", type datetime}}),
    ChangedTypeUS = Table.TransformColumnTypes(ChangedType,{{"latitude", type number}, {"longitude", type number}}, "en-US"),
    GroupedRows = Table.Group(ChangedTypeUS, {"ID_CARD"}, {{"fnCalc", fnCalc, type table}}),
    Combined = Table.Combine(GroupedRows[fnCalc])
in
    Combined

 


Note: Check this link to learn how to use my query.
Check this link if you don't know how to provide sample data.

lbendlin
Super User
Super User

You _could_ use measures for that but it would be rather convoluted.  Instead, consider using Visual Calculations - they have a much easier way to address the "previous"  record.

Helpful resources

Announcements
Las Vegas 2025

Join us at the Microsoft Fabric Community Conference

March 31 - April 2, 2025, in Las Vegas, Nevada. Use code MSCUST for a $150 discount!

Jan25PBI_Carousel

Power BI Monthly Update - January 2025

Check out the January 2025 Power BI update to learn about new features in Reporting, Modeling, and Data Connectivity.

Jan NL Carousel

Fabric Community Update - January 2025

Find out what's new and trending in the Fabric community.