Im trying to create a shape on my map but I'm having a hard time finding any information about mkpolygon with swift. I was hoping someone on here would see this and point me into the right direction.
This is what I currently have but the polygon is not appearing.
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let pr = MKPolygonRenderer(overlay: overlay)
pr.strokeColor = UIColor.purpleColor()
pr.lineWidth = 14
return pr
}
func createPolyline(mapView: MKMapView) {
var points=[CLLocationCoordinate2DMake(49.142677, -123.135139),CLLocationCoordinate2DMake(49.142730, -123.125794),CLLocationCoordinate2DMake(49.140874, -123.125805),CLLocationCoordinate2DMake(49.140885, -123.135214)]
let polygon = MKPolygon(coordinates: &points, count: points.count)
self.mapView.addOverlay(polygon)
}
Turns out what I was forgetting was to set the map view delegate. I will leave this up incase anyone wants to see what I used to get mkpolygon working in swift.
Related
I am trying to add an array of multipolylines as MKOverlay to a map view as show in code below:
import SwiftUI
import MapKit
struct PolylineMapView: UIViewRepresentable {
func makeCoordinator() -> MapViewCoordinator{
return MapViewCoordinator(self)
}
func updateUIView(_ view: MKMapView, context: Context){
view.mapType = MKMapType.standard
}
func makeUIView(context: Context) -> MKMapView{
let view = MKMapView(frame: .zero)
view.delegate = context.coordinator
view.showsUserLocation = true
view.showsScale = true
view.showsCompass = true
view.setUserTrackingMode(MKUserTrackingMode.followWithHeading, animated: true)
let overlays: [MKOverlay] = [MKOverlay]() // this overlay will contain 27,000 overlays
view.addOverlays(overlays)
return view
}
}
// MARK: - Coordinator
class MapViewCoordinator: NSObject, MKMapViewDelegate {
var parent: PolylineMapView
init(_ parent: PolylineMapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let multiPolyline = overlay as? MKMultiPolyline{
let polylineRenderer = MKMultiPolylineRenderer(multiPolyline: multiPolyline)
polylineRenderer.strokeColor = .magenta
polylineRenderer.lineWidth = 2
polylineRenderer.shouldRasterize = true
return polylineRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
The issue I face while adding the huge number of overlays at a time is that the app runs out of memory and crashes.
My question is how can I add the overlays only in the mapRect that is visible in order to avoid adding all at once? What is the best way to handle this? It would also be interesting to know how to only render the overlays at a certain zoom level on the map.
Adding over 14000 apple MapKit - You are going beyond limit. There is limit set to everything.
To avoid the issue, do below steps.
Show only overlay which are visible to the current region but not to exceed 50. If they are more then 50, show note on the map that "Zoom in to see more properties"
Once user zoom in, based on the region of the visible map, repeat step 1.
Check Zillow app for the same.
https://apps.apple.com/us/app/zillow-real-estate-rentals/id310738695
Only add overlays that make sense at the moment:
MKOverlay has a method that tells you if it intersects with a MKMapRect.
let isVisible = overlay.intersects(mapView.boundingMapRect)
Then add only those overlays that intersect.
If there are still too many overlays left, you can use.
let visible = mapView.boundingMapRect.contains(overly.boundingMapRect)
If you zoom out, you might have to choose which are the most important polylines and show only those.
If this works: write a diff algorithm:
It is probably a bad idea to remove all previous overlays and add all new ones after the region of the mapView changes.
Instead, remove all overlays from mapView that aren't shown any more, add the new overlays, and don't change the overlays that are part of the old and new mapView region.
One more idea: you could merge multiple MKMultiPolyline objects that are near each other (and share the same color...) into one MKMultiPolyline. This can be combined with the idea above.
I'm rendering a bunch of lines on a map that are having an issue when one line goes over another resulting in a merge. I'm using a custom renderer which sets the width and colors etc inside the func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer method. How can I prevent the merge of the MKPolyLines
Here's an image detailing the "merge" issue.
final class RouteRenderer: MKPolylineRenderer {
override func strokePath(_ path: CGPath, in context: CGContext) {
guard let overlayColor = self.overlay as? RouteLine, let color = overlayColor.color else {
super.strokePath(path, in: context)
return
}
context.saveGState()
context.setStrokeColor(color.cgColor)
context.addPath(path)
context.drawPath(using: .stroke)
context.restoreGState()
}
}
Note I'm storing the lines as an array of [MKPolylines] in an attempt to solve the issue but I think that partly solved it, the issue I think is with the renderer.
I am trying to render an overlay (polygon) on the map using swift MapKit. I can render successfully but when I zoom out too much then zoom in again, all the rendered polygons get disappear. How can I prevent that from happening and force the rendered polygons to stay on the map at all zoom scales? I am using MapKit and MKPolygonRenderer.
I add the polygons to the map using the following method in my viewController:
// function to add polygon overlay to the map
private func addPolygonsToMap() {
guard let polygons = arrayOfpolygons else {
return
}
for polygon in polygons {
mapView.addOverlay(polygon)
}
}
My view controller conforms to MKMapViewDelegate and here is the delegate method for it.
// method for overlay on map
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolygonRenderer(overlay: overlay)
renderer.fillColor = UIColor.purple.withAlphaComponent(0.4)
renderer.strokeColor = .black
renderer.lineWidth = 2
return renderer
}
here is a simple demonstration with some points to draw a test polygon.
https://developer.apple.com/documentation/mapkitjs/mapkit/polygonoverlay
I did not find any swift solution for this problem.
I am running on simulator with iOS 13.
I have ann app which will allow users to draw in their own geofences. Right now the way that it works is the user draws their geofence on the screen and the app is keeping track of where they are drawing and storing their path into a CGMutablePath.
My question is how can I take this CGMutablePath and apply it to a MKPolygon so that the users hand drawn geofence can be applied as a Map overlay?
First you need to use your points from the user's touches to build an MKOverlay:
let coords = points.map({ CLLocationCoordinate2D(latitude: Double($0.x), longitude: Double($0.y)) })
let polygon = MKPolygon(coordinates: coords, count: coords.count)
You might need to also take into account the current zoom level of the map to make sure the conversion between points on the screen and lat/lng is correct.
Next, you can add this overlay to the map:
mapView.add(overlay)
To style the overlay (change its color, etc), use the MKMapViewDelegate method:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolygonRenderer(overlay: overlay)
renderer.fillColor = UIColor.blue
return renderer
}
My question is how can I take this CGMutablePath and apply it to a MKPolygon
You don't. If what you have is a general CGPath, use a custom MKOverlay along with MKOverlayPathRenderer.
How add annotations to polyline and polygon in Swift & MapKit? By Point is simple.
S.,
I'm not sure what you're asking here, but I assume you want to display an annotation somewhere on the polyline.
First the intro how to get the the polyline:
So, lets assume you have an array of CLLocation objects that will draw the polyline on the map. We call this array of location objects: myLocations and it's of type [CLLocation]. Now somewhere in your app you call a method that creates the polyline, we call this method createOverlayObject(locations: [CLLocation]) -> MKPolyline.
Your call could look like this:
let overlayPolyline = createOverlayObject(myLocations)
The method you called then could look like this:
func createOverlayObject(locations: [CLLocation]) -> MKPolyline {
//This method creates the polyline overlay that you want to draw.
var mapCoordinates = [CLLocationCoordinate2D]()
for overlayLocation in locations {
mapCoordinates.append(overlayLocation.coordinate)
}
let polyline = MKPolyline(coordinates: &mapCoordinates[0], count: mapCoordinates.count)
return polyline
}
This was the first part, don't forget to implement the mapView(_: rendererForOverlay overlay:) to get the line rendered. this part could look something like this:
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
//This function creatss the renderer for the polyline overlay. This makes the polyline actually display on screen.
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = mapLineColor //The color you want your polyline to be.
renderer.lineWidth = self.lineWidth
return renderer
}
Now the second part get the annotation somewhere on the map. This is actually straight forward if you know what the coordinates are where you want to put your annotation. creating and displaying the annotation is straightforward again, assuming you have defined a map view called myNiceMapView:
func createAnnotation(myCoordinate: CLLocationCoordinate2D) {
let myAnnotation = MKPointAnnotation()
myAnnotation.title = "My nice title"
startAnnotation.coordinate = myCoordinate
self.myNiceMapView.addAnnotations([myAnnotation])
}
Don't forget to implement mapView(_: MKMapView, viewForAnnotation annotation:) -> MKAnnotationView? method, which might look like:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
//This is the mapview delegate method that adjusts the annotation views.
if annotation.isKindOfClass(MKUserLocation) {
//We don't do anything with the user location, so ignore an annotation that has to do with the user location.
return nil
}
let identifier = "customPin"
let trackAnnotation = MKAnnotationView.init(annotation: annotation, reuseIdentifier: identifier)
trackAnnotation.canShowCallout = true
if annotation.title! == "Some specific title" { //Display a different image
trackAnnotation.image = UIImage(named: "StartAnnotation")
let offsetHeight = (trackAnnotation.image?.size.height)! / 2.0
trackAnnotation.centerOffset = CGPointMake(0, -offsetHeight)
} else { //Display a standard image.
trackAnnotation.image = UIImage(named: "StopAnnotation")
let offsetHeight = (trackAnnotation.image?.size.height)! / 2.0
trackAnnotation.centerOffset = CGPointMake(0, -offsetHeight)
}
return trackAnnotation
}
Now the challenges is finding the right coordinate where to put your annotation. I can't find anything better than that you have a CLLocationCoordinate2D that references the location you want to put the annotation. Then with a for-in loop find the location where you want to put your annotation, something like this:
for location in myLocations {
if (location.latitude == myReferenceCoordinate.latitude) && (location.longitude == myReferenceCoordinate.longitude) {
self.createAnnotation(location: CLLOcationCoordinate2D)
}
}
Hope this answers your question.