swift MKPolyLineRenderer intersect issue - swift

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.

Related

Adding over 14000 apple MapKit multipolyline overlays as MKOverlays crashes app

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.

Prevent replacing of WMS Overlay while adding Polygon or Polyline to MKMapView

I have implemented Web Map Service in the MKMapView by subclassing the MKTileOverlay & rendering it using MKTileOverlayRenderer. It works fine and displays the custom map properly.
When I call method like mapView.addOverlay(polyLine) to add Polyline or Polygon. The WMS overlay gets replaced with the Apple Maps overly.
// Set up the overlay and adds it to MKMapView.
func setupTileRenderer() {
let wmsURL = formTemplate?.wmsURL
let overlay = WMSTileOverlay(urlTemplate: wmsURL)
overlay.canReplaceMapContent = true
mapView.addOverlay(overlay, level: .aboveLabels)
tileRenderer = MKTileOverlayRenderer(tileOverlay: overlay)
wmsTileOverlay = overlay
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let render = MKPolylineRenderer(overlay: overlay)
render.lineWidth = 2
render.strokeColor = UIColor.red
return render
} else if overlay is MKPolygon {
let render = MKPolygonRenderer(overlay: overlay)
render.lineWidth = 2
render.strokeColor = UIColor.red
return render
} else if overlay is WMSTileOverlay {
return tileRenderer!
}
return MKOverlayRenderer(overlay: overlay)
}
How do I prevent this? I don't want wmsTileOverlay to get replaced while adding polyline or polygon.
I realised that before drawing the polygon I was removing the previous overlays so at that time I was removing all the overlays. Just checking the overlay is WMSTileOverlay then not removing it.
/// Clears the overlays added by the user.
func clearOverlaysOnMapView() {
for overlay in mapView.overlays {
if !(overlay is WMSTileOverlay) {
mapView.removeOverlay(overlay)
}
}
}

How to fix cracks appearing in MKPolyline rendering

I am facing cracks issue when rendering MKPolyline. Is there a way to control the Z of the polyline (like we do in game dev with the z-bias) or any other technic?
I believe my code’s quite standard, as follows:
public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let renderer = MKPolylineRenderer(overlay: overlay)
// Some code where I define thickness and color
renderer.strokeColor = color
renderer.lineWidth = CGFloat(thickness)
return renderer
}
return MKOverlayRenderer(overlay: overlay)
}

How to prevent overlays on the map from disappearing when zoom scale changes in swift?

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.

MKPolygon Swift not appearing

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.