How to draw line on google maps - ios

I have so many coordinates of a user from server and I want draw line to connect those like this.
I've tried to use This way but my app get crash, I guess this happend because i send too many request.

The simplest way is to use a GMSPolyline.
Assuming you have a coordinates array of CLLocationCoordinate2D's and they are in the correct order.
let path = GMSMutablePath()
for coord in coordinates {
path.add(coord)
}
let line = GMSPolyline(path: path)
line.strokeColor = UIColor.blue
line.strokeWidth = 3.0
line.map = self.map

- Swift 4 Extensions
Make path with coordinates:
extension GMSMutablePath {
convenience init(coordinates: [CLLocationCoordinate2D]) {
self.init()
for coordinate in coordinates {
add(coordinate)
}
}
}
Add path to map:
extension GMSMapView {
func addPath(_ path: GMSPath, strokeColor: UIColor? = nil, strokeWidth: CGFloat? = nil, geodesic: Bool? = nil, spans: [GMSStyleSpan]? = nil) {
let line = GMSPolyline(path: path)
line.strokeColor = strokeColor ?? line.strokeColor
line.strokeWidth = strokeWidth ?? line.strokeWidth
line.geodesic = geodesic ?? line.geodesic
line.spans = spans ?? line.spans
line.map = self
}
}
Usage:
let path = GMSMutablePath(coordinates: [<#Coordinates#>])
mapView.addPath(path)

Related

How do i remove this default red marker from google maps iOS Swift

I want to remove this red pins. I am adding custom pins to source (yellow pin) and destination (blue pin, but I still not understand why this red pins also show ?
Here is Code :-
func reDrawRoute(pickupCoordinate : CLLocationCoordinate2D, destinationCoordinate :CLLocationCoordinate2D, type: String) {
// func setMapMarkersRoute(vLoc: CLLocationCoordinate2D, toLoc: CLLocationCoordinate2D) {
self.sourceMarker.map = nil
self.destMarker.map = nil
//add the markers for the 2 locations
if type == "S2D" {
self.sourceMarker = GMSMarker.init(position: pickupCoordinate)
self.sourceMarker.icon = UIImage(named: "source")
self.sourceMarker.map = gMapView
self.destMarker = GMSMarker.init(position: destinationCoordinate)
self.destMarker.icon = UIImage(named: "destination")
self.destMarker.map = gMapView
} else if type == "C2S" {
self.carMarker.position = pickupCoordinate
self.carMarker.icon = UIImage(named: "pin-car")
self.carMarker.map = gMapView
self.destMarker = GMSMarker.init(position: destinationCoordinate)
self.destMarker.icon = UIImage(named: "source")
self.destMarker.map = gMapView
} else if type == "C2D" {
self.carMarker.position = pickupCoordinate
self.carMarker.icon = UIImage(named: "pin-car")
self.carMarker.map = gMapView
self.destMarker = GMSMarker.init(position: destinationCoordinate)
self.destMarker.icon = UIImage(named: "destination")
self.destMarker.map = gMapView
}
//zoom the map to show the desired area
var bounds = GMSCoordinateBounds()
bounds = bounds.includingCoordinate(pickupCoordinate)
bounds = bounds.includingCoordinate(destinationCoordinate)
self.gMapView.moveCamera(GMSCameraUpdate.fit(bounds))
//finally get the route
getRoute(from: pickupCoordinate, to: destinationCoordinate)
}
This is for getting route coordinates between source and destination.
func getRoute(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) {
let source = MKMapItem(placemark: MKPlacemark(coordinate: from))
let destination = MKMapItem(placemark: MKPlacemark(coordinate: to))
let request = MKDirections.Request()
request.source = source
request.destination = destination
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
directions.calculate(completionHandler: { (response, error) in
if let res = response {
//the function to convert the result and show
self.show(polyline: self.googlePolylines(from: res))
}
})
}
This code is for showing route.
private func googlePolylines(from response: MKDirections.Response) -> GMSPolyline {
let route = response.routes[0]
var coordinates = [CLLocationCoordinate2D](
repeating: kCLLocationCoordinate2DInvalid,
count: route.polyline.pointCount)
route.polyline.getCoordinates(
&coordinates,
range: NSRange(location: 0, length: route.polyline.pointCount))
let polyline = Polyline(coordinates: coordinates)
let encodedPolyline: String = polyline.encodedPolyline
let path = GMSPath(fromEncodedPath: encodedPolyline)
return GMSPolyline(path: path)
}
I am using this method for adding custom markers..

Polyline starts disappearing after zoom out

I have encountered a strange problem, when i zoom out the my polyline of custom coordinates starts disappearing, following is my code
func setTrackerLines(currentLoc: String, destinationLoc: String) {
let str1 = currentLoc
let LocArray1 = str1.components(separatedBy: ",")
let str2 = destinationLoc
let LocArray2 = str2.components(separatedBy: ",")
let path = GMSMutablePath()
path.add(CLLocationCoordinate2D(latitude: (LocArray1[0] as NSString).doubleValue, longitude: (LocArray1[1] as NSString).doubleValue))
path.add(CLLocationCoordinate2D(latitude: (LocArray2[0] as NSString).doubleValue, longitude: (LocArray2[1] as NSString).doubleValue))
let rectangle = GMSPolyline(path: path)
rectangle.strokeColor = UIColor.blue
rectangle.strokeWidth = 1
rectangle.map = self.myMapView
}
}
have call this method on viewdidload(), This is how i am using the code
for i in 0 ..< pointArray.count {
print("Delta: \(delta)")
if pointArray.count != (i + 1) {
self.setTrackerLines(currentLoc: pointArray[i], destinationLoc: pointArray[i + 1])
}
The result is showing in pictures. Kindly help please

draw polyline in parallel with animation marker in google maps

I am using google maps in my application wherein I have to draw the polyline parallel along with the animation marker.Both should move simultaneous.,
Right now, my solution works like this ., First polyline is drawn with new coordinates, then animation marker is moved after.
I have tried few links in the stack overflow.. wherein the solution wasn't there.
This is the solution i'm looking for in swift iOS... the below link is for android.. which works perfectly
How to move marker along polyline using google map
Thanks if you can help me out in these..`
#objc func pocDrawPolyline() {
if poclastShownIndex < (vehicleLocationArray.count) {
let dict = vehicleLocationArray[poclastShownIndex]
if let lati = dict["latitude"], let logi = dict["longitude"] {
let lat = Double(lati as! String)
let log = Double(logi as! String)
let location = dict["location"] as? String
pocCreateVehicleMarkerWith(address: location ?? "School Bus", latitude: lat!, and: log!)
pocPath.add(CLLocationCoordinate2DMake(lat!, log!))
}
polyline.path = pocPath
polyline.strokeWidth = 3.0
polyline.strokeColor = UIColor.red
polyline.map = googleMapView
poclastShownIndex += 1
} else {
//No update from "NOW" API call
}
}
func pocCreateVehicleMarkerWith(address: String, latitude: Double, and Longitude: Double) {
// Creates a marker for Vehicle.
if vechicleMarker.map == nil {
vechicleMarker.position = CLLocationCoordinate2D(latitude: latitude, longitude: Longitude)
vechicleMarker.title = address
vechicleMarker.icon = UIImage(named: "bus1")
vechicleMarker.map = googleMapView
} else {
CATransaction.begin()
CATransaction.setAnimationDuration(0.5)
vechicleMarker.position = CLLocationCoordinate2D(latitude: latitude, longitude: Longitude)
vechicleMarker.title = address
vechicleMarker.icon = UIImage(named: "bus1")
CATransaction.commit()
if poclastShownIndex > 0 {
if let oldLatitude = vehicleLocationArray[poclastShownIndex-1]["latitude"],
let oldLongitude = vehicleLocationArray[poclastShownIndex-1]["longitude"],
let newLatitude = vehicleLocationArray[poclastShownIndex]["latitude"],
let newLongitude = vehicleLocationArray[poclastShownIndex]["longitude"] {
let oldLat = Double(oldLatitude as! String)
let oldLon = Double(oldLongitude as! String)
let newLat = Double(newLatitude as! String)
let newLon = Double(newLongitude as! String)
let oldloc = CLLocationCoordinate2D(latitude: oldLat!, longitude: oldLon!)
let newloc = CLLocationCoordinate2D(latitude: newLat!, longitude: newLon!)
let distanceInMeters = distance(from: oldloc, to: newloc)
if distanceInMeters > 0 {
print("Rotation Degree ------ \(CLLocationDegrees(getHeadingForDirection(fromCoordinate: oldloc, toCoordinate: newloc)))")
vechicleMarker.groundAnchor = CGPoint(x: CGFloat(0.5), y: CGFloat(0.5))
vechicleMarker.rotation = CLLocationDegrees(getHeadingForDirection(fromCoordinate: oldloc, toCoordinate: newloc))
googleMapView.animate(toLocation: newloc)
}
}
}
}
}func timerMethod() {
pocTimer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(pocDrawPolyline), userInfo: nil, repeats: true)
}`

How to draw a CLLocationCoordinate2Ds on MKMapSnapshotter (drawing on mapView printed image)

I have mapView with array of CLLocationCoordinate2D. I use these locations to draw lines on my mapView by using MKPolyline. Now i want to store it as a UIimage. I found that theres class MKMapSnapshotter but unfortunately i can't draw overlays on it "Snapshotter objects do not capture the visual representations of any overlays or annotations that your app creates." So i get only blank map image. Is there any way to get image with my overlays?
private func generateImageFromMap() {
let mapSnapshotterOptions = MKMapSnapshotter.Options()
guard let region = mapRegion() else { return }
mapSnapshotterOptions.region = region
mapSnapshotterOptions.size = CGSize(width: 200, height: 200)
mapSnapshotterOptions.showsBuildings = false
mapSnapshotterOptions.showsPointsOfInterest = false
let snapShotter = MKMapSnapshotter(options: mapSnapshotterOptions)
snapShotter.start() { snapshot, error in
guard let snapshot = snapshot else {
//do something with image ....
let mapImage = snapshot...
}
}
}
How can i put overlays on this image? Or maybe theres other way for that problem.
Unfortunately, you have to draw them yourself. Fortunately, MKSnapshot has a convenient point(for:) method to convert a CLLocationCoordinate2D into a CGPoint within the snapshot.
For example, assume you had an array of CLLocationCoordinate2D:
private var coordinates: [CLLocationCoordinate2D]?
private func generateImageFromMap() {
guard let region = mapRegion() else { return }
let options = MKMapSnapshotter.Options()
options.region = region
options.size = CGSize(width: 200, height: 200)
options.showsBuildings = false
options.showsPointsOfInterest = false
MKMapSnapshotter(options: options).start() { snapshot, error in
guard let snapshot = snapshot else { return }
let mapImage = snapshot.image
let finalImage = UIGraphicsImageRenderer(size: mapImage.size).image { _ in
// draw the map image
mapImage.draw(at: .zero)
// only bother with the following if we have a path with two or more coordinates
guard let coordinates = self.coordinates, coordinates.count > 1 else { return }
// convert the `[CLLocationCoordinate2D]` into a `[CGPoint]`
let points = coordinates.map { coordinate in
snapshot.point(for: coordinate)
}
// build a bezier path using that `[CGPoint]`
let path = UIBezierPath()
path.move(to: points[0])
for point in points.dropFirst() {
path.addLine(to: point)
}
// stroke it
path.lineWidth = 1
UIColor.blue.setStroke()
path.stroke()
}
// do something with finalImage
}
}
Then the following map view (with the coordinates, as MKPolyline, rendered by mapView(_:rendererFor:), like usual):
The above code will create the this finalImage:

Check if user location is inside a shape

I made this method to check if an user location is inside a polygon on a map view (mapkit).
I pass to the method the current user location (CLLocationCoordinate2D) and return a boolean just to know if the user is in a polygon or not.
func userInsidePolygon(userlocation: CLLocationCoordinate2D ) -> Bool{
// get every overlay on the map
let o = self.mapView.overlays
// loop every overlay on map
for overlay in o {
// handle only polygon
if overlay is MKPolygon{
let polygon:MKPolygon = overlay as! MKPolygon
let polygonPath:CGMutablePathRef = CGPathCreateMutable()
// get points of polygon
let arrPoints = polygon.points()
// create cgpath
for (var i:Int=0; i < polygon.pointCount; i++){
let mp:MKMapPoint = arrPoints[i]
if (i == 0){
CGPathMoveToPoint(polygonPath, nil, CGFloat(mp.x), CGFloat(mp.y))
}
else{
CGPathAddLineToPoint(polygonPath, nil, CGFloat(mp.x), CGFloat(mp.y))
}
}
let mapPointAsCGP:CGPoint = self.mapView.convertCoordinate(userlocation, toPointToView: self.mapView)
return CGPathContainsPoint(polygonPath , nil, mapPointAsCGP, false)
}
}
return false
}
I don't really understand why, but the user is never inside a polygon after this test. (and i'm pretty sure he is)
I think it's possible that i have a logic problem with lat/long against x,y.
Does anybody already have work with like this ?
Thanks in advance for all suggestions.
Cheers
The problem is that you are converting the userLocation from the map coordinates to the view coordinates but when you build the path, you don't convert the points to the views coordinates.
You'll need to convert the MKMapPoint to a CLLocationCoordinate2D then to a CGPoint.
let polygonMapPoint: MKMapPoint = arrPoints[i]
let polygonCoordinate = MKCoordinateForMapPoint(polygonPoint)
let polygonPoint self.mapView.convertCoordinate(polygonPointAsCoordinate, toPointToView: self.mapView)
Then use polygonPoint when building the path
CGPathMoveToPoint(polygonPath, nil, polygonPoint.x, polygonPoint.y)
Swift 3, Xcode 8 answer:
func userInsidePolygon(userlocation: CLLocationCoordinate2D ) -> Bool {
var containsPoint: Bool = false
// get every overlay on the map
let o = self.mapView.overlays
// loop every overlay on map
for overlay in o {
// handle only polygon
if overlay is MKPolygon{
let polygon:MKPolygon = overlay as! MKPolygon
let polygonPath:CGMutablePath = CGMutablePath()
// get points of polygon
let arrPoints = polygon.points()
// create cgpath
for i in 0..<polygon.pointCount {
let polygonMapPoint: MKMapPoint = arrPoints[i]
let polygonCoordinate = MKCoordinateForMapPoint(polygonMapPoint)
let polygonPoint = self.mapView.convert(polygonCoordinate, toPointTo: self.mapView)
if (i == 0){
polygonPath.move(to: CGPoint(x: polygonPoint.x, y: polygonPoint.y))
}
else{
polygonPath.addLine(to: CGPoint(x: polygonPoint.x, y: polygonPoint.y))
}
}
let mapPointAsCGP:CGPoint = self.mapView.convert(userlocation, toPointTo: self.mapView)
containsPoint = polygonPath.contains(mapPointAsCGP)
if containsPoint {
return true
}
}
}
return containsPoint
}

Resources