How to add clicked annotations on mapbox using offline mbtiles files - ios

I have already managed to use interactive offline mbtiles (created on TileMill) in order to:
Load more than 1000 points fast
Make them understand when user is clicking each point and show a popup with the name of each point
But I can't make the bubble with the name clickable again.
I use the following code to generate the layer for the annotation of each point
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation{
RMMarker *marker = [[RMMarker alloc] initWithMapboxMarkerImage:#"embassy"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 32)];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = annotation.userInfo;
marker.leftCalloutAccessoryView = imageView;
marker.canShowCallout = YES;
return marker;
}
and this is how I get the teaser and build my annotation from the mbtiles file:
- (void)singleTapOnMap:(RMMapView *)mapView at:(CGPoint)point{
[mapView removeAllAnnotations];
RMMBTilesSource *source = (RMMBTilesSource *)mapView.tileSource;
if ([source conformsToProtocol:#protocol(RMInteractiveSource)] && [source supportsInteractivity])
{
NSString *formattedOutput = [source formattedOutputOfType:RMInteractiveSourceOutputTypeTeaser
forPoint:point
inMapView:mapView];
if (formattedOutput && [formattedOutput length])
{
// parse the country name out of the content
//
NSUInteger startOfCountryName = [formattedOutput rangeOfString:#"<strong>"].location + [#"<strong>" length];
NSUInteger endOfCountryName = [formattedOutput rangeOfString:#"</strong>"].location;
NSString *countryName = [formattedOutput substringWithRange:NSMakeRange(startOfCountryName, endOfCountryName - startOfCountryName)];
// parse the flag image out of the content
//
NSUInteger startOfFlagImage = [formattedOutput rangeOfString:#"base64,"].location + [#"base64," length];
NSUInteger endOfFlagImage = [formattedOutput rangeOfString:#"\" style"].location;
UIImage *flagImage = [UIImage imageWithData:[NSData dataFromBase64String:[formattedOutput substringWithRange:NSMakeRange(startOfFlagImage, endOfFlagImage)]]];
RMAnnotation *annotation = [RMAnnotation annotationWithMapView:mapView coordinate:[mapView pixelToCoordinate:point] andTitle:countryName];
annotation.userInfo = flagImage;
[mapView addAnnotation:annotation];
[mapView selectAnnotation:annotation animated:YES];
}
}
}
UPDATED
I figured out how to do that by using a leftCalloutAccessoryView on the marker (I added the following at the end of layerForAnnotation method :
marker.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
and used the following delegate method to track the event:
-(void)tapOnCalloutAccessoryControl:(UIControl *)control forAnnotation:(RMAnnotation *)annotation onMap:(RMMapView *)map{
NSLog(#"I will now pass to the next screen ! YEAH! %#",annotation.title);
}
The problem now is that I want to get rid off the left calloutAccesoryView. Any suggestions?

None of what you have here about the map is needed. What you really want is the callout (which is of type SMCalloutView, a dependent project to the Mapbox SDK that you are using) to have its clicks registered.
Check out this issue for more details:
https://github.com/mapbox/mapbox-ios-sdk/issues/422

Related

iOS Google Maps, different custom images for Clusters and individual markers

I am using Google Maps SDK in my iOS app. I am populating the map using the clustering methods.
I have set custom images for the different clustering buckets ex. 10,20...
The individual markers however have the default (google maps red marker icon).
I would like a custom icon for clustering and a different one for single markers.
However inside the methods that render the Cluster that add markers, if you set the marker icons it changes all of the images not just singles.
How do I set different icons for singles and clusters?
this adds the items to clusterManager
id<GMUClusterItem> item =
[[POIItem alloc] initWithPosition:CLLocationCoordinate2DMake([bay.latitude doubleValue], [bay.longitude doubleValue]) name:bay.name status:bay.marker_status];
[clusterManager addItem:item];
Here I add the icons for the cluster buckets
- (id<GMUClusterIconGenerator>)iconGeneratorWithImages {
return [[GMUDefaultClusterIconGenerator alloc] initWithBuckets:#[ #10, #50, #100, #200, #1000 ]
backgroundImages:#[
[UIImage imageNamed:#"big_parking_pin_img"],
[UIImage imageNamed:#"big_parking_pin_img"],
[UIImage imageNamed:#"big_parking_pin_img"],
[UIImage imageNamed:#"big_parking_pin_img"],
[UIImage imageNamed:#"big_parking_pin_img"]
]];
}
This is where the google cluster class adds markers
- (void)renderCluster:(id<GMUCluster>)cluster animated:(BOOL)animated {
float zoom = _mapView.camera.zoom;
if ([self shouldRenderAsCluster:cluster atZoom:zoom]) {
CLLocationCoordinate2D fromPosition;
if (animated) {
id<GMUCluster> fromCluster =
[self overlappingClusterForCluster:cluster itemMap:_itemToOldClusterMap];
animated = fromCluster != nil;
fromPosition = fromCluster.position;
}
UIImage *icon = [_clusterIconGenerator iconForSize:cluster.count];
GMSMarker *marker = [self markerWithPosition:cluster.position
from:fromPosition
userData:cluster
clusterIcon:icon
animated:animated];
[_markers addObject:marker];
} else {
for (id<GMUClusterItem> item in cluster.items) {
CLLocationCoordinate2D fromPosition;
BOOL shouldAnimate = animated;
if (shouldAnimate) {
GMUWrappingDictionaryKey *key = [[GMUWrappingDictionaryKey alloc] initWithObject:item];
id<GMUCluster> fromCluster = [_itemToOldClusterMap objectForKey:key];
shouldAnimate = fromCluster != nil;
fromPosition = fromCluster.position;
}
GMSMarker *marker = [self markerWithPosition:item.position
from:fromPosition
userData:item
clusterIcon:nil
animated:shouldAnimate];
[_markers addObject:marker];
[_renderedClusterItems addObject:item];
}
}
[_renderedClusters addObject:cluster];
}
// Returns a marker at final position of |position| with attached |userData|.
// If animated is YES, animates from the closest point from |points|.
- (GMSMarker *)markerWithPosition:(CLLocationCoordinate2D)position
from:(CLLocationCoordinate2D)from
userData:(id)userData
clusterIcon:(UIImage *)clusterIcon
animated:(BOOL)animated {
CLLocationCoordinate2D initialPosition = animated ? from : position;
GMSMarker *marker = [GMSMarker markerWithPosition:initialPosition];
marker.userData = userData;
if (clusterIcon != nil) {
marker.icon = clusterIcon;
marker.groundAnchor = CGPointMake(0.5, 0.5);
}
marker.map = _mapView;
if (animated) {
[CATransaction begin];
[CATransaction setAnimationDuration:kGMUAnimationDuration];
marker.layer.latitude = position.latitude;
marker.layer.longitude = position.longitude;
[CATransaction commit];
}
return marker;
}
I had the similar problem 2 days ago and I just found the solution. Hope it will be useful for you.
For example you have a mapView and you set a delegate to it in right place:
[self.mapView setDelegate:self];
Then you need to implement the optional method from GMSMapViewDelegate protocol:
- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position {
[self performSelector:#selector(updateMarkers) withObject:nil afterDelay:0.2];
}
I use delay 0.2 seconds, because markers wouldn't update their icons if you'll use smaller value.
The next step is implement method for updating icons:
-(void) updateMarkers {
// "mapView" property in your self.mapView has type GMSVectorMapView,
//and it is hidden, so you can't get like self.mapView.mapView
id vectorMap = [self.mapView valueForKey:#"mapView"];
// "accessibilityItems" - property that have all items in visible part of map.
NSMutableArray* GMSMarkersArray = [vectorMap mutableArrayValueForKey:#"accessibilityItems"];
// Very often you'll get object of GMSPointOfInteretUIItem class, and you don't need it =)
NSMutableArray *discardedItems = [NSMutableArray array];
for (id item in GMSMarkersArray) {
if (![item isKindOfClass:[GMSMarker class]])
[discardedItems addObject:item];
}
[GMSMarkersArray removeObjectsInArray:discardedItems];
// If marker don't have icon image, he use default red pin, but property is still have nil-value ...
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"icon = nil"];
NSArray* singleMarkers = [GMSMarkersArray filteredArrayUsingPredicate:predicate];
// ... and here you can setup any icon you want, for all singles markers in visible part of map.
for(GMSMarker* marker in singleMarkers) {
marker.icon = [UIImage imageNamed:#"yourIcon.png"];
}
}
Also if you create your own marker and add it to cluster, you can get it from userData property of GMSMarker object in last loop. And for example you have there your custom marker with icon you want, just change last loop for something like:
for(GMSMarker* marker in singleMarkers) {
YourMarkerClass* yourMaker = marker.userData;
marker.icon = yourMaker.icon;
}
Sorry for possible mistakes and ask the questions if you don't understand something =)

Rotate Annotation Custom Image [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am clearly not getting this. I'm trying to work out how to rotate a custom image of map annotations. i.e. multiple icons pointing different directions. I have loop'ed over a whole bunch of airplane data and I want to simply show the direction the plane is heading.
Review the code that I have managed to piece together to get at least half of it working and provide a suggestive idea on how to turn the image based on a variable.
Abstract View of the loop'ed data
NSMutableArray *locations = [[NSMutableArray alloc]init];
CLLocationCoordinate2D location;
MKPointAnnotation *myAnn;
myAnn = [[MKPointAnnotation alloc]init];
location.latitude = nLatitudeFloat;
location.longitude = nLongitudeFloat;
myAnn.coordinate = location;
myAnn.title = nRealname;
//Add the Annotation object to the list so when the map is presented all points are listed on the map.
[locations addObject:myAnn];
//[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
[self.mapView addAnnotations:locations];
Update of the annotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]]) return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
// Try to dequeue an existing pin view first.
MKAnnotationView *pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// If an existing pin view was not available, create one.
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"airplane21.png"] ;
pinView.calloutOffset = CGPointMake(0, 32);
pinView.transform = CGAffineTransformMakeRotation(30); <------AT THE MOMENT I HAVE JUST HARD CODED THIS TO SEE IF IT WORKS. IT ROTATES THE ENTIRE ANNOTATION
// Add a detail disclosure button to the callout.
//UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//pinView.rightCalloutAccessoryView = rightButton;
// Add an image to the left callout.
UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"airplane21.png"]];
pinView.leftCalloutAccessoryView = iconView;
iconView.transform = CGAffineTransformMakeRotation(30);
} else {
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Any ideas?
I have seen this by the way. I just wasn't confident to know if it would be suitable or not.
Rotate annotation image on MKMapView
I had the same problem and I have a solution that seems to work.
You need to first make a custom annotation that can hold the data that you want (ex. coordinates, your plane heading). When you loop through your data make sure you're using that custom annotation. For example
CustomAnnotation* annotation = [[CustomAnnotation alloc] init];
annotation.coordinates = ...
annotation.bearing = ...
Then in your viewForAnnotation method, you can get that info by doing something like
if ([annotation isKindOfClass:[CustomAnnotation class]]) {
CustomAnnotation* myAnn = (CustomAnnotation*)annotation;
double bearing = myAnn.bearing; // or whatever it's called
...
}
Hope this helped.
Edit: For rotating your image, I found the following code snippet somewhere. It works but it pixellates your image a little bit.
#interface UIImage (RotationMethods)
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees;
#end
#implementation UIImage (RotationMethods)
static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees
{
// calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
// Create the bitmap context
//UIGraphicsBeginImageContext(rotatedSize); // For iOS < 4.0
UIGraphicsBeginImageContextWithOptions(rotatedSize, NO, 0.0);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
// Rotate the image context
CGContextRotateCTM(bitmap, DegreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#end
Stick that in your .m, and on any UIImage you can now do [someUIImage imageRotatedByDegrees:yourDegrees];
So now in your viewForAnnotation method you can do something like
UIImage* image = [[UIImage imageNamed:#"yourImage.png"] imageRotatedByDegrees:degrees];
annView.image = image;

Route-Me: Alpstein fork - Customize cluster icon and display cluster count

I am using Route-Me: Alpstein fork for developing an iOS app. The original Route-Me/Mapbox code had an option to customize the cluster icon and also the the cluster count. I've been looking for a way to do this with the Route-Me: Alpstein fork.
Something similar to this:
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation
{
if (annotation.isUserLocationAnnotation)
return nil;
RMMapLayer *layer = nil;
if (annotation.isClusterAnnotation)
{
layer = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"circle.png"]];
layer.opacity = 0.75;
layer.bounds = CGRectMake(0, 0, 75, 75);
[(RMMarker *)layer setTextForegroundColor:[UIColor whiteColor]];
[(RMMarker *)layer changeLabelUsingText:[NSString stringWithFormat:#"%i", [annotation.clusteredAnnotations count]]];
}
else
{
layer = [[RMMarker alloc] initWithMapboxMarkerImage];
}
return layer;
}
I cannot see 'isClusterAnnotation' defined anywhere in the source. How can I achieve the same results using the Route-Me: Alpstein fork? Any help would be greatly appreciated.
In my project I used the following, inside the mapView:layerForAnnotation: method of the map delegate:
if ([annotation.annotationType isEqualToString:#"RMClusterAnnotation"]) {
UIImage *clusterImage = [UIImage imageNamed:#"foo.png"];
RMMarker *newMarker = [[RMMarker alloc] initWithUIImage:clusterImage];
// this is how I managed to count the annotations inside a cluster
NSInteger annotationsInCluster = [((RMQuadTreeNode *)annotation.userInfo).annotations count];
// you can add a label to the annotation with the number of clustered annotations
[newMarker changeLabelUsingText: [NSString stringWithFormat:#"%i", annotationsInCluster]];
return newMarker;
}
Hope this works for you!

Zoom to random annotation and show bubble when map view loads

I am trying to zoom to a random annotation AND have the bubble open automatically as well.
I am pinning my annotations in the viewDidLoad as follows:
...arrays...
for (int i=0; i<22; i++){
MKPointAnnotation *annot = [[MKPointAnnotation alloc] init];
annot.title = [wineryName objectAtIndex:i];
annot.subtitle = [wineryAddress objectAtIndex:i];
annot.coordinate = CLLocationCoordinate2DMake([[lat objectAtIndex:i]doubleValue], [[lon objectAtIndex:i]doubleValue]);
[mapView setCenterCoordinate:annot.coordinate animated:YES];
[mapView addAnnotation:annot];
then I am styling the bubble as follows:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
//dequeue an existing pin view first
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.pinColor=MKPinAnnotationColorRed;
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 35, 35);
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
[button setImage:[UIImage imageNamed:#"RightArrow.png"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = button;
...arrays...
for (int i = 0; i < 22; i++) {
if ([wineryTitle[i] isEqualToString:[annotation title]]) {
UIImageView *profileIconView = [[UIImageView alloc] init];
profileIconView.frame = CGRectMake(0, 0, 40, 33);
profileIconView.image = [UIImage imageNamed:wineryImage[i]];
pinView.leftCalloutAccessoryView = profileIconView;
[profileIconView release];
break;
}
}
return pinView;
}
then I am trying to zoom to a random location as follows:
- (void)zoomToUserLocation:(MKUserLocation *)userLocation
{
if (!userLocation)
return;
MKCoordinateRegion region;
//zoom to random pin when page loads
int randomNumber = rand() % 22;
switch (randomNumber) {
case 1:
region.center = CLLocationCoordinate2DMake(34.642109, -120.440292);
[self.mapView selectAnnotation:[self.mapView.annotations objectAtIndex:0] animated:TRUE];
break;
case 2:
region.center = CLLocationCoordinate2DMake(34.667408, -120.334781);
[self.mapView selectAnnotation:[self.mapView.annotations objectAtIndex:1] animated:TRUE];
break;
case 3:
...etc
}
region.span = MKCoordinateSpanMake(5.0, 5.0);
region = [self.mapView regionThatFits:region];
[self.mapView setRegion:region animated:YES];
}
ALL OF THIS WORKS< EXCEPT: In the zoomToUserLocation method, the map is zooming to one location and then showing the bubble of a different location. It seems like the random operator is randomly selecting a location and a bubble separately. Does anyone know how to fix this so that the bubble automatically appears at the same location that is randomly selected?
The code in the zoomToUserLocation method is making the wrong assumption that the map view's annotations array only contains the annotations you explicitly added and (more importantly) that the annotations array will be in the same order that you added the annotations in.
Both of those assumptions are unsafe to make.
For some more discussion, see MKMapView annotations changing/losing order?.
Your app is apparently adding 22 annotations but:
If showsUserLocation is YES, then the map view's annotations array will contain 23 annotations (your 22 and the user location that the map adds itself).
There is no guarantee that the annotation at index N of the map's annotations array will be the same annotation that is at index N in your winery array.
For your purpose of simply zooming to a random location, it is not really necessary to establish a link between the map's annotations array and your own array(s).
Instead, the coordinates to center on can be retrieved from the randomly chosen annotation from the annotations array itself.
For example:
MKCoordinateRegion region;
//make list of annotations excluding the user location
NSMutableArray *myAnnotations = [NSMutableArray arrayWithCapacity:10];
for (id<MKAnnotation> ann in self.mapView.annotations)
{
if (! ([ann isKindOfClass:[MKUserLocation class]]))
[myAnnotations addObject:ann];
}
int numOfAnnotations = myAnnotations.count;
//if no annotations to choose from, do nothing
if (numOfAnnotations == 0)
return;
int randomNumber = rand() % numOfAnnotations;
//suggest using arc4random instead of rand
//see https://stackoverflow.com/questions/160890/generating-random-numbers-in-objective-c
id<MKAnnotation> randomAnnotation = [myAnnotations objectAtIndex:randomNumber];
region.center = randomAnnotation.coordinate;
[self.mapView selectAnnotation:randomAnnotation animated:YES];
region.span = ... //rest of the code as-is
//however, calling regionThatFits is unnecessary
There are numerous other unrelated improvements that could be made to the code but those will have to be the topic of separate questions. Here are a couple of them however...
One major improvement I can suggest is to create a custom annotation class (called Winery perhaps) which consolidates all the data of a winery into a single object rather than using separate arrays for the name, address, latitude, longitude, image, etc.
This will make development and future changes much easier to manage.
The second major improvement is to remove the inefficient loop in the viewForAnnotation delegate method which searches for the winery name in order to set the left accessory view's image. This search loop is executed every time the annotation view is needed. With just 22 annotations, you may not notice a performance issue but it's unnecessary work. If the first improvement is done, the search loop can be eliminated since all the annotation's properties will already be in the annotation object.
See Optimizing Code for MKMapView - Large Number of Annotations for some idea of the above.

MKPolyline polylineWithPoints bug?

I want to display a route from my location to a destination.
I took this code http://code.google.com/p/octomapkit and I have added a few logging messages.
Go got the route coordinates ( around 103) properly. They are on the way and filling the route from my location to destination, so the google call and parsing the elements is good.
But when I want to display in MKMapView than it shows only the starting the polyline. Like 15 or 20 not more.
I will try to post the code from top to bottom:
The original code took the first element only and I was thinking maybe if I get the last I will see something else, if yes, than I will add all overlays - that's why the for loop.
otherwise : MKPolyline *polyLine = [self.mapView.overlays objectAtIndex:0];
#pragma mark MKMapViewDelegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKPolylineView *routeLineView = nil;
// should take the objectAtIndex:0 , but for test I will check if it has more
for(int i=0; i <self.mapView.overlays.count; i++ ){
MKPolyline *polyLine = [self.mapView.overlays objectAtIndex:i];
routeLineView = [[[MKPolylineView alloc] initWithPolyline:polyLine] autorelease];
routeLineView.fillColor = [UIColor redColor];
routeLineView.strokeColor = [UIColor redColor];
routeLineView.lineWidth = 3;
}
return routeLineView;
}
-(void) routeLoadSucceededWithRoutePoints:(MKMapPoint*)routePoints count:(int)pointNumber {
//NSLog(#"MKMapVew+OctoRoute.routeLoadSucceededWithRoutePoints count: %d", pointNumber);
MKPolyline* routeLine = nil;
routeLine = [MKPolyline polylineWithPoints:routePoints count:pointNumber];
// add the overlay to the map
if (nil != routeLine) {
// added zoom support:
if(shouldZoom){
MKCoordinateRegion region = [self coordinateRegion];
[self setRegion:region animated:YES];
}
//[self removeOverlays:self.overlays];
[self addOverlay:routeLine];
}
}
the //[self removeOverlays:self.overlays]; has no effect if is commented or not, Iwas hoping it will create more :) -but not.
Inside mapPointCArrayFromJSONString I see the coordinates properly:
-(void) jsonLoadSucceededWithData:(NSData*)loadedData {
self.routeParser.jsonStr = [[[NSString alloc] initWithData:loadedData encoding:NSUTF8StringEncoding] autorelease];
MKMapPoint *mapPointCArray = [self.routeParser mapPointCArrayFromJSONString];
//NSLog(#"OctoRouteService.jsonLoadSucceededWithData : %d"+ mapPointCArray.);
[delegate routeLoadSucceededWithRoutePoints:mapPointCArray count:[self.routeParser numberOfPoints]];
}
The steps has the coordinated for sure. Checked many times.
-(MKMapPoint*) mapPointCArrayFromJSONString {
NSArray *steps = [self routeStepsArrayFromJSONString];
//NSLog(#"OctoRouteParser.mapPointCArrayFromJSONString steps:%d ", steps.count);
if(steps.count == 0){
return nil;
}
MKMapPoint *mapPointCArray = malloc(sizeof(CLLocationCoordinate2D) * [steps count]*2 -1);
numberOfPoints = [steps count]-1;
int index=0;
for (NSDictionary *stepDict in steps) {
[self addRouteStepDict:stepDict toMapPointCArray:mapPointCArray atIndex:index];
index = index+2;
}
return mapPointCArray;
}
I can't see a reason why is only the first fraction of the route on my map, with red line.
Any suggestion?
The viewForOverlay delegate method will be called by the map view for each overlay it needs to display a view for. It may also call it more than once for each overlay.
Your code only needs to worry about creating and returning a view for the single overlay passed as a parameter to that method.
The code there should be something like this:
MKPolylineView *routeLineView = [[[MKPolylineView alloc] initWithPolyline:overlay] autorelease];
routeLineView.fillColor = [UIColor redColor];
routeLineView.strokeColor = [UIColor redColor];
routeLineView.lineWidth = 3;
return routeLineView;
The existing code in your question returns the view corresponding to the last overlay that happens to be in the overlays array for every overlay the map view calls viewForOverlay for.
Additionally, that octomapkit has a bug in the mapPointCArrayFromJSONString method. These lines:
MKMapPoint *mapPointCArray = malloc(sizeof(CLLocationCoordinate2D)
* [steps count]*2 -1);
numberOfPoints = [steps count]-1;
should be:
MKMapPoint *mapPointCArray = malloc(sizeof(CLLocationCoordinate2D)
* [steps count]*2);
numberOfPoints = [steps count]*2;
The original first line is wrong because it excludes the end point of the last line segment.
The original second line is very wrong because numberOfPoints is supposed to reflect the number of points in the mapPointCArray (not the last index in the steps array). The way it was, the overlay would only show half the route.
It would be cleaner to change that code so the calculation is done only once:
numberOfPoints = [steps count]*2;
MKMapPoint *mapPointCArray = malloc(sizeof(CLLocationCoordinate2D)
* numberOfPoints);
The viewForOverlay method should still be coded as explained earlier. It should only work with the overlay parameter passed to it and not directly with the map view's overlays array.

Resources