My distance in miles calculation seems wrong in swift 3 - swift3

I have a user's current location and a particular location. I am trying to get distance in miles. my distance in miles seems wrong, I got 1.2 miles on my app, then I checked the miles in google map. I got 2.1 miles on google map. Here is my code, I am not sure why my code is not accurate. Please help.
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
map.delegate = self
map.userTrackingMode = MKUserTrackingMode.follow
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
// set location
let latitude: CLLocationDegrees = 32.5850
let longitude: CLLocationDegrees = -85.4904
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region = MKCoordinateRegion(center: coordinates, span: span)
map.setRegion(region, animated: true)
// add annotation
let annotation = MKPointAnnotation()
annotation.title = "1100 S College St"
annotation.coordinate = coordinates
map.addAnnotation(annotation)
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let latitude = userLocation.coordinate.latitude
let longitude = userLocation.coordinate.longitude
let location1 = CLLocation(latitude: 32.5850, longitude: -85.4904) // set location
let location2 = CLLocation(latitude: latitude, longitude: longitude) // user's current location
let distanceInMeters : CLLocationDistance = location1.distance(from: location2) // distance in meters
let distanceInMiles = distanceInMeters/1609.344 // distance in miles
let roundUp = String(format:"%.1f", distanceInMiles) // round up
distanceLabel.text = "\(roundUp) mi"
}

This part is correct:
let distanceInMiles = distanceInMeters/1609.344 // distance in miles
Check distance in meters and compare with Google Maps.
If you are in iOS 10 you can use new Measurement and related classes like this:
let meters = 1000.0
let distanceMeters = Measurement(value: meters, unit: UnitLength.meters)
let distanceMiles = distanceMeters.converted(to: UnitLength.miles)
let miles = distanceMeters.converted(to: UnitLength.miles).value
This code produces this results:

Swift 4 , XCode9
extension CLLocation {
func myDistance(coordinate: CLLocation)->String{
let distanceInMeters = self.distance(from: coordinate)
if distanceInMeters < 1000 {
return distanceInMeters.description + " M"
}else{
let distanceInKilometer = distanceInMeters / 1000
return String(format:"%.1f", distanceInKilometer) + " KM"
}
}
}

Related

Realitykit / ArKit make a collision only between 2 objects

I have a ball and a cylinder. When i tap on the ball it moves in forward position. When the ball hits the cylinder they collide and the cylinder moves and changes color. Thats working fine.
I have 2 problems:
When i start the app the cylinder is constantly colliding and changes color immediately, so even when the ball is not near it. It looks like its colliding with something else.
The second thing is, there will be more cylinders in the scene, how can i make a collision event only between 2 objects. Lets say there are 2 cylinders in the scene how can ik set a filter or group.
import SwiftUI
import RealityKit
import ARKit
import FocusEntity
import Combine
struct ContentView : View {
var body: some View {
ARViewContainer()
}
}
struct ARViewContainer: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
let view = ARView()
let session = view.session
let config = ARWorldTrackingConfiguration()
config.planeDetection = .horizontal
session.run(config)
let coachingOverlay = ARCoachingOverlayView()
coachingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
coachingOverlay.session = session
coachingOverlay.goal = .horizontalPlane
view.addSubview(coachingOverlay)
context.coordinator.view = view
session.delegate = context.coordinator
view.addGestureRecognizer(UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap)))
return view
}
func updateUIView(_ uiView: ARView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator()
}
//coordinator class
class Coordinator: NSObject,ARSessionDelegate {
weak var view: ARView?
var focusEntity: FocusEntity?
#Published var sceneIsPlaced:Bool = false //is de scene reeds geplaats na openen app
#Published var subscriptions: [AnyCancellable] = []
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
guard let view = self.view else { return }
self.focusEntity = FocusEntity(on: view, style: .classic(color: .yellow))
}
//na tap op het scherm, wat te doen -> creeer locatie en plaats het object
#objc func handleTap(recognizer: UITapGestureRecognizer) {
guard let view = self.view, let focusEntity = self.focusEntity else { return }
//tap location van de tap gesture
let tapLocation = recognizer.location(in: self.view)
//Create Anchor
let anchor = AnchorEntity()
let importModel = try! Entity.load(named: "cilinder") //alle objects!
importModel.position = focusEntity.position
importModel.scale = SIMD3(repeating: 0.5)
//The cylinder
let kolomMiddleModel = importModel.findEntity(named: "cilinder")!.children[0] as! (ModelEntity & HasPhysicsBody & HasCollision)
let materialKolomMiddle = SimpleMaterial(color: .yellow, isMetallic: true)
kolomMiddleModel.model?.materials = [materialKolomMiddle]
kolomMiddleModel.generateCollisionShapes(recursive: false)
let physics = PhysicsBodyComponent(massProperties: .default, material: .default, mode: .dynamic)
kolomMiddleModel.components.set(physics)
//MAKE A BALL
let materialsBall = SimpleMaterial(color: .red, isMetallic: true)
let ballModel = ModelEntity(mesh: .generateSphere(radius: 0.1),
materials: [materialsBall])
as (Entity & HasPhysicsBody & HasCollision)
ballModel.position = [-0.1, -1.0,-0.1]
ballModel.generateCollisionShapes(recursive: true)
ballModel.name = "ballModel"
//LIGHTS --> let op voor de shadow moet de plane van occlusion material met dynamical lightning op tue
let directionalLight = DirectionalLight()
directionalLight.light.color = .white
directionalLight.light.intensity = 4000
directionalLight.light.isRealWorldProxy = true
directionalLight.shadow?.maximumDistance = 1.5
directionalLight.shadow?.depthBias = 7.0
directionalLight.orientation = simd_quatf(angle: .pi/1.5, axis: [0,1,0])
//maak een anchor voor het licht
let lightAnchor = AnchorEntity(world: [0, 0, 2.5])
lightAnchor.addChild(directionalLight)
//COLLISION EVENTS
DispatchQueue.main.async {
//collision of kolomModel
view.scene.subscribe(to: CollisionEvents.Began.self,
on: kolomMiddleModel) { _ in
print("Collision kolomModel detected!")
let material = SimpleMaterial(color: .red, isMetallic: true)
kolomMiddleModel.model?.materials = [material]
}.store(in: &self.subscriptions)
}
// view.installGestures(for: ballModel)
//
//PLACE SCENE IF NOT PLACED ALREADY
if !sceneIsPlaced {
//ADD MODELS TO ANCHOR
anchor.addChild(importModel)
anchor.addChild(ballModel)
anchor.addChild(lightAnchor)
view.scene.addAnchor(anchor)
sceneIsPlaced = true
//If the scene is already placed get the taplocation
}else{
if let locationEntity = view.entity(at: tapLocation) {
let entityTapped = locationEntity as! ModelEntity
//MAKE THE BALL GO FORWARD
print("entity tapped \(entityTapped)")
entityTapped.physicsBody = .init()
entityTapped.physicsBody?.mode = .kinematic
entityTapped.physicsMotion = PhysicsMotionComponent(linearVelocity: [0, 0, -0.5],
angularVelocity: [1, 3, 5])
}
}
}
}
}
Here is an example of two objects in RealityKit that can only collide with each other:
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.1), materials: [SimpleMaterial()])
sphere.collision = CollisionComponent(shapes: [ShapeResource.generateSphere(radius: 0.1)])
sphere.collision?.filter = CollisionFilter(group: 1 << 2, categories: .default, mask: .default)
let cube = ModelEntity(mesh: .generateBox(size: [0.1, 0.1, 0.1]), materials: [SimpleMaterial()])
cube.collision = CollisionComponent(shapes: [ShapeResource.generateBox(size: [0.1, 0.1, 0.1])])
cube.collision?.filter = CollisionFilter(group: 1 << 1, categories: .default, mask: .default)
sphere.collision?.filter.collisionGroup = 1 << 1
cube.collision?.filter.collisionGroup = 1 << 2
arView.scene.addAnchor(sphere)
arView.scene.addAnchor(cube)
This code creates two objects: a sphere and a cube. Each object is given a CollisionComponent with a corresponding ShapeResource and CollisionFilter. The CollisionFilter for each object specifies that only objects in a different collisionGroup can collide with it. As a result, the sphere and cube can only collide with each other, not with other objects in the scene.
See more information about CollisionGroups here:
https://developer.apple.com/documentation/realitykit/collisionfilter

Saving Coordinates Swift 3 Xcode 8

I am having trouble storing coordinates into a variable. I set a variable to type string! and then when the function runs the latitude and longitude are both stored separately. I am also performing a segue because the sign-up process I am using requires multiple screens.
The data then gets stored into firebase. All of my other fields get uploaded to firebase but, the latitude and longitude do not show up at all.
Here is my code.
import UIKit
import CoreLocation
class SignUpLocation: UIViewController {
lazy var geocoder = CLGeocoder()
var latitude: String!
var longitude: String!
//Geocoding
private func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) {
if let error = error {
print("Unable to Forward Geocode Address (\(error))")
locationLabel.text = "Unable to Find Location for Address"
} else {
var location: CLLocation?
if let placemarks = placemarks, placemarks.count > 0 {
location = placemarks.first?.location
}
if let location = location {
let coordinate = location.coordinate
locationLabel.text = "\(coordinate.latitude), \(coordinate.longitude)"
latitude = "\(coordinate.latitude)"
longitude = "\(coordinate.longitude)"
} else {
locationLabel.text = "No Matching Location Found"
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let thirdVC = segue.destination as! SignUpContact
//:::: Initial Sign Up Values
thirdVC.firstNameVar2 = firstNameVar
thirdVC.businessNameVar2 = businessNameVar
thirdVC.emailVar2 = emailVar
thirdVC.passwordVar2 = passwordVar
//:::: Second Sign Up Values
thirdVC.streetAddressVar = streetAddressTextField.text!
thirdVC.cityVar = cityTextField.text!
thirdVC.countryVar = countryTextField.text!
thirdVC.stateVar = stateTextField.text!
thirdVC.zipcodeVar = zipcodeTextField.text!
thirdVC.latitudeVar = latitude
thirdVC.longitudeVar = longitude
}

How to encode [CLLocation] properly in swift?

I am doing an application which need to store the user's data in local. I read the documentation in Apple website, it covers part of the info in how to encode basic type, such as String and Int. However, when I try to encode with the [CLLocation]type, it failed. I am asking if some expert can give me any hint on how to encode such type in Swift?
Here is my code about the Model class.
import Foundation
import UIKit
import CoreLocation
import os.log
import CoreData
//route model
//store the model in the local database.
//change all the [CLLocations] to become the String inorder to store it in local.???? not willing
class Route: NSObject, NSCoding {
//MARK: Properties
var name :String
var area : String
var image: UIImage?
var date : DispatchTime
var routePoints = [CLLocation]()
var rating: Int
//MARK: Achieving paths
//static properties, belong to the
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("Routes")
//MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: PropertyKey.name)
aCoder.encode(image, forKey: PropertyKey.image)
aCoder.encode(date, forKey: PropertyKey.date)
aCoder.encode(area, forKey: PropertyKey.area)
//unable to encode the [cllocation]
aCoder.encode(routePoints, forKey: PropertyKey.routePoints)
aCoder.encode(rating, forKey: PropertyKey.rating)
}
//should also added in the locations as one of the variable.
init?(Name: String, Area: String, Date: DispatchTime, Image: UIImage?, RoutePoints: [CLLocation], Rating: Int) {
guard (Rating >= 0) && (Rating <= 5) else {
return nil
}
self.name = Name
self.area = Area
self.image = Image
self.date = Date
self.routePoints = RoutePoints
self.rating = Rating
}
required convenience init?(coder aDecoder: NSCoder){
//this is the necessary property
//these are optional properties
let rating = aDecoder.decodeInteger(forKey: PropertyKey.rating)
guard let date = aDecoder.decodeObject(forKey: PropertyKey.date) as? DispatchTime,
let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String,
let image = aDecoder.decodeObject(forKey: PropertyKey.image) as? UIImage,
let area = aDecoder.decodeObject(forKey: PropertyKey.area) as? String,
let routePoints = aDecoder.decodeObject(forKey: PropertyKey.routePoints) as? [CLLocation] else{
print("Unable to decode")
return nil
}
self.init(Name: name, Area: area, Date: date, Image: image, RoutePoints: routePoints, Rating: rating)
}
//MARK: Types
struct PropertyKey {
static let name = "name"
static let image = "image"
static let date = "date"
static let area = "area"
static let rating = "rating"
static let routePoints = "routePoints"
}
}
Best wishes
The reason is that CLLocation can't be stored with NSCoder. You could implement a simple coder / decoder using a swift map for this type of value to basically store the locations in a dictionary.
let p1 = CLLocation(latitude: 1, longitude: 1)
let p2 = CLLocation(latitude: 2, longitude:2)
var locations = [p1, p2]
let codedArray = locations.map{ ["lat":$0.coordinate.latitude, "long":$0.coordinate.longitude] }
let decodedArray = codedArray.map{ CLLocation(latitude:$0["lat"]!, longitude:$0["long"]!) }

iOS App Mapview Line Draw

Having problem in displaying polyline on the mapview
Following this tutorial
MapView Tutorial
Attached is my code.
Annotation is appearing on the map but unable to call the renderer method. Though the delegate is there.
Main Problem: Unable to draw line between two coordinates
Console Output: 2017-02-06 22:54:56.770584 MapTest[2329:805733] [LogMessageLogging] 6.1 Unable to retrieve CarrierName. CTError: domain-2, code-5, errStr:((os/kern) failure)
Here is the code
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
#IBOutlet weak var myMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// 1.
myMap.delegate = self
// 2.
let sourceLocation = CLLocationCoordinate2D(latitude: 40.759011, longitude: -73.984472)
let destinationLocation = CLLocationCoordinate2D(latitude: 40.748441, longitude: -73.985564)
// 3.
let sourcePlacemark = MKPlacemark(coordinate: sourceLocation, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
// 4.
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
// 5.
let sourceAnnotation = MKPointAnnotation()
sourceAnnotation.title = "Times Square"
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.title = "Empire State Building"
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
// 6.
self.myMap.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )
// 7.
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .automobile
// Calculate the direction
let directions = MKDirections(request: directionRequest)
// 8.
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.myMap.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.myMap.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
print("Line 85 is being called......start...")
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 4.0
print("Line 85 is being called.......end..")
return renderer
}
}
Your rendererForOverlay function has the wrong syntax; Xcode told me this when testing your code. Use
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
Instead, and a line will be drawn between the two points.

How to use user location out of locationmanager function swift 3

i am kinda new to coding and have come up with this problem. I im making af trashcanfinder app based on MapKit and I would like to show the distance from the user location to my pins on the map. I have already made my function to convert a CLLocationCoordinate2D to a CLLocation, and have a function to show distance from one point to another, but I can't seem to use the user location out of the locationmanager function.
This is my code:
#IBOutlet weak var mapView: MKMapView!
let manager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
}
func distancePointToPoint(location1: CLLocationCoordinate2D, location2: CLLocationCoordinate2D) -> String {
let cllocation1 = converter(location: location1)
let cllocation2 = converter(location: location2)
let distance = cllocation1.distance(from: cllocation2)
let shortDistance = distance - distance.truncatingRemainder(dividingBy: 0.1) //makes sure there is only one number behind comma
if shortDistance < 0 {
return "The distance is \(shortDistance) meters"
}
else {
return "The distance is \(shortDistance - (shortDistance / 1000).truncatingRemainder(dividingBy: 0.1)) kilometers"
}
}
So I need to use the user location as location1 in the distancePointToPoint function.
I know that with any other function I could just return the value, but I don't have any values to give the locationmanager function as it will get it from the device itself.
I already have done research and the only other way I have found is to put myLocation variable outside of the function and only change the value inside the function, but the xcode starts to complain that the class doesn't have an initializer and I don't know what to do.
Sorry if I made any stupid mistakes as I have just started to learn code.
Any help is appreciated!
Here is the LocationManager didUpdate Methods :
//MARK: LocationManager Delegates Methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//Get Current Location
let location = locations.last! as CLLocation
let userLocation:CLLocation = locations[0] as CLLocation
//Save current lat long
UserDefaults.standard.set(userLocation.coordinate.latitude, forKey: "LAT")
UserDefaults.standard.set(userLocation.coordinate.longitude, forKey: "LON")
UserDefaults().synchronize()
}
And then create a function and which gets called from viewWillAppear Method :
func createPin(){
//Access user Location LAT & LON from User Defaults
let coordinate = CLLocationCoordinate2D(latitude: UserDefaults.standard.value(forKey: "LAT") as! CLLocationDegrees, longitude: UserDefaults.standard.value(forKey: "LON") as! CLLocationDegrees)
var region = MKCoordinateRegion(center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
region.center = coordinate
self.map.setRegion(region, animated: true)
//Also now you have coordintes know for USER Location you can add annotation too
//Rest do your stuff
}
Feel free to comment if any further issue. Thanks