I have some locations ( in this case >3000 ) stored with Core Data. Once I open the map, I fetch the locations and store them in an array. Each time the mapview region is changed I call a function which will calculate which annotations are visible in the current visibleMaprect and filter them by pixel-distance. ( I know there would be more complex optimizations, like quadtrees, but I would not really implement it right now, if it’s not extremely necessary ).
This is my code :
//locations is an array of NSManagedObjects
for (int i =0 ; i < [locations count]; i++)
{
// managed object class for faster access, valueforkey takes ages ...
LocationEntity * thisLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake( [thisLocation.latitude doubleValue], [thisLocation.longitude doubleValue]) ;
// mapRect is mapView.visibleMapRect
BOOL isOnScreen = MKMapRectContainsPoint(mapRect, MKMapPointForCoordinate(coord));
if (isOnScreen)
{
CGPoint cgp = [mapView convertCoordinate:coord toPointToView:mapView];
// compare the distance to already existing annotations
for (int idx = 0; idx < [annotations count] && hasEnoughDistance; idx++)
{
CGPoint cgp_prev = [mapView convertCoordinate:[[annotations objectAtIndex:idx] coordinate] toPointToView:mapView];
if ( getDist(cgp, cgp_prev) < dist ) hasEnoughDistance = FALSE;
}
}
if (hasEnoughDistance)
// if it's ok, create the annotation, add to an array and after the for add all to the map
}
The map is freezing for a few seconds after each zoom/movement.
I checked with time profiler and the simple obtainment of coordinates is taking sometimes 1 whole second, sometimes just 0.1, even though the coordinates are indexed attributes in my model… Also these type of lines seem to take ages :
CGPoint cgp = [mapView convertCoordinate:coord toPointToView:mapView];
Any suggestions how could I calculate the pixel/point distance between two annotations/coordinates without going through this function ? Or any optimization suggestions for Core Data?
Thanks 🙂
Ok, I sort of missed the not having them too close bit from your explanation. The conversion between the coordinates is very slow. The way you can alleviate it is to precompute the coordinates into map points with
MKMapPointForCoordinateand store them persistently – they only depend on the coordinates. Then you can quickly calculate the distance between the map points of two annotations, scale it depending on your current zoom level of the map and this will quite closely relate to the actual distance on the screen. It should be accurate enough and will be much faster.I would recommend calculating the squared distance and comparing it to squared
dist. You would be saving a lot on thesqrt().If you still get boggled down on
getDist()(orgetSqDist()) you could either go for a kd tree or use the Accelerate Framework to do the calculations. I’ve done the latter when I needed to calculate distances between many points and the speedup was very good. But the details of this is an another cup of tea. Let me know if you need any help with that.The fact that your coordinates are indexed would only help if you actually searched for annotations by the coordinates, so it won’t help if you just look through all of them.
A way of dealing with long loading times from CoreData would be to try making your annotations as lightweight as possible, so only storing the coordinates and map points. Then you could have a way of getting the rest of the annotation data as needed. This could be done with the proxy pattern.
One more thing. Fast enumeration might be faster and is better practice as well, so
instead of