Guest User

Untitled

a guest
Feb 23rd, 2018
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.19 KB | None | 0 0
  1. // JKHeatMapEngine.swift
  2. // JKTinder
  3. // Created by Joseph Kalash on 2/10/18.
  4. // Copyright © 2018 Joseph Kalash. All rights reserved.
  5.  
  6. import Alamofire
  7. import DeepDiff
  8.  
  9. // Note: This manager only deals with profile IDs and does not store any other info. Once user selectes a node in the
  10. // cluster data, the array of profileIDs are passed to the next VC where the profile details are fetched and displayed to the user
  11.  
  12. struct DistanceEstimate : Hashable {
  13. var distance : JKTrilateration.Circle //The central point estimate
  14. var weight : Double //Estimated error on the distance
  15.  
  16. // JKTrilateration.Circle already conforms to Hashable
  17. var hashValue: Int {
  18. return distance.hashValue
  19. }
  20.  
  21. // JKTrilateration.Circle already conforms to Hashable thus conforms to Equatable as well
  22. static func == (lhs: DistanceEstimate, rhs: DistanceEstimate) -> Bool {
  23. return lhs.distance == rhs.distance && lhs.weight == rhs.weight
  24. }
  25. }
  26.  
  27. struct LocationEstimate : Hashable {
  28. var estimatedDistances : Set<DistanceEstimate> //The set of estimated distances
  29. var estimatedLocation : Vector2?
  30.  
  31. // JKTrilateration.Circle already conforms to Hashable
  32. var hashValue: Int {
  33. return estimatedDistances.hashValue
  34. }
  35.  
  36. // JKTrilateration.Circle already conforms to Hashable thus conforms to Equatable as well
  37. static func == (lhs: LocationEstimate, rhs: LocationEstimate) -> Bool {
  38. return lhs.estimatedDistances == rhs.estimatedDistances && lhs.estimatedLocation == rhs.estimatedLocation
  39. }
  40.  
  41. mutating func locate() {
  42.  
  43. var knownPos : [[Double]] = []
  44. var dist : [Double] = []
  45.  
  46. let distances : [JKTrilateration.Circle] = self.estimatedDistances.flatMap({ $0.distance })
  47. for d in distances {
  48. knownPos.append([d.center.x, d.center.y])
  49. dist.append(d.radius)
  50. }
  51.  
  52. //Convert distance to km
  53. dist = dist.map({ $0 / 1000 })
  54.  
  55. let weights = self.estimatedDistances.flatMap({ $0.weight })
  56.  
  57. let optimizer = NonLinearLeastSquareOptimizer(knownPos: knownPos, dist: &dist, weights: weights)
  58. self.estimatedLocation = optimizer.getLocation()
  59. }
  60. }
  61.  
  62. //Continuously run :
  63. //1. Fetch random point in the boundary defined by circle at center of mapView / radius zoomlevel
  64. //2. Request nearby profiles and store all distance estimates in userLocations data structure
  65. //3. Run Levmarq least square optimization on all profiles that have at least 3 entry points
  66. //4. Refresh locatedUsers with new/updated estimated locations
  67. //5. Refresh MapView with the new points
  68.  
  69. class JKHeatMapEngine {
  70.  
  71.  
  72. // Stores all points found for given profile IDs. Maps profile IDs to a set of estimates
  73. // Each estimated point distance has a weight; the error on the computation (used to regularize in ML training)
  74. // Also holds that have been located, alongside their location. Maps a profile id to a location.
  75. internal var userLocations : [String : LocationEstimate] = [:]
  76.  
  77. public var runner : DataRequest? {
  78. didSet {
  79. oldValue?.cancel() //Cancel any existing sequest when being reassigned
  80. }
  81. }
  82.  
  83. //Repeatedly call the runner until either method called again or runner canceled by the controller
  84. public func fetchUsers(mapCenter : Vector2, mapZoomRadius : Double, callback : @escaping (_ diff: Array<Change<[String : Vector2]>>) -> Void) {
  85. let randomPoint = Utils.randomPointInCircle(JKTrilateration.Circle(center: mapCenter, radius: mapZoomRadius))
  86.  
  87. runner = APIManager.current.fetchNearbyAt(loc: randomPoint, callback: { (json, error) in
  88.  
  89. guard let dict = json, let profiles = dict["profiles"] as? [NSDictionary] else {
  90. self.fetchUsers(mapCenter: mapCenter, mapZoomRadius: mapZoomRadius, callback: callback)
  91. return
  92. }
  93.  
  94. //Store to compute DeepDiff
  95. var profileIdsToLocate : [String] = []
  96.  
  97. //A user either has a distance shared --> Append a point
  98. for i in 0 ..< profiles.count {
  99. if let profileId = profiles[i]["profileId"] as? String {
  100. var estimate : DistanceEstimate? = nil
  101. if let distance = profiles[i]["distance"] as? Double {
  102. estimate = DistanceEstimate(distance: JKTrilateration.Circle(center: randomPoint, radius: distance), weight: 5.0)
  103. }
  104. else {
  105. //Find the approximate distance
  106. if let (distance, w) = Utils.estimateDistance(of: i, profiles: profiles) {
  107. estimate = DistanceEstimate(distance: JKTrilateration.Circle(center: randomPoint, radius: distance), weight: w)
  108. }
  109. }
  110.  
  111. //If we somehow found no estimate, skip
  112. if estimate == nil { continue }
  113.  
  114. //Check if user location already a record
  115. if self.userLocations[profileId] != nil {
  116. self.userLocations[profileId]!.estimatedDistances.insert(estimate!) //Appent to existing set
  117.  
  118. //Locates profiles whose distance found is anywhere between 3rd and 6th
  119. if self.userLocations[profileId]!.estimatedDistances.count == 3 {
  120. profileIdsToLocate.append(profileId)
  121. }
  122. }
  123. else {
  124. //Create initial set
  125. self.userLocations[profileId] = LocationEstimate(estimatedDistances: [estimate!], estimatedLocation: nil)
  126. }
  127. }
  128. }
  129.  
  130. //Compute distances
  131. for id in profileIdsToLocate {
  132. self.userLocations[id]!.locate()
  133. }
  134.  
  135. //Repeat as long as we were able to locate users
  136. if profileIdsToLocate.count > 0 {
  137. self.fetchUsers(mapCenter: mapCenter, mapZoomRadius: mapZoomRadius, callback: callback)
  138. }
  139. }, applyFilterParams: false)
  140. }
  141.  
  142. }
Add Comment
Please, Sign In to add comment