Calculation of distance using latitude and longitude information from user and home

Hi all,

as you perhaps also tried at sometime (like me) to get a distance information (user <> home) using latitude / longitude information (e.g. from OwnTracks), I would like to share my realization, how I finally solved this issue. If someone has an idea to improve this rule, please let me know - but so far it works fine already.

The formula behind this calculation I took from here.

rule OwnTrack: Distance User <> Home

	when
		Item OwnTrackUserLatitude changed or
		Item OwnTrackUserLongitude changed
		
	then
		
		var Number LatitudeHome = xx.xxxxxx
		var Number LongitudeHome = yy.yyyyyy
		var Number Earthradius = 6372.797 

		var Number LatitudeUser = (OwnTrackUserLatitude.state as DecimalType).doubleValue
		var Number LongitudeUser = (OwnTrackUserLongitude.state as DecimalType).doubleValue
		
		var Number pi80 = Math::PI / 180.0
		
		var Number LatitudeHomePi = LatitudeHome * pi80
		var Number LongitudeHomePi = LongitudeHome * pi80
		var Number LatitudeUserPi = LatitudeUser * pi80
		var Number LongitudeUserPi = LongitudeUser * pi80
		
		var Number LatitudeDifferencePi = LatitudeUserPi - LatitudeHomePi
		var Number LongitudeDifferencePi = LongitudeUserPi - LongitudeHomePi
		
		var Number a = Math::sin((LatitudeDifferencePi / 2).doubleValue()) * Math::sin((LatitudeDifferencePi / 2).doubleValue()) + Math::cos(LatitudeHomePi.doubleValue()) * Math::cos(LatitudeUserPi.doubleValue()) * Math::sin((LongitudeDifferencePi / 2).doubleValue()) * Math::sin((LongitudeDifferencePi / 2).doubleValue())
		var Number c = 2 * Math::atan2(Math::sqrt(a.doubleValue()), Math::sqrt(1 - a.doubleValue()))
		var Number km = Earthradius * c

		sendCommand(OwnTrackUserDistance, km)
end
2 Likes

Use PointType.distanceFrom(PointType):

import org.openhab.core.library.types.PointType

rule SetOwnTracksLocation
when
  Item OwnTrackUserLatitude changed or
  Item OwnTrackUserLongitude changed
then
  val PointType homeLocation = new PointType("xx.xxxxxx,yy.yyyyyy")
  val PointType newLocation = new PointType(OwnTrackUserLatitude.state, OwnTrackUserLatitude.state)
  OwnTrackUserDistance.postUpdate(newLocation.distanceFrom(homeLocation))
end
4 Likes

The “PointType item” is a great feature.
But it is still not really documented. I am using it in a combination with my networkhealth item,
If i am at home: Set my distance to zero.

But which unit has the distance?
Should be metric, i suppose.
Is it possible to transform that distances below 5000 meters are printed as meters, above 5000 meters as kilometers and below 1000 meters displayed as a float (.1f)

below 1000 and 1000-4999,99 Meters could be formatted using two text items and the [visibility…] switch in the sitemap.

In the example above your post, the item OwnTrackUserDistance is a Number item, so your rule might look like:

rule IAmHome
when
  Item MyPhoneIsHome changed to ON
then
  OwnTrackUserDistance.postUpdate(0)
end

The return value from aPoint.distanceFrom(anotherPoint) is meters. So you could have a String item named DistanceFromHome and a rule like this (untested):

rule DistanceChanged
when
  Item OwnTracksUserDistance changed
then
  var String readable = "hmmm...."
  var Number m = OwnTracksUserDistance.state as DecimalType
  var Number km = m / 1000
  // if you use miles and feet, just multiply by conversion factors
  switch m {
    case 0 : readable = "I'm home!"
    case m < 1000 : readable = String::format("%.1fm", m)
    case m < 5000 : readable = String::format("%.0fm", m)
    case m >= 5000 : readable = String::format("%.1fkm", km)
  }
  DistanceFromHome.postUpdate(readable)
}
2 Likes

Thanks,

small modification was needed, but now it works (i added .floatValue() behind the values in meters)
There was an unused Bracket } and the missing end parameter

rule "User 2 Distancce changed"
when
  Item mqttDistU2toHome changed
then
  var String readable = "hmmm...."
  var Number m = mqttDistU2toHome.state as DecimalType
  var Number km = m / 1000
  // if you use miles and feet, just multiply by conversion factors
  switch m {
    case 0 : readable = "Zuhause!"
    case m < 1000 : readable = String::format("%.1fm", m.floatValue())
    case m < 5000 : readable = String::format("%.0fm", m.floatValue())
    case m >= 5000 : readable = String::format("%.1fkm", km)
  }
  mqttDistU2toHomeTXT.postUpdate(readable)
end
1 Like

Is this pseudo language or do these variables (OwnTrackUserDistance, etc.) exist?

[edit:]
Sorry, got it… it is just a freely named variable name.

Hey everyone, I hope it’s ok if I pick up this old topic again as at least it nearly matches what I do… but looks like I’m to stupid to really understand what I have to do. Already found numerous thread here, the documentation and some other boards… But I can’t manage to find what I really have to create.

I’m very sorry I think it’s easy but I’m not able to manage it on my own.

I have to items. Both are location items.
Standort_Zuhause > Location of my house
Mercedes_Position > Current location of my car

What I want to do now is to check the distance between both locations and if the distance is lower than let’s they 10 meters I want something to happen for example sending a push.

As a trigger for the rule I of couse I can use change of “Mercedes_Position” item.But maybe this will create a lot of traffic as driving the car would result in continues rule triggers.

Second thing is how I define the rule itself? So how to check the distance between both iteam and how I define the value that the distacne has to go below of.

Already found that PointType could help but didn’t understood how.

Maybe someone can help.

This is how I set a switch to indicate if our car is at home or not:

rule "Zoe Home"
when
    Item ZOE_Location changed
then
    val String ZOE_position_north = ZOE_Location.state.toString.split(",").get(0)
    val String ZOE_position_east = ZOE_Location.state.toString.split(",").get(1)
    val PointType home_location_point  = new PointType(new DecimalType(53.xxxxxx), new DecimalType( 7.yyyyyy))
    val PointType Zoe_location_point  = new PointType(new DecimalType(ZOE_position_north), new DecimalType(ZOE_position_east))
    val int distance = Zoe_location_point.distanceFrom(home_location_point).intValue()
    if ( distance < 50) {
        ZOE_Home.postUpdate(ON)
    } else {
        ZOE_Home.postUpdate(OFF)
    }
end

Are you sure it updates that often?
Our car only updates the position data if it has stopped and engine is turned off.