Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implemented the algorithm

The two classes can now handle the points aggregation properly.
  • Loading branch information...
commit f674df53019a224bf5a5d7c555d0d893cc44fd16 1 parent a268a17
Fabiano Francesconi authored
Showing with 200 additions and 71 deletions.
  1. +11 −1 FFMapRoute.h
  2. +41 −2 FFMapRoute.m
  3. +4 −7 FFMapRoutes.h
  4. +144 −61 FFMapRoutes.m
12 FFMapRoute.h
View
@@ -1,6 +1,5 @@
//
// FFMapRoute.h
-// iScout
//
// Created by Fabiano Francesconi on 19/07/10.
// Copyright 2010 Fabiano Francesconi. All rights reserved.
@@ -10,11 +9,22 @@
#import <MapKit/MapKit.h>
@interface FFMapRoute : NSObject {
+ NSMutableArray *points;
NSUInteger level;
MKPolyline *line;
}
@property (nonatomic, retain) MKPolyline *line;
+@property (nonatomic, retain) NSMutableArray *points;
@property (nonatomic, assign) NSUInteger level;
+/* Init the object with a segment */
+- (id) initWithSegment:(NSArray *)segment;
+
+/* Add the points contained in newPoints array to the local array*/
+- (void) addPointsFromArray:(NSArray *)newPoints;
+
+/* Add coordinate to the local array by wrapping it into a NSData object */
+- (void) addPoint:(CLLocationCoordinate2D) coordinate;
+
@end
43 FFMapRoute.m
View
@@ -1,6 +1,5 @@
//
// FFMapRoute.m
-// iScout
//
// Created by Fabiano Francesconi on 19/07/10.
// Copyright 2010 Fabiano Francesconi. All rights reserved.
@@ -8,11 +7,51 @@
#import "FFMapRoute.h"
+@interface FFMapRoute ()
+@end
+
@implementation FFMapRoute
+- (id) initWithSegment:(NSArray *)segment {
+ if (self = [super init]) {
+ points = [[NSMutableArray alloc] initWithArray:segment];
+
+ MKMapPoint *mapPoints = malloc(sizeof(MKMapPoint) * [points count]);
+ int idx=0;
+
+ for (NSData *value in points) {
+ CLLocationCoordinate2D *coordinate = (CLLocationCoordinate2D *) [value bytes];
+ MKMapPoint point = MKMapPointForCoordinate(*coordinate);
+ mapPoints[idx] = point;
+ idx++;
+ }
+
+ /* Generate the new line */
+ MKPolyline *segmentLine = [MKPolyline polylineWithPoints:mapPoints count:[points count]];
+
+ self.line = segmentLine;
+ }
+
+ return self;
+}
+
- (void) dealloc {
- [line dealloc];
+ [line release];
+ [points release];
+
[super dealloc];
}
+#pragma mark -
+#pragma mark Public Methods
+
+- (void) addPointsFromArray:(NSArray *)newPoints {
+ [points addObjectsFromArray:newPoints];
+}
+
+- (void) addPoint:(CLLocationCoordinate2D) coordinate {
+ NSData *newPoint = [NSData dataWithBytes:&coordinate length:sizeof(CLLocationCoordinate2D)];
+ [points addObject:newPoint];
+}
+
@end
11 FFMapRoutes.h
View
@@ -1,6 +1,5 @@
//
// FFMapRoute.h
-// iScout
//
// Created by Fabiano Francesconi on 10/07/10.
// Copyright 2010 Fabiano Francesconi. All rights reserved.
@@ -12,22 +11,20 @@
#import "FFMapRoute.h"
/* Each kAGGREGATION_FACTOR points, aggregate those in a single polyline */
-#define kAGGREGATION_FACTOR 10
+#define kAGGREGATION_FACTOR 3
@interface FFMapRoutes: NSObject {
- NSMutableArray *points;
- NSMutableArray *lines;
-
+ /* The target mapview that will handle overlays */
MKMapView *mapView;
CLLocationCoordinate2D lastCoordinate;
/* Array of routes */
- FFMapRoute **routes;
+ NSMutableArray *routes;
}
@property (nonatomic, assign) MKMapView *mapView;
-- (void) addCoordinate:(CLLocationCoordinate2D)coordinate;
+- (void) addCoordinate:(CLLocationCoordinate2D) coordinate;
@end
205 FFMapRoutes.m
View
@@ -1,6 +1,5 @@
//
// FFMapRoute.m
-// iScout
//
// Created by Fabiano Francesconi on 10/07/10.
// Copyright 2010 Fabiano Francesconi. All rights reserved.
@@ -9,102 +8,186 @@
#import "FFMapRoutes.h"
@interface FFMapRoutes ()
-- (void) addOverlayToMapViewWithCoordinate:(CLLocationCoordinate2D) coordinate;
-- (void) manageOverlaysInMapView;
-
-@property (nonatomic, retain) NSMutableArray *points;
-@property (nonatomic, retain) NSMutableArray *lines;
-@property (nonatomic, assign) CLLocationCoordinate2D lastCoordinate;
+- (void) addCoordinate:(CLLocationCoordinate2D)coordinate toArray:(NSMutableArray *)array;
+- (void) aggregateRoutes;
+BOOL areCoordinateEqual(CLLocationCoordinate2D aCoordinate, CLLocationCoordinate2D bCoordinate);
+- (void) aggregatePoints:(NSArray *)points toArray:(NSMutableArray *)array;
+- (void) checkAggregationIsNeeded;
@end
@implementation FFMapRoutes
- (id) init {
if (self = [super init]) {
- points = [[NSMutableArray alloc] init];
- lines = [[NSMutableArray alloc] init];
- *routes = malloc(class_getInstanceSize([FFMapRoute class]) * kAGGREGATION_FACTOR);
+ routes = [[NSMutableArray alloc] init];
}
return self;
}
- (void) dealloc {
- [points release];
- [lines release];
+ [routes release];
[super dealloc];
}
#pragma mark -
+#pragma mark Public Methods
-- (void) addCoordinate:(CLLocationCoordinate2D)coordinate {
- /* Wrap the coordinate inside a NSData object */
- NSData *value = [NSData dataWithBytes:&coordinate length:sizeof(CLLocationCoordinate2D)];
- [points addObject:value];
-
- [self addOverlayToMapViewWithCoordinate:coordinate];
-}
-
-- (void) addOverlayToMapViewWithCoordinate:(CLLocationCoordinate2D) coordinate {
+- (void) addCoordinate:(CLLocationCoordinate2D) coordinate {
/* If we don't have a last coordinate, then wait for a second point */
if ((lastCoordinate.latitude == 0) && (lastCoordinate.longitude == 0)) {
lastCoordinate = coordinate;
return;
}
+
+ NSMutableArray *lastroutes = [routes lastObject];
- /* If we have kAGGREGATION_FACTOR segments, aggregate those */
- if ([lines count] == kAGGREGATION_FACTOR) {
- [self manageOverlaysInMapView];
- } else {
- /* Add the new segment */
- MKMapPoint *mapPoints = malloc(sizeof(MKMapPoint) * 2);
- mapPoints[0] = MKMapPointForCoordinate(lastCoordinate);
- mapPoints[1] = MKMapPointForCoordinate(coordinate);
-
- /* Generate the polyline */
- MKPolyline *segmentLine = [MKPolyline polylineWithPoints:mapPoints count:2];
-
- /* Add the segment to the mapview overlays */
- [mapView addOverlay:segmentLine];
- [lines addObject:segmentLine];
+ /* If the last object does not exist means that the routes array is empty.
+ So create the first array */
+ if (!lastroutes) {
+ NSMutableArray *newroutes = [[NSMutableArray alloc] initWithCapacity:kAGGREGATION_FACTOR];
+ [self addCoordinate:coordinate toArray:newroutes];
- free(mapPoints);
+ [routes addObject:newroutes];
+
+ [newroutes release];
}
-
+ /* Otherwisely, we need to check the level of the last array */
+ else {
+ /* Extract the existing route */
+ FFMapRoute *route = [lastroutes lastObject];
+
+ /* If the level is the minor one, than we are good */
+ if (route.level == 1)
+ [self addCoordinate:coordinate toArray:lastroutes];
+
+ /* Otherwisely, we have to create a bogus element for all the missing levels */
+ else {
+ int currentLevel = route.level;
+ for (int i=(currentLevel - 1); i>=1; i--) {
+ NSMutableArray *placeholder = [[NSMutableArray alloc] initWithCapacity:kAGGREGATION_FACTOR];
+ FFMapRoute *route = [[FFMapRoute alloc] init];
+ route.level = i;
+ [placeholder addObject:route];
+ [routes addObject:placeholder];
+
+ [route release];
+ [placeholder release];
+ }
+ [self addCoordinate:coordinate];
+ }
+ }
+
lastCoordinate = coordinate;
+
+ [self checkAggregationIsNeeded];
}
-- (void) manageOverlaysInMapView {
- /* Aggregate all the points into a single polyline */
- MKMapPoint *mapPoints = malloc(sizeof(MKMapPoint) * [points count]);
- int idx = 0;
+#pragma mark -
+#pragma mark Private Methods
+
+BOOL areCoordinateEqual(CLLocationCoordinate2D aCoordinate, CLLocationCoordinate2D bCoordinate) {
+ if ((aCoordinate.latitude == bCoordinate.latitude) &&
+ (aCoordinate.longitude == bCoordinate.longitude))
+ return TRUE;
+ return FALSE;
+}
+
+- (void) addCoordinate:(CLLocationCoordinate2D)coordinate toArray:(NSMutableArray *)array {
+ FFMapRoute *route;
+
+ /* Wrap the new segment into a small array and add it to the newroutes array */
+ NSMutableArray *segment = [[NSMutableArray alloc] initWithCapacity:2];
+ NSData *aPoint = [NSData dataWithBytes:&lastCoordinate length:sizeof(CLLocationCoordinate2D)];
+ NSData *bPoint = [NSData dataWithBytes:&coordinate length:sizeof(CLLocationCoordinate2D)];
- for (NSData *value in points) {
- CLLocationCoordinate2D *coordinate = (CLLocationCoordinate2D *) [value bytes];
- MKMapPoint point = MKMapPointForCoordinate(*coordinate);
- mapPoints[idx] = point;
- idx++;
+ [segment addObject:aPoint];
+ [segment addObject:bPoint];
+
+
+ /* Check if we are in a bogus/placeholder situation */
+ route = [array lastObject];
+
+ /* if the polyline doesn't exist, then we have an empty class. Remove it and keep with the job. */
+ if (route && (route.line == nil)) {
+ [array removeLastObject];
+ route = nil;
}
- /* Generate the new line */
- MKPolyline *segmentLine = [MKPolyline polylineWithPoints:mapPoints count:[points count]];
+ /* Otherwisely we have to create it */
+ route = [[FFMapRoute alloc] initWithSegment:segment];
+ /* Any new route has a level value of 1 */
+ route.level = 1;
+ [array addObject:route];
+
+ [mapView addOverlay:[route line]];
+
+ [route release];
+ [segment release];
+}
+
+- (void) checkAggregationIsNeeded {
+ /* check if aggregation algorithm should run */
+ NSArray *lastroutes = [routes lastObject];
+
+ if ([lastroutes count] == kAGGREGATION_FACTOR) {
+ [self aggregateRoutes];
+ [self checkAggregationIsNeeded];
+ }
+}
- /* Discard the old polyline */
- NSMutableArray *discardedLines = [NSMutableArray array];
+- (void) aggregateRoutes {
+ NSArray *lastroutes;
+ NSMutableArray *prevroutes;
- for (MKPolyline *line in lines) {
- [discardedLines addObject:line];
- [mapView removeOverlay:line];
+ /* Extract the last points and remove those from the routes array */
+ lastroutes = [[routes lastObject] retain];
+ [routes removeLastObject];
+
+ /* Extract the previously aggregated array, if any, and performs few checks */
+ prevroutes = [routes lastObject];
+
+ /* If it does not exist, then we have only to aggregate the lastroutes and higher its level */
+ if (!prevroutes) {
+ NSMutableArray *newAggregatePoints = [[NSMutableArray alloc] initWithCapacity:kAGGREGATION_FACTOR];
+ [self aggregatePoints:lastroutes toArray:newAggregatePoints];
- printf("Removed an overlay\n");
+ [routes addObject:newAggregatePoints];
+ [newAggregatePoints release];
+ }
+ /* Else, append it to the existing one */
+ else {
+ [self aggregatePoints:lastroutes toArray:prevroutes];
+ }
+}
+
+- (void) aggregatePoints:(NSArray *)points toArray:(NSMutableArray *)array {
+ CLLocationCoordinate2D prev;
+ NSMutableArray *newPoints = [[NSMutableArray alloc] init];
+ int level = 1;
+
+ /* Check all the routes */
+ for (FFMapRoute *route in points) {
+ for (NSData *value in [route points]) {
+ CLLocationCoordinate2D *coordinate = (CLLocationCoordinate2D *) [value bytes];
+ if (areCoordinateEqual(prev, *coordinate))
+ break;
+ [newPoints addObject:value];
+ prev = *coordinate;
+ }
+ level = route.level;
+ /* Remove the overlay */
+ [mapView removeOverlay:route.line];
}
- [lines removeObjectsInArray:discardedLines];
+ FFMapRoute *newRoute = [[FFMapRoute alloc] initWithSegment:newPoints];
+ [newPoints release];
+ newRoute.level = ++level;
- /* Add the new polyline */
- [mapView addOverlay:segmentLine];
- [lines addObject:segmentLine];
+ [mapView addOverlay:[newRoute line]];
- free(mapPoints);
+ [array addObject:newRoute];
+ [newRoute release];
}
-@end
+
+@end
Please sign in to comment.
Something went wrong with that request. Please try again.