How to Set a Custom Annotations for All Points Except for User Location

How to set a custom annotations for all points except for user location?

The problem is that you are not examining the incoming annotation. Examine it! If it is a MKUserLocation, return nil.

How do i initiate directions to pin annotations from user location when the user taps the pin?

assuming you already have a location manager created

var currentPlacemark: CLPlacemark? 

@IBAction func getDirectionsTapped(_ sender: Any) { //action from storyboard to viewcontroller (command + drag)
getAddress()
}

func getAddress(){
let overlays = mapView.overlays // these lines of code
mapView.removeOverlays(overlays) // are to clean up your mapview when
locationManager.stopUpdatingLocation() // a new directions request is made

guard let currentPlacemark = currentPlacemark else {
return
}

let directionRequest = MKDirections.Request()
let destinationPlacemark = MKPlacemark(placemark: currentPlacemark)

directionRequest.source = MKMapItem.forCurrentLocation()
directionRequest.destination = MKMapItem(placemark: destinationPlacemark)

directionRequest.transportType = ."[your transport type]"

let directions = MKDirections(request: directionRequest)
directions.calculate { (directionsResponse, error) in
guard let directionsResponse = directionsResponse else {
if let error = error { // unused variable
print("Error")
}
return
}
for route in directionsResponse.routes{ //for loop to show all possible routes or let route = directionsResponse.route[0] to get first route only
self.mapView.addOverlay(route.polyline, level: .aboveRoads) //level is optional
self.mapView.setVisibleMapRect(route.polyline.boundingMapRect, animated: true)
}

}
locationManager.startUpdatingLocation()
}

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let location = view.annotation as? [pointannotation name] {
self.currentPlacemark = MKPlacemark(coordinate: location.coordinate)
}
}

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let render = MKPolylineRenderer(overlay: overlay as! MKPolyline)
render.strokeColor = //choose a color
return render
}

use custom annotation to handle exception (AOP)

Firstly, I noticed that you use reflection in order to fetch the method annotation. Instead, you can simply bind it to an advice method parameter:

@Before("@annotation(authorized)")
public void authorize(JoinPoint joinPoint, Authorized authorized) {
String[] roles = authorized.value();
if (!userHasRoles(roles))
throw new ResponseStatusException(
HttpStatus.UNAUTHORIZED,
"You must have one of the following permission: " + Arrays.asList(roles)
);
}

Concerning your question, I recommend to read some documentation. You can use an @Around advice and handle your exceptions there. Whether your pointcut only targets methods with a specific annotation or matches methods or classes more globally, is up to you. I do not recommend to use AOP based on matching annotations, unless absolutely necessary. The annotations pollute your source code, scattering aspect-related information across your code base. If instead, you can match pointcuts based on package names, class names, parent classes, implemented interfaces, method names, method return value types, method parameter types or whatever you have in your application anyway, the aspect is easier to maintain, and you cannot so easily forget to add an annotation in every single place where you e.g. need exception handling.

Besides, Spring already has mechanisms for exception handling, e.g. @ExceptionHandler for controllers and @ControllerAdvice for global exception handling.

Of course, you can still implement exception handling for Spring beans using Spring AOP, like I said. Or if you use native AspectJ, because you want to handle exceptions in non-Spring classes, an @Around advice comes in handily.

package de.scrum_master.aspect;

import java.time.Instant;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
public class ExceptionHandlerAspect {
String level = "INFO";
String message = "Oops!";

@Around("execution(* *(..))")
public Object catchAndLogException(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
}
catch (Exception e) {
String loggedUser = user() == null ? "" : user().name;
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
System.out.printf("%s [%-5s] (%s) %s:%d - %s%n",
Instant.now(), level, loggedUser,
signature.getMethod(), joinPoint.getSourceLocation().getLine(),
message
);
return null;
}
}

private User user() {
return new User();
}
}

This shall print something like:

2022-08-27T07:38:46.644235600Z [INFO ] (jane_doe) public static void de.scrum_master.app.Application.main(java.lang.String[]):4 - Oops!

Please note that if you only want to log the exception instead of handling it, a @AfterThrowing advice type is preferable. If you really want to handle (catch and swallow) the exception and your target method returns something other than void, please make sure to return a meaningful value. In my little example, I am simply returning null (which AspectJ automatically maps to 0 or false for the corresponding primitive types). Usually, this is not what you want.

Please also note how the aspect fetches class and method name information as well as the source line directly from the joinpoint's meta data instead of using reflection on the stack trace.

Custom annotation showing same image for all different types of POI's

can you try to change:

}else if  {

into:

else if(state == "Jachthavens")

update:

check subtitle instead of state:

if annotationView == nil {

annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView!.canShowCallout = true

if(annotationView.subtitle == "track") {
annotationView!.image = UIImage(named: "startpin")
} else if(annotationView.subtitle == "pause") {
annotationView!.image = UIImage(named: "pausepin")
} else if(annotationView.subtitle == "stop") {
annotationView!.image = UIImage(named: "endpin")
} else if (annotationView.subtitle == "Jachthavens") {
annotationView!.image = UIImage(named: "jachthavenpin")
let deleteButton = UIButton(type: UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 35
deleteButton.frame.size.height = 35
deleteButton.backgroundColor = UIColor.whiteColor()
deleteButton.setImage(UIImage(named: "jachthaven"), forState: .Normal)
deleteButton.addTarget(self, action: #selector(ViewController.infoClicked(_:)), forControlEvents: .TouchUpInside)
annotationView!.leftCalloutAccessoryView = deleteButton
annotationView?.annotation = annotation
} else if (annotationView.subtitle == "Watersportwinkels") {
annotationView!.image = UIImage(named: "watersportwinkelspin")
let deleteButton = UIButton(type: UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 35
deleteButton.frame.size.height = 35
deleteButton.backgroundColor = UIColor.whiteColor()
deleteButton.setImage(UIImage(named: "watersportwinkels"), forState: .Normal)
deleteButton.addTarget(self, action: #selector(ViewController.infoClicked(_:)), forControlEvents: .TouchUpInside)
annotationView!.leftCalloutAccessoryView = deleteButton
annotationView?.annotation = annotation
}

} else {
annotationView!.annotation = annotation
}

Direction of target annotation when outside of visible area

The simplest way is to place four "pointer" views above the map at each of the cardinal points. Then, as the user moves the map (using mapView:regionDidChangeAnimated: delegate method) determine which pointer should be shown. Hide all the other ones; and then show the correct one. Also, apply a transformation to the pointer so that the bearing angle is represented as you have done.

Here is a screenshot of a storyboard with the above configuration:
storyboard

And here is a sample implementation (Code is not optimal, of course.):

//
// MapViewController.m
// AnimationTest
//
// Created by Scott Atkinson on 4/17/15.
//

#import "MapViewController.h"

@import MapKit;

typedef NS_ENUM(NSInteger, CardinalPoint) {
North,
South,
East,
West
};

@interface MapViewController () <MKMapViewDelegate>

@property (weak, nonatomic) IBOutlet MKMapView *mapView;

// Views that show cardinal points on the map (Only one should be shown at a time)
@property (weak, nonatomic) IBOutlet UIView *northPointerView;
@property (weak, nonatomic) IBOutlet UIView *eastPointerView;
@property (weak, nonatomic) IBOutlet UIView *westPointerView;
@property (weak, nonatomic) IBOutlet UIView *southPointerView;

// Location to show on the map
@property (strong, nonatomic) CLLocation * targetLocation;

@end

@implementation MapViewController

- (void)viewDidLoad {
[super viewDidLoad];

[self hidePointerViews];

// Add the location to the map
self.targetLocation = [[CLLocation alloc] initWithLatitude:37.331898 longitude:-122.029824];
MKPlacemark * placemark = [[MKPlacemark alloc] initWithCoordinate:self.targetLocation.coordinate addressDictionary:nil];
[self.mapView addAnnotation:placemark];
}

// ******************** MKMapViewDelegate ********************
#pragma mark - MKMapViewDelegate

// As the map moves, update the cardinal pointer views
- (void) mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
if([self isCurrentLocationVisible] && ![self isTargetLocationVisible]) {
// The user location is visible, but the target is not, so show a pointer
double bearing = [self bearingToLocation:self.targetLocation fromLocation:self.mapView.userLocation.location];
[self showCardinalPointDirection:bearing];

} else {
// Hide the pointers
[self hidePointerViews];
}
}

// ******************** Coordinate Helpers ********************
#pragma mark - Coordinate Helpers

- (BOOL) isCurrentLocationVisible {
return MKMapRectContainsPoint(self.mapView.visibleMapRect,
MKMapPointForCoordinate(self.mapView.userLocation.coordinate));
}

- (BOOL) isTargetLocationVisible {
return MKMapRectContainsPoint(self.mapView.visibleMapRect,
MKMapPointForCoordinate(self.targetLocation.coordinate));
}

// From: http://stackoverflow.com/questions/3925942/cllocation-category-for-calculating-bearing-w-haversine-function
double DegreesToRadians(double degrees) {return degrees * M_PI / 180.0;};
double RadiansToDegrees(double radians) {return radians * 180.0/M_PI;};

/// Calculate the bearing between two points
-(double) bearingToLocation:(CLLocation *) destinationLocation fromLocation:(CLLocation *) fromLocation {

double lat1 = DegreesToRadians(fromLocation.coordinate.latitude);
double lon1 = DegreesToRadians(fromLocation.coordinate.longitude);

double lat2 = DegreesToRadians(destinationLocation.coordinate.latitude);
double lon2 = DegreesToRadians(destinationLocation.coordinate.longitude);

double dLon = lon2 - lon1;

double y = sin(dLon) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
double radiansBearing = atan2(y, x);

if(radiansBearing < 0.0)
radiansBearing += 2*M_PI;

return RadiansToDegrees(radiansBearing);
}

// ******************** Pointer View ********************
#pragma mark - Pointer View

- (void) hidePointerViews {
self.northPointerView.hidden =
self.southPointerView.hidden =
self.eastPointerView.hidden =
self.westPointerView.hidden = YES;
}

- (void) showCardinalPointDirection:(double) bearing {
CardinalPoint point = [self cardinalPointWithBearing:bearing];

// Determine which pointer should be shown based on the bearing
UIView * activePointer;
switch (point) {
case North:
activePointer = self.northPointerView;
break;
case South:
activePointer = self.southPointerView;
break;
case East:
activePointer = self.eastPointerView;
break;
case West:
activePointer = self.westPointerView;
break;
}

// Rotate the pointer to show the bearing
activePointer.transform = CGAffineTransformMakeRotation(DegreesToRadians(bearing));

// Hide all pointers except the pertinent one
[self hidePointerViews];
activePointer.hidden = NO;
}

/// Returns the cardinal point for a given bearing (in Degrees)
- (CardinalPoint) cardinalPointWithBearing:(double) bearing {
if (bearing > 45.0 && bearing <= 135.0) {
return East;
} else if (bearing > 135.0 && bearing <= 225.0) {
return South;
} else if (bearing > 225.0 && bearing <= 315.0) {
return West;
} else {
return North;
}
}
@end

Additionally, the pointer rotation is based on the bearing between the userLocation and the targetLocation. It feels a little strange. Probably better to make the rotation based off of some other point. Maybe the center of the visible region at that moment...

How to change MKMapView's user-location blue dot to an image of choice?

In the viewForAnnotation: method of MKMapViewDelegate probably you would be having the code like this.

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {

if (annotation == mapView.userLocation) return nil;
...

We return nil if the annotation is userLocation to let the mapView display the blue dot & circle animation. In order to show our custom annotation for userLocation just remove the line return nil; and do your customization there.

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {

static NSString* AnnotationIdentifier = @"Annotation";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];

if (!pinView) {

MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
if (annotation == mapView.userLocation){
customPinView.image = [UIImage imageNamed:@"myCarImage.png"];
}
else{
customPinView.image = [UIImage imageNamed:@"mySomeOtherImage.png"];
}
customPinView.animatesDrop = NO;
customPinView.canShowCallout = YES;
return customPinView;

} else {

pinView.annotation = annotation;
}

return pinView;
}


Related Topics



Leave a reply



Submit