More Related Content

Slideshows for you(20)


Zenly - Reverse geocoding

  1. An architecture to handle reverse geocoding
  2. Zenly is a location app that lets you know what friends and family are up to in real-time. The app is focused on location, and therefore relies a lot on adresses. Our users do millions of reverse geocoding's everyday and it has to work :)
  3. The operation to transform a latitude/longitude into an address. Handled by CoreLocation : ReverseGeocoder one simple API. public typealias CLGeocodeCompletionHandler = ([CLPlacemark]?, NSError?) -> Void // reverse geocode requests public func reverseGeocodeLocation(location: CLLocation, completionHandler: CLGeocodeCompletionHandler) May return multiple placemarks. Reverse geocoding
  4. In practice Take one, use it, forget it…. Usually data comes from two providers NAVTEQ and TELEATLAS. Other APIs available : Google (do not mix and match), open data… Simply search for “Free reverse geocoding”. CoreLocation hides everything.
  5. When problems start Some applications may want to display multiple adresses for multiple people, possibly moving … … and we bump into a limit. Apple writes: “avoid more than one per minute and do it when UI needs it”. Problem of count and rate. Empirically it can handle one every few seconds. If too many: longer answer time and then errors. “Boss, we need to pay Google”
  6. Architecture leading to a problem Being purely model driven leads to problems. User Address Reverse Geocode Location Change UI User Address Reverse Geocode Location Change User Address Reverse Geocode Location Change
  7. Thinking bias Get UI components ready ASAP to avoid spinning wheel. Optimize in a time based manner. Optimize in the context of one user only.
  8. Improvements Global. Cache. Scheduling. Priority and UI driven. Track identifier. Do only what is necessary!
  9. Global operation Avoid to use “reverse geocoding on the go”. One available through a unique object (Singleton). Handle both time based and position based consideration and optimization. Make sure an operation can be redistributed through multiple places. Only one object is responsible.
  10. Data structure func reverseGeocode<T:ReverseGeocodable>(geocodableObject:T?, identifier:String?, priority:ReverseGeocodingPriority, completionHandler: CLGeocodeCompletionHandler?) -> ReverseGeocodeResult struct GeopositioningRunningInfo:CustomStringConvertible { var priority:ReverseGeocodingPriority = ReverseGeocodingPriority.Normal var context:Any? = nil var handlers:[Any] = [] var identifier:String? }
  11. Cache Based on coordinates….but how to encode this ? Geohash: a division of the planet and many nice things (neighbors…). See Wikipedia !. Must have a resolution: level. May depend on where you are! Implementation through an NSCache where key is geohash and value the originally received CLPlacemarks. Scheduled operations should be considered: multiple handlers. Multiple people in the same place.
  12. Geohash samples //Paris: Zenly office var aPos:Pos = Pos(latitude:48.868117, longitude:2.355915) var geoHash:String = (ServiceReverseGeocoder.sharedGeocoder.geoHashString(aPos, level: 8))! XCTAssert(geoHash == "u09wj85t") geoHash = (ServiceReverseGeocoder.sharedGeocoder.geoHashString(aPos, level: 3))! XCTAssert(geoHash == "u09") geoHash = (ServiceReverseGeocoder.sharedGeocoder.geoHashString(aPos, level: 1))! XCTAssert(geoHash == "u") Downscaled information in no network.
  13. Scheduling Avoid flooding the Apple Reverse Geocoder. Schedule operation on a heartbeat (timer). Do nothing in the background. Stop when all is done. If error: next operation can be progressively delayed. Don’t trap yourself!
  14. Scheduling Principle func reverseGeocode<T:ReverseGeocodable>(geocodableObject:T?, identifier:String?, priority:ReverseGeocodingPriority, completionHandler: CLGeocodeCompletionHandler?) -> ReverseGeocodeResult { var result = ReverseGeocodeResult.DefaultLaunch //Do pre check //Keep where we are in order to start heartbeat if necessary let countBefore:Int = self.reverseGeocodingCount() //More stuff //Start heartbeat if needed let countAfter:Int = self.reverseGeocodingCount() if 0 == countBefore && countAfter > 0 { self.heartbeat = Foundation.NSTimer.scheduledTimerWithTimeInterval(self.GeocodingServiceHeartbeatValue, target: self, selector:#selector(_doExecuteHeartBeat(_:)) , userInfo: nil, repeats: true) self.heartbeat?.tolerance = self.GeocodingServiceHeartbeatTolerance } return result }
  15. Priority Arbitrary number: 3 sounds good for normal usage. Very simple FIFO model : 3 FIFOs as queues. Heartbeat is unstacking the queues. Operations can be moved between queues. Should be settable on a more permanent basis: use cautiously! Priorities are to be used!!!!
  16. Priority queues HighMedium u09wj85t u09wj86d ke7fynh8 6gyqdy5u 6gyqdy5v drt2xp2mu09wj84f Normal Heart beat
  17. UI driven Medium High Medium
  18. Track identifier Follow someone moving T1 T2 T3 T4 T5 T6
  19. Implementation details Follow someone moving. Identifier can be a user UUID but we can think of other cases (e.g UI). We do not want to encode position of the past. An operation with an identifier replaces the previous one. Should handle both identifier and priority. One more improvement: still keep a delivery ratio.
  20. Thank You!