What is the fastest way to sort a lot of locations on distance?

邮差的信 提交于 2019-12-05 10:05:53

My final solution is as follows: I select all waypoints within 1 degree latitude and longitude (about 1000 waypoints normally) and then compute and store the distance to the current position in the table. I can then sort on the distance. The slow thing would be the saving in Core Data. But after sorting (and fetching), I simply cancel the changes. The saving was taking more than 90 % of the whole thing, so this worked quite well.

Peter McEvoy

Sounds like you need to convert your locations to positions on a Hilbert curve - then points "near" you are a simple subtraction?

Mapping N-dimensional value to a point on Hilbert curve

Can't say I know the technique inside out, but that's where I'd start to look

If what's important is the order (as opposed to having accurate distances), you could sort slices of the waypoint sequence in a moving window (that is, sort items i to i+n, where i changes). Start at the beginning of the waypoint sequence. Sort n items (n = 10 is a good place to start). If any items changed position, move the window forward by n/2 (experiment with different offsets or algorithms to choose an offset) and repeat. Depending on how dense the waypoint are near the current location, I would expect this to stop after only a few sorts.

Note that I haven't thought about this long enough to say whether or not this will actually work.

Of the three options you mention, I like using threads the most. That's the classical way of handling a non-responsive UI when it's blocked by heavy computation.

I store my locations in the data model as latitude/longitude coordinates. Then I wrote some helper extensions to find a semirectangular region of lat/lon coordinates and query by that. Here's the code I'm using. I know the question was for Objective-C but the question is old and likely most people are looking for a Swift answer anyway now.

Swift 3

   extension CLLocationDistance {
        var feet: Double {
            return self * 3.28084
        }
        var miles: Double {
            return self.feet / 5280.0
        }
    }

    extension CLLocationDegrees {
        static var north: CLLocationDegrees {
            return 90.0
        }
        static var south: CLLocationDegrees {
            return -90.0
        }
        static var east: CLLocationDegrees {
            return 180.0
        }
        static var west: CLLocationDegrees {
            return -180.0
        }

        var radians: Double {
            return Double.pi * self / 180.0
        }
    }

    extension CLLocationCoordinate2D {
        static var origin: CLLocationCoordinate2D {
            return CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        }

        static var northPole: CLLocationCoordinate2D {
            return CLLocationCoordinate2D(latitude: 90.0, longitude: 0.0)
        }

        static var southPole: CLLocationCoordinate2D {
            return CLLocationCoordinate2D(latitude: 90.0, longitude: 0.0)
        }

        var metersPerDegreeLatitude: CLLocationDistance {
            return 111319.4907932736
        }
        var metersPerDegreeLongitude: CLLocationDistance {
            return max(0.0, cos(self.latitude.radians) * self.metersPerDegreeLatitude)
        }
    }

    extension CLCircularRegion {
        var northernmostLatitude: CLLocationDegrees {
            let longitude = self.center.latitude + self.radius / self.center.metersPerDegreeLatitude
            return min(longitude, .north)
        }

        var southernmostLatitude: CLLocationDegrees {
            let longitude = self.center.latitude - self.radius / self.center.metersPerDegreeLatitude
            return max(longitude, .south)
        }

        var easternmostLongitude: CLLocationDegrees {
            guard self.northernmostLatitude <= .north else {
                return .east
            }
            guard self.southernmostLatitude >= .south else {
                return .east
            }
            return min(.east, self.center.longitude + self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
        }

        var westernmostLongitude: CLLocationDegrees {
            guard self.northernmostLatitude <= .north else {
                return .west
            }
            guard self.southernmostLatitude >= .south else {
                return .west
            }
            return max(.west, self.center.longitude - self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
        }

        func buildPredicate(latitudeName: String = "latitude", longitudeName: String = "longitude") -> NSPredicate {
            let args = [self.southernmostLatitude, self.northernmostLatitude, self.westernmostLongitude, self.easternmostLongitude]
            return NSPredicate(format: "\(latitudeName) >= %@ && \(latitudeName) <= %@ && \(longitudeName) >= %@ && \(longitudeName) <= %@", argumentArray: args)
        }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!