Check your eligibility for this 50% exam voucher offer and join us for free live learning sessions to get prepared for Exam DP-700.
Get StartedDon'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.
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_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.
Solved! Go to Solution.
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
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:
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
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
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:
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
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.
March 31 - April 2, 2025, in Las Vegas, Nevada. Use code MSCUST for a $150 discount!
Check out the January 2025 Power BI update to learn about new features in Reporting, Modeling, and Data Connectivity.
User | Count |
---|---|
15 | |
11 | |
8 | |
8 | |
8 |
User | Count |
---|---|
22 | |
13 | |
11 | |
10 | |
10 |