iOS - The best way to measure distance - ios

I'm currently working on a project where I need the user to tell where (on the real world map) to build a wall.
Question: What (in your opinion) is the most accurate way for the user to show(/tell) where to place the wall?
Idea 1 I have thought about drawing on a map, But that wouldn't be so accurate.
Idea 2 Another thing that I have thought of is, that the user should place their phone on the beginning and the end of the wall. And in that way the app could use CLLocationManager to print the locations on the map, and also measure the distance between the two ends.
This is the code that I tried my thought with, but it wasn't really that accurate at all.
let locationManager = CLLocationManager()
var location1: CLLocation = CLLocation()
var location2: CLLocation = CLLocation()
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
resett.isHidden = true
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.activityType = .fitness
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
currentCord.text = "\(locValue.latitude) \(locValue.longitude)"
}
func measure(cord1: CLLocation, cord2: CLLocation) -> Float {
let distanceInMeters = cord1.distance(from: cord2) //result is in meters
return Float(distanceInMeters)
}
func calculateCoordinates(status: Int) {
let coordinate = calculate()
if status == 1 {
location2 = coordinate
let measured = measure(cord1: location1, cord2: location2)
print("\(measured) m")
displayLabel.text = "\(measured)m"
resett.isHidden = false
} else {
location1 = coordinate
}
}
func calculate() -> CLLocation {
let locValue:CLLocationCoordinate2D = locationManager.location!.coordinate
let coordinate = CLLocation(latitude: locValue.latitude, longitude: locValue.longitude)
return coordinate
}
#IBAction func FirstAction(_ sender: Any) {
button1.setTitle("Klar", for: .normal)
calculateCoordinates(status: 0)
}
#IBAction func SecondAction(_ sender: Any) {
button2.setTitle("Klar", for: .normal)
calculateCoordinates(status: 1)
}
Thanks in advance!

Assuming you mean a real-world "wall", with order of dimension of centimeters to a few meters, the location of the iPhone GPS is really not adapt for these kind of measurments, being the typical minimum error on good covered areas 5 meters.
If asking explicitely the geocoordinates (with very high accuracy) is not feasable:
I'd rather ask the user explicitly the dimensions and then maybe dragging the object (wall) on the map, zooming as much as possible while approaching the possible final position.
PS: there are anyway many optimizations that are possible to be made to increase the accuracy of the CLLocationManager, first of all filtering away results with low accuracy and manually away the one with big horizzontal accuracy issues after receiving it.

The unfortunate answer is that the GPS on an iPhone is not very accurate. If you can get a location accurate within a 32 meter radius circle, you're doing well, and that's in an open area with clear line of sight to the sky, and no radio interference. If things are less ideal, the results can be far less accurate.
(Apple reports a "horizontal accuracy" reading in it's GPS location results that is actually a radius for the margin of error.)
For the purposes of building a wall, 32 meters is simply not good enough, and the GPS in an iPhone can't reliably do better. You really need surveying equipment for that.

Related

How to navigate vehicle icon for current location using google map SDK in swift?

I'm calling direction API to draw the route and trying to move the current location marker icon as user moves the location, But I m facing following problems while implementation.
while moving the icon, It appears like icon got tilt.
It doesn't display the actual user position on the route, Icon appears like it is traveling on the side of route.
My location icon (blue icon) on google map continuously changes its position.
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
let direction = newHeading.trueHeading
lastDriverAngleFromNorth = direction
self.sourceMarker?.rotation = (lastDriverAngleFromNorth - mapBearing) - bearingValue
DispatchQueue.main.async {
CATransaction.begin()
CATransaction.setValue(2, forKey: kCATransactionAnimationDuration)
self.gmsmapView?.animate(toBearing: newHeading.magneticHeading)
CATransaction.commit()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
self.currentLocation = lastLocation
let zoom = self.gmsmapView?.camera.zoom ?? 20.0
zoomLevel = zoom
let destination = CLLocation.init(latitude: viewModel.marker.location[0], longitude: viewModel.marker.location[1])
let bearing = getBearingBetweenTwoPoints(point1: lastLocation.coordinate, point2: destination.coordinate)
self.cameraMoveToLocation(toLocation: lastLocation, zoom:zoom, bearing: bearing)
}
}
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
mapBearing = position.bearing
if let coordinate = self.currentLocation?.coordinate{
self.rerouteCalculation(currentLocation:coordinate)
}
self.sourceMarker?.rotation = (lastDriverAngleFromNorth - mapBearing) - bearingValue
}
func cameraMoveToLocation(toLocation: CLLocation, zoom : Float, bearing : Double) {
self.gmsmapView?.animate(toLocation: toLocation.coordinate)
self.sourceMarker?.position = toLocation.coordinate
}
Can anyone please help me, I'm stuck here.
Make sure that you choose the correct location accuracy for your needs.
CCLocationAccuracy offers the following options:
kCLLocationAccuracyBestForNavigation: <= You should use this when the device is plugged to a power source
The highest possible accuracy that uses additional sensor data to
facilitate navigation apps.
kCLLocationAccuracyBest: <= You should use this when the device runs on battery
The best level of accuracy available.
kCLLocationAccuracyNearestTenMeters: Accurate
to within ten meters of the desired target.
kCLLocationAccuracyHundredMeters: Accurate to
within one hundred meters.
kCLLocationAccuracyKilometer: Accurate to the
nearest kilometer.
kCLLocationAccuracyThreeKilometers: Accurate
to the nearest three kilometers.

Can't get user's current location

I have a simple goal of my app that is get the current coordinate, and use them to add an annotation on the mapview.
I have been tried lots of solution from google results, but its still not working....
The debug area never shows "locationManager did UpdateLocation", the message what I print in function....
It's seems like the app never run "did UpdateLocation" function, even startUpdatingLocation() has been called?
Add location privacy string in info.plist : Done.
Turn on the GPS on my Mac Pro : Done.
Xcode version : 10.1
MacOS : 10.13.6 (High Sierra)
let cloaction = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
MapView.delegate = self
MapView.showsScale = true
MapView.showsPointsOfInterest = true
MapView.showsUserLocation = true
cloaction.requestAlwaysAuthorization()
cloaction.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
print("IN")
cloaction.delegate = self
cloaction.desiredAccuracy = kCLLocationAccuracyBest
cloaction.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager did UpdateLocation")
let location = CLLocationCoordinate2D(latitude: (locations.first?.coordinate.latitude)!, longitude: (locations.first?.coordinate.longitude)!)
currentLat = (locations.first?.coordinate.latitude)!
currentLon = (locations.first?.coordinate.longitude)!
let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
MapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2DMake(currentLat,currentLon), span: span), animated: true)
MapView.showsUserLocation = true
print(locations.first?.coordinate.latitude)
print(locations.first?.coordinate.longitude)
}
Actually the reason is very simple: You call the didUpdateLocation mehthod wich is only called when you change your location. Your Mac is on certain place and dont move so thats why it is not working.
Have you import CoreLocation?
Start with making a variable let myLocation = CLLocation()
Instead of have so much in viewDidLoad you can make a function and call the mLocation in viewDidLoad instead :
func mLocation(){
cloaction.delegate = self
cloaction.desiredAccuracy = kCLLocationAccuracyBest
cloaction.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled(){
cloaction.startUpdatingLocation()
}
}
And thats all you need for the clocation
LocationManager could also be updated
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations:[CLLocation]){
let newLocation = locations[0]
print("\(myLocation.coordinate.latitude)")
print("\(myLocation.coordinate.longitude)")
}
Yes! finally... thank your answering, it's really need to run on device, thanks Kosuke Ogawa's suggestion, and every one's guide, I am a new to learn swift, and first time ask question here, it's fun, thank you every one.
(But I don't know how to accept a answer if the answer is a comment? Someone teach me how do that?)

How to get exact current location for google maps with accuracy less than 1 meter, Swift 4?

I am working on construction project and for that I want to fetch exact current location which must satisfy the accuracy withing the 1 meter. I am using google maps with SDK "CLLocationManager" and I am getting the current location but the location is not exact, it has some (+/-)5 meters to (+/-)10 meters error in location. I want the exact/accurate current location which should not exceeds the location accuracy error more than a feet.
Please help me out to fetch EXACT CURRENT LOCATION.
Also, is there any third party library, any hardware device (which I can connect to iOS device.) or anything else, please let me know.
Your valuable comment will be most appreciate.
Edited:-
Here I am sharing my code to get the current location using CLLocationManager:
override func viewDidLoad()
{
super.viewDidLoad()
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.requestAlwaysAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let position = CLLocationCoordinate2D(latitude: manager.location!.coordinate.latitude, longitude: manager.location!.coordinate.longitude)
marker.position = position
print("position:",position)
}
Thank you..
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let location = manager.location else{
return
}
var currentLocationCoordinate = location.coordinate
}
using this delegate function you will get the current location
You can use external gps if you want to get the exact location, with the help of device you will always get this fluctuation. You can also set your location accuracy to best.
https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html
You should always relay on GPS for accurate location. You can set locationManager.desireAccuracy = kCLLocationAccuracyBest. It will call you locationManager didUpdateLocation with the location array with each location having its accuracy you can apply your logic here.
Be alert to call locationManager.stopUpdatingLocation() when you done with desire accuracy location.
e.g
let horizontalAccuracy: Double = 20.0
let howRecent = location.timestamp.timeIntervalSinceNow
guard CLLocationCoordinate2DIsValid(location.coordinate),
location.horizontalAccuracy > 0,
location.horizontalAccuracy < horizontalAccuracy,
abs(howRecent) < 10 else { return false }
return true
}

Any better way to handle overwhelming heading data from CLLocationManager to present on UI?

I can fetch Heading data from CLLocationManager.
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.headingFilter = 0.2
locationManager.headingOrientation = CLDeviceOrientation.landscapeRight
locationManager.startUpdatingHeading()
locationManager.delegate = self
I can use
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
let headingDegree = newHeading.trueHeading
// Keep update CALayer
scrollLayer.scroll(to: CGPoint(x: headingDegree, y: 20.0))
}
to update UI. The UI is a heading tap on the top of a CAScrollLayer.
Problem:
The heading tape keeps shaking when the iPhone yawing too fast. I believe the scrolling activity is not quick enough to process overwhelming heading data.
Question:
Is there any better way to handle heading update data with CAScrollLayer?
It sounds like you need some smoothing in place. To be clear, smoothing (in it's most basic form) is taking an average value over time. Let's say that you want to take the average value over the last 60 or so updates then you would have an array of values which you use to calculate the average:
var arr: [Double] = []
func average(latestVal: Double) -> Double
{
if arr.count >= 60 {
arr.remove(at: 0)
}
arr.append(latestVal)
let total = arr.reduce(0, { $0 + $1 })
return total / Double(arr.count)
}
This really is the perfect opportunity to implement a queue in Swift which you can find details on here.

CLLocation distance tracking starts off incorrectly

I am new to Swift (and this website, so sorry if I am doing anything wrong), and I am trying to make a running app that tracks the user's location. While the function I used to track the distance works, it doesn't start at 0. When I hit the start button, the distance starts at a random number and then it starts tracking from there.
My question is: Is there something I am not addressing something correctly? If so, is there a way to fix it so that the tracking is more accurate? Here is what I have so far:
override func viewDidLoad() {
super.viewDidLoad()
stopwatchLabel.text = "00:00.00"
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.activityType = .fitness
locationManager.distanceFilter = 10.0
mapView.showsUserLocation = true
startLocation = nil
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Location Delegate Methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002))
self.mapView.setRegion(region, animated: true)
if startLocation == nil {
startLocation = locations.first
}
var distance = startLocation.distance(from: location!)
let lastDistance = location?.distance(from: location!)
distance += lastDistance!
distanceString = "\(distance)"
distanceLabel.text = distanceString
}
Here is what the app looks like:
the run screen
I realize that other people have asked similar questions, but the questions either have no answer, or they are in a different language (such as Objective-C). If this question has been answered before and I'm just overlooking it, could someone please link the answer to me? Thank you!
When the location manager starts, the first location returned is the cached, last know location. You need to check for this, via the timestamp, as well as check for the level of accuracy that is returned. Something like this in your didUpdateLocations delegate:
let newLocation = locations.last
let timeDiff = newLocation?.timestamp.timeIntervalSinceNow
let accuracyNeeded:CLLocationAccuracy=100.0
if timeDiff < 5.0 && (newLocation?.horizontalAccuracy)!<=accuracyNeeded{
//your code here
}
You have to allow the sensors time to warm up.
Here is a typical didUpdateLocations implementation. We keep track of both the time elapsed since we started updating locations and the improving horizontal accuracy as the sensors warm up. If the horizontal accuracy doesn't improve in a reasonable time, we give up.
You will need a nil property, a Date?, called startTime, and constants REQ_TIME and REQ_ACC. self.stopTrying() turns off updates and resets startTime to nil.
let loc = locations.last!
let acc = loc.horizontalAccuracy
let time = loc.timestamp
let coord = loc.coordinate
if self.startTime == nil { // Date? property, keep track of time
self.startTime = Date()
return // ignore first attempt
}
let elapsed = time.timeIntervalSince(self.startTime)
if elapsed > REQ_TIME { // required time before giving up
self.stopTrying()
return
}
if acc < 0 || acc > REQ_ACC { // desired accuracy
return // wait for the next one
}
// got it
print("You are at \(coord.latitude) \(coord.longitude)")

Resources