Power BI is turning 10, and we’re marking the occasion with a special community challenge. Use your creativity to tell a story, uncover trends, or highlight something unexpected.
Get startedJoin us for an expert-led overview of the tools and concepts you'll need to become a Certified Power BI Data Analyst and pass exam PL-300. 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.
This is your chance to engage directly with the engineering team behind Fabric and Power BI. Share your experiences and shape the future.
Check out the June 2025 Power BI update to learn about new features.
User | Count |
---|---|
10 | |
9 | |
9 | |
8 | |
8 |
User | Count |
---|---|
14 | |
12 | |
11 | |
11 | |
8 |