I am working in an IOS 7 project ,it contains a location checking (current location is in given polygons).
I am Using the following code to check the condition
Created an array of MKPolygons
for(MKPolygon *poly in self.polygonArray)
{
[self checkTheLocationIsInPolygon:currentLocation polygon:poly];
}
- (void)checkTheLocationIsInPolygon:(CLLocation*)aLocation polygon:(MKPolygon*)aPolygon
{
CLLocationCoordinate2D coordinate = {aLocation.coordinate.latitude, aLocation.coordinate.longitude};
MKMapPoint mapPoint = MKMapPointForCoordinate(coordinate);
CGMutablePathRef mpr = CGPathCreateMutable();
MKMapPoint *polygonPoints = aPolygon.points;
size_t nCount = aPolygon.pointCount;
for (int p = 0; p < nCount; p++)
{
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
BOOL pointIsInPolygon = CGPathContainsPoint(mpr, NULL, mapPointAsCGP, FALSE);
CGPathRelease(mpr);
if(pointIsInPolygon == YES)
{
//IN
}
else
{
//Out
}
}
This code is working correctly(pointIsInPolygon return YES/NO correctly) for the first polygon .Then the next iteration (Next polygon from array) pointIsInPolygon gives the previous state means, it return NO if the first polygon was outside the location and it return YES if the first polygon was inside the location .
How to fix this issue?
If anybody know, please give me a suggestion
Swift is simple, you need to do the following, attention in the example is not implemented MKPolygon Array for mutiple MKPolygon on a map
// Init array for any MKPolygons
var arrayMKPolygon : NSMutableArray = []
override func viewWillAppear(animated: Bool) {
// Add one or more than one
self.setMKPolygon()
let tapGesture = UITapGestureRecognizer(target: self, action: "revealRegionDetailsWithPressOnMap:")
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
self.mapView.addGestureRecognizer(tapGesture)
}
func setMKPolygon(){
// Poinst for polygon -> (or NSArray)
for() {
----> (dynamic for example web service) var points = [CLLocationCoordinate2DMake(41.000512, -109.050116),
CLLocationCoordinate2DMake(41.002371, -102.052066),
CLLocationCoordinate2DMake(36.993076, -102.041981),
CLLocationCoordinate2DMake(36.99892, -109.045267)]
// Polygon
let poly: MKPolygon = MKPolygon(coordinates: &points, count: 4)
// Add polygon
mapView.addOverlay(poly)
// Add objecto to Array
arrayMKPolygon.addObject(poly)
}
}
func revealRegionDetailsWithPressOnMap(sender: UITapGestureRecognizer) {
let touchLocation = sender.locationInView(self.mapView)
let locationCoordinate = self.mapView.convertPoint(touchLocation, toCoordinateFromView: self.mapView)
print("Tapped at lat: \(locationCoordinate.latitude) long: \(locationCoordinate.longitude)")
for i in 0...arrayMKPolygon.count - 1 {
let polygonView = MKPolygonRenderer(overlay: arrayMKPolygon[i] as! MKOverlay)
let mapPoint = MKMapPointForCoordinate(locationCoordinate)
let circlePoint = polygonView.pointForMapPoint(mapPoint)
let mapCoordinateIsInCircle : Bool = CGPathContainsPoint(polygonView.path, nil, circlePoint, false)
if mapCoordinateIsInCircle{
print("Yes, is within index --> \(i)")
}
else{
print("NO")
}
}
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.magentaColor()
polygonView.fillColor = UIColor.yellowColor().colorWithAlphaComponent(0.15)
polygonView.lineWidth = 1.5;
return polygonView
}
return MKPolygonRenderer(overlay: overlay)
}
Related
Here is the example picture:
Now I want to remove the overlay between 5 & 6 annotations. Here is the code snippet what I tried so far:
Function to create polyline
func createPolyline() {
if (arrTempFootStepsLocation.count>0){ //This array contains all the coordinates from starting annonation 1 to 6
let points:[CLLocationCoordinate2D] = arrTempFootStepsLocation
let geodesic = MKGeodesicPolyline(coordinates: points, count: points.count)
mapService.addOverlay(geodesic)
}
}
Function to create annotations
func addAnnotations() {
if let arrProof = arrTempProof{ //This array contains 6 coordinates which are taken from arrTempFootStepsLocation only to create annotation
for (index , modelPhotoProof) in arrProof.enumerated() {
if (modelPhotoProof.locationPoint?.latitude != 0.0 && modelPhotoProof.locationPoint?.longitude != 0.0){
let CLLCoordType = CLLocationCoordinate2D(latitude: modelPhotoProof.locationPoint!.latitude, longitude: modelPhotoProof.locationPoint!.longitude)
let anno = MyMKPointAnnotation()
anno.tag = index
anno.coordinate = CLLCoordType
mapService.addAnnotation(anno)
}
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if view.annotation is MyMKPointAnnotation {
if arrRemovalAnnotationPoint.count<2{ //This array contains selected annotation coordinates
arrRemovalAnnotationPoint.append((view.annotation?.coordinate)!)
print("Annotation point inserted.")
}
}
}
Delete overlay between two annotations
#IBAction func btnDeletePolylinesPressed(_ sender:UIButton){
if arrRemovalAnnotationPoint.count == 2 {
//Find the index of those two annotations from arrTempFootStepsLocation
let firstAnnoIndex = arrTempFootStepsLocation.index(where: {$0.latitude == arrRemovalAnnotationPoint.first?.latitude && $0.longitude == arrRemovalAnnotationPoint.first?.longitude})
let lastAnnoIndex = arrTempFootStepsLocation.index(where: {$0.latitude == arrRemovalAnnotationPoint.last?.latitude && $0.longitude == arrRemovalAnnotationPoint.last?.longitude})
//prepare array to remove coordinates between those two annotations
if let first = firstAnnoIndex, let last = lastAnnoIndex{
var arrToRemoved = [CLLocationCoordinate2D]()
if first > last {
arrToRemoved = Array(arrTempFootStepsLocation[last...first])
for i in 0..<arrToRemoved.count{
let itemToBeRemoved = arrToRemoved[i]
for j in 0..<arrTempFootStepsLocation.count - 1 {
let itemOriginal = arrTempFootStepsLocation[j]
if (itemToBeRemoved.latitude == itemOriginal.latitude && itemToBeRemoved.longitude == itemOriginal.longitude){
arrTempFootStepsLocation.remove(at: j)
}
}
}
mapService.removeOverlays(mapService.overlays)
self.perform(#selector(self.createPolyline()), with: nil, afterDelay: 0.8)
}
else {
arrToRemoved = Array(arrTempFootStepsLocation[first...last])
for i in 0..<arrToRemoved.count{
let itemToBeRemoved = arrToRemoved[i]
for j in 0..<arrTempFootStepsLocation.count - 1 {
let itemOriginal = arrTempFootStepsLocation[j]
if (itemToBeRemoved.latitude == itemOriginal.latitude && itemToBeRemoved.longitude == itemOriginal.longitude){
arrTempFootStepsLocation.remove(at: j)
}
}
}
mapService.removeOverlays(mapService.overlays)
self.perform(#selector(self.createPolyline()), with: nil, afterDelay: 0.8)
}
}
arrRemovalAnnotationPoint.removeAll()
}
}
Now the problem is when I press delete button its doesn't clear all coordinates between these annotations. Now when I redraw the polyline it still connects between these annotations.
Whats wrong in my code? Is there any other way to do this? Any help will be appreciated.
remove the particular index in (arrRemovalAnnotationPoint) array
I'm using the cocoapod FBAnnotationClusteringSwift and it's possible to group my annotations together. However I want to loop over all those annotations that are clustered together when the cluster annotation is tapped.
How do I do this?
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if (view.annotation!.isKindOfClass(FBAnnotationCluster) == true){
//I WANT TO LOOP OVER ALL ANNOTATIONS IN THE CLUSTER HERE
}
if (view.annotation!.isKindOfClass(ItemAnnotation) == true){
let annotation = view.annotation! as? ItemAnnotation
if let annotation = annotation, let item = annotation.item, d = delegate{
d.itemAnnotationPressed(item)
}
}
}
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if (view.annotation!.isKindOfClass(FBAnnotationCluster) == true){
let annotation = view.annotation! as? FBAnnotationCluster
var itemListFromAnnotation = [Item]()
for annotation in (annotation?.annotations)! {
let itemAnnotation = annotation as? ItemAnnotation
itemListFromAnnotation.append((itemAnnotation?.item)!)
}
if let d = delegate{
d.itemClusterAnnotationPressed(itemListFromAnnotation)
}
}
if (view.annotation!.isKindOfClass(ItemAnnotation) == true){
mapView.deselectAnnotation(view.annotation, animated: false)
let annotation = view.annotation! as? ItemAnnotation
if let annotation = annotation, let item = annotation.item, d = delegate{
d.itemAnnotationPressed(item)
}
}
}
I am new to iOS development and currently using the FBAnnotationClusteringSwift to cluster makers. My app needs to have two annotations which will not be clustered, as they indicate the source and destination addresses. The remaining annotations must be clustered as they represent stations.
What I want to achieve is something like the image bellow, where "A" is my source address, and the clusters represent stations:
However what is happening is, as the clusters are created, the Annotation that represents a non-clustered annotation (the source address) disappears as following:
If the library is mixing all annotations together, that is pretty bad, there should be a way to separate those that I've added through clusteredAnnotationsWithinMapRect (stations) and those markers that were already added before (addresses). Here is the relevant part of the current code:
var markerClusterer: FBClusteringManager?
ViewController ... {
override func viewDidLoad() {
super.viewDidLoad()
...
self.markerClusterer = FBClusteringManager()
self.markerClusterer!.delegate = self
}
func cellSizeFactorForCoordinator(coordinator:FBClusteringManager) -> CGFloat{
return 1.0
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// Here is just like the example code from FBAnnotationClusterSwift
var stations: [MKAnnotation] = fetchStations(...)
if (stations.count > 0) {
NSOperationQueue().addOperationWithBlock({
let scale:Double = Double(self.mapView.bounds.size.width) / self.mapView.visibleMapRect.size.width
self.markerClusterer!.addAnnotations(stations)
var annotationArray = stations
// Needed to do that otherwise the clusters never "explode" into pins
if (scale <= 0.06) {
annotationArray = self.markerClusterer!.clusteredAnnotationsWithinMapRect(self.mapView.visibleMapRect, withZoomScale:scale)
}
self.markerClusterer!.displayAnnotations(annotationArray, onMapView: self.mapView)
})
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
var reuseId = ""
if (annotation.isKindOfClass(FBAnnotationCluster)) {
reuseId = "Cluster"
var clusterView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if clusterView == nil {
clusterView = FBAnnotationClusterView(annotation: annotation, reuseIdentifier: reuseId, options: nil)
} else {
clusterView!.annotation = annotation
}
return clusterView
} else if (annotation.isKindOfClass(AddressPointAnnotation)) {
reuseId = "AddressPin"
var addressPinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if addressPinView == nil {
addressPinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
addressPinView!.canShowCallout = true
}
else {
addressPinView!.annotation = annotation
}
let addressPin = annotation as! AddressPointAnnotation
addressPinView!.image = UIImage(named: addressPin.icon)
return addressPinView
} else if (annotation is Station) {
reuseId = "StationPin"
var stationPinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if stationPinView == nil {
stationPinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
else {
stationPinView!.annotation = annotation
}
let stationPin = annotation as! Station
stationPinView!.image = UIImage(named: "station")
return stationPinView
} else {
return nil
}
}
}
// Annotation for the stations
class Station: NSObject, MKAnnotation {
var id: Int = 0
var availableBikes: Int = 0
var availableBikeStands: Int = 0
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 39.208407, longitude: -76.799555)
}
// Annotations for addresses
class AddressPointAnnotation: MKPointAnnotation {
var icon: String!
}
class Address: NSObject {
var marker: AddressPointAnnotation?
func addMarker(coordinate: CLLocationCoordinate2D) {
marker = AddressPointAnnotation()
marker!.coordinate = coordinate
// ViewController is passed to the Address, so it can add itself to the map
self.controller.mapView.addAnnotation(marker!)
if (self.direction == SOURCE) {
marker!.title = "Starting address"
marker!.icon = "from"
} else {
marker!.title = "Destination address"
marker!.icon = "to"
}
self.controller.mapView.addAnnotation(marker!)
}
}
Any help, idea, or comment is more than welcome. Thanks.
I have at least 100 diferents Points ... how can associate each point with the position in my 'listOfPoints' assigning a tag in the position associated in viewForAnnotation .
Here i add my Points, some events will have the same title.
var listOfPoints : Array<Events> = [] // List Of all events
//add points to map
for (index, mPoints) in enumerate(listOfPoints) {
var point: MKPointAnnotation! = MKPointAnnotation()
var location = CLLocationCoordinate2D(latitude: mPoints.latitude, longitude: mPoints.longitude)
point.coordinate = location
point.title = mPoints.name
point.subtitle = mPoints.address
self.mapView.addAnnotation(point)
}
//Draw custom pin in the map
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
print("ffff");
var identifier = "CustomAnnotation"
if annotation.isKindOfClass(MKPointAnnotation) {
var pin = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if pin == nil {
pin = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
pin.tag = tagPosition; // here
pin.image = UIImage(named: "mapa_pin")
pin.centerOffset = CGPointMake(0, -10)
pin.canShowCallout = true
var pointTitle = pin!.annotation.title! as String
// Callout
var button = UIButton.buttonWithType(.DetailDisclosure) as UIButton
pin!.leftCalloutAccessoryView = button
var image = UIImageView(image: UIImage(named: "mapa_pin"))
pin!.rightCalloutAccessoryView = image
} else {
pin!.annotation = annotation
}
return pin
}
return nil
}
// Print the position
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
println(view.tag);
}
How can associated this tag with the position on my 'listOfPoints'
pin.tag = tagPosition;
or is there another way?
I think that is easy and im doing this in an app my.
I customize a class for Annotation from MKAnnotation:
import MapKit
import AddressBook
class MyAnnotation: NSObject, MKAnnotation
{
let identifier : String
let title: String
let subtitle: String
let coordinate: CLLocationCoordinate2D
let color: MKPinAnnotationColor
init(identifier: String, title: String, subtitle: String, coordinate: CLLocationCoordinate2D, color: MKPinAnnotationColor)
{
self.identifier = identifier
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.color = color
super.init()
}
func mapItem() -> MKMapItem
{
let addressDictionary = [String(kABPersonAddressStreetKey): subtitle]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDictionary)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}
}
Now you can use the field identifier from class MyAnnotation in your points:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
{
if let annotation = annotation as? MyAnnotation
{
let identifier = annotation.identifier
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView
{
view = dequeuedView
view.annotation = annotation
}
else
{
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.animatesDrop = true
view.leftCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIView
view.rightCalloutAccessoryView = UIButton.buttonWithType(UIButtonType.ContactAdd) as! UIView
view.pinColor = annotation.color
//view.image
//MKAnnotationView 32 x 29
}
return view
}
return nil
}
If you don't understand you can red this excelente article:
http://www.raywenderlich.com/90971/introduction-mapkit-swift-tutorial
The problem is that you are using a plain vanilla built-in MKPointAnnotation. It has no tag property. You need to define your own MKAnnotation class (i.e. a class that adopts the MKAnnotation protocol). That way, it can have any properties you like.
My very easy solution on Objective-C
.h file:
#import <MapKit/MapKit.h>
#interface mapMKPointAnnotation : MKPointAnnotation
#property (nonatomic) NSInteger tag;
#end
and create object with tag:
mapMKPointAnnotation *annotation = [[mapMKPointAnnotation alloc] init];
[annotation setTag:1];
next get tag in method:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *SFAnnotationIdentifier = #"SFAnnotationIdentifier";
mapMKPointAnnotation *mMKPAnn = (mapMKPointAnnotation *) annotation;
NSString *img = [mMKPAnn tag] == 0 ? #"map_icon" : #"map_icon_active";
this is my code for drawing first Line,
for another line with another color how can i do ?????
func mapView(mapView : MKMapView! , rendererForOverlay overlay: MKOverlay!) ->MKOverlayRenderer! {
if overlay is MKPolyline {
var polyLineRenderer = MKPolylineRenderer(overlay: overlay)
polyLineRenderer.strokeColor = UIColor.blackColor()
polyLineRenderer.lineWidth = 10
return polyLineRenderer
}
return nil
}
Finally i found the way :
In the top of the class i put
var toGo : MKPolyline?
var toCome : MKPolyline?
and after that in view Did load :
var polyLineGoes = MKPolyline(coordinates: &coordinateGoes, count: coordinateGoes.count)
toGo = polyLineGoes
mapView.addOverlay(polyLineGoes)
var polyLineComes = MKPolyline(coordinates: &coordinateComes, count: coordinateComes.count)
toCome = polyLineComes
mapView.addOverlay(polyLineComes)
at the End of class :
func mapView(mapView : MKMapView! , rendererForOverlay overlay: MKOverlay!) ->MKOverlayRenderer! {
if overlay is MKPolyline {
if ( toGo != nil) && (toCome != nil ) {
if overlay as? MKPolyline == toGo {
var polyLineRenderer = MKPolylineRenderer(overlay: overlay)
polyLineRenderer.strokeColor = UIColor.redColor()
polyLineRenderer.lineWidth = 3
return polyLineRenderer
} else if overlay as? MKPolyline == toCome {
print(overlay.description)
var polyLineRenderer = MKPolylineRenderer(overlay: overlay)
polyLineRenderer.strokeColor = UIColor.blueColor()
polyLineRenderer.lineWidth = 3
return polyLineRenderer
}
}
}
return nil
}
I was facing a same problem, but I just find another way to solve it.
In the mapView the code will be:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer!{
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = variables.lineColor
polylineRenderer.lineWidth = 4
return polylineRenderer
}
return nil
}
and I just have a struct named variables, and it contains variable lineColor. Also, I have another CONSTANT struct it has some CONSTANT to make code in mapView class more readable. The code in these two structs will be:
struct CONSTANT {
static let greenColor = UIColor.greenColor().colorWithAlphaComponent(0.5)
static let blueColor = UIColor.blueColor().colorWithAlphaComponent(0.5)
static let redColor = UIColor.redColor().colorWithAlphaComponent(0.5)
}
struct variables {
// let the default lineColor be green
static var lineColor = CONSTANT.greenColor
}
With these, I will just change my variables.lineColor to the color in CONSTANT struct. For example,
lineColor = CONSTANT.greenColor
mapView.addOverlay(myPolyLine, level: MKOverlayLevel.AboveRoads)
lineColor = CONSTANT.blueColor
mapView.addOverlay(myPolyLine, level: MKOverlayLevel.AboveRoads)
lineColor = CONSTANT.redColor
mapView.addOverlay(myPolyLine, level: MKOverlayLevel.AboveRoads)
First post here :D
I have found yet another way.
First, we need to extend MKPolyline:
extension MKPolyline {
struct ColorHolder {
static var _color: UIColor?
}
var color: UIColor? {
get {
return ColorHolder._color
}
set(newValue) {
ColorHolder._color = newValue
}
}
}
In viewDidLoad, we can assign every polyline a color now:
var polyline1 = MKPolyline(coordinates: coordinates1, count: coordinates1.count)
polyline1.color = UIColor(.red)
mapView.addOverlay(polyline1)
var polyline2 = MKPolyline(coordinates: coordinates2, count: coordinates2.count)
polyline2.color = UIColor(.green)
mapView.addOverlay(polyline2)
Finally, we have our mapView-function:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let overlay_ = overlay as? MKPolyline {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = overlay_.color
return renderer
} else {
return MKOverlayRenderer()
}
}