I am new to maps technology .
I am using google apis to the the json data to fetch the routes on the map using the polyline .
I need some help regarding the polyline . I want to clear the path of polyline as per users drives through the route . I tried searching many solutions but couldnt worked for me .
I have attached an image for the same ...
Function to draw polyline ---
func drawPath(from polyStr: String){
print("inside Drawpath")
path = GMSPath(fromEncodedPath: polyStr)!
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 6
polyline.map = myMapView // Google MapView
polyline.strokeColor = UIColor(red: 0, green: 128/255, blue: 1, alpha: 1)
// let camera = GMSCameraUpdate.fit(GMSCoordinateBounds(coordinate: CLLocationCoordinate2D(latitude: Double(lat), longitude: Double(long)), coordinate: CLLocationCoordinate2D(latitude: Double(directionlat)!, longitude: Double(directionlat)!)))
let cameraUpdate = GMSCameraUpdate.fit(GMSCoordinateBounds(coordinate: CLLocationCoordinate2D(latitude: Double(lat), longitude: Double(long)), coordinate: CLLocationCoordinate2D(latitude: Double(directionlat)!, longitude: Double(directionlat)!)))
// self.timer = Timer.scheduledTimer(timeInterval: 0.003, target: self, selector: #selector(animatePolylinePath), userInfo: nil, repeats: true)
//myMapView.moveCamera(cameraUpdate)
let currentZoom = myMapView.camera.zoom
// myMapView.animate(toZoom: currentZoom - 1.4)
}
Any help is appreciated .
Thanks and regards
I have faced the same issue and I found a solution which works fine in my current 2 projects. So let me share my idea here.
first, get the initial polypathString from the direction API. Now you can get the number of points in the path by using GMSPath object and by combining that with CLLocation.distance method I have tried to find whether the driver is in the path.
If he is found on the path
I take the index from array in which the driver is close and start drawing from there
else
I request to fetch an another direction API since he is not the path drawing it is not right.
let polyPath = GMSPath.init(fromEncodedPath: pathStr)!
func isPathChanged(byDriver coordinate : CLLocation) -> Bool{
guard self.path.count() != 0 else{return true}
for range in 0..<path.count(){
let point = path.coordinate(at: range).location//CLLocation
if point.distance(from: coordinate) < 75{
self.driversPositiionAtPath = range
return false
}
}
self.driversPositiionAtPath = 0
return true
}
not just do the magic trick
Note: I am maintaining a variable to store the driver's position at polyline driversPositiionAtPath
if isPathChanged(byDriver : driversLocation){
self.wsToFetchNewPathFromDirection()
}else{
self.drawRoute(for : polyPath )
}
func drawRoute(for path : GMSPath){
let drawingPath = GMSMutablePath()
for i in self.driversPositiionAtPath..<path.count(){
drawingPath.add(path.coordinate(at: i))
}
self.polyline.path = drawingPath
self.polyline.strokeColor = UIColor.black
self.polyline.strokeWidth = 3.0
self.polyline.map = mapLocation
}
Check if driver is on the path or not, once he moves or every 10 seconds.
happy codding !
Related
i am trying to create clusters in my google map. When i show clusters on map i want to show only the cluster count and when the cluster is clicked or zoom then only i want to show my Custom marker view.
Bu the marker view are already present before clustering and when i zoom in a cluster, the default pins are shown, which i dont want.
below is the code for clustering
for data in self.map_data {
//check if map data is on or off and show map data accordignly
let offerdata = data
let geocoder = CLGeocoder()
let strAddress = "\(offerdata.agent_street ?? "")"+" "+"\(offerdata.agent_city ?? "")"+" "+"\(offerdata.agent_zipcode ?? "")"
//MARK: GEOCODER FOR GETTING LAT LONG BASE ON ADDRESS
geocoder.geocodeAddressString(strAddress) {
placemarks, error in
let placemark = placemarks?.first
let lat = Double(placemark?.location?.coordinate.latitude ?? 0.00)
let lon = Double(placemark?.location?.coordinate.longitude ?? 0.00)
print("Lat: \(String(describing: lat)), Lon: \(String(describing: lon))")
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: lon, zoom: 5)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: lat, longitude: lon)
marker.title = "\(offerdata.agent_firstname ?? "")"+" \(offerdata.agent_lastname ?? " ")"
marker.iconView?.backgroundColor = .lightGray
marker.userData = data
//amrut
// Clustering
let item = POIItem(position: CLLocationCoordinate2DMake(lat, lon), name: "marker.title" ?? "",data: data)
self.clusterManager.add(item)
//map info views
var image = UIImage()
var currency = String()
if data.currency == "AUD"{
currency = "$\(data.currency ?? "")"
}
else if data.currency == "EUR"{
currency = "€\(data.currency ?? "")"
}
else{
currency = "£\(data.currency ?? "")"
}
let amount = "\(data.offer_amount ?? "")".convertTo2Decimal+" "+currency
//create custom marker accordin to condtions
// "\(agent.offer_amount ?? "")".convertTo2Decimal+" "+"\(agent.currency ?? "AUD")"
if data.auto_approval_on_off == "ON"{
if (data.is_badge_display == "1") {
image = UIImage(named: "blueMarker.png") ?? UIImage()//blue
} else {
image = UIImage(named: "orangeMarker.png") ?? UIImage()//oranfge
}
}
else if (data.is_badge_display == "1") {
image = UIImage(named: "blueMarker.png") ?? UIImage()//blue
} else {
image = UIImage(named: "blackMarker.png") ?? UIImage()//black
}
let customMarker = CustomMarkerView(frame: CGRect(x: 0, y: 0, width: 100, height: 40), image:image , lblText:amount)
// marker.iconView=customMarker
marker.map = self.MapView
self.MapView.camera = camera
}
}
and the same thing i do when in 'GMUClusterRendererDelegate willRenderMarker' delegate method and just uncomment the marker.iconView=customMarker
can anyone please help me what i need to be doing. It will be really helpful
Image for reference
Image for reference2
Two things i want:
1- show clustering for initial load without map icon views
2- show only map icon view and not the pins after cluster rendered/zoom
Please someone help me
I have a problem similar to this. The work around I came up with was to run the clearMarkers function and then re-add the entire array of markers back in using the addMarkers function
I'm still working on this so if I find a better answer I'll update
How to display current location from coordinates manually? I am taking coordinates from another GPS device. How to set it manually?
Where the coordinates come from is largely immaterial: as long as you have one you can set the map to display an area including that point. If you want to display the point itself you can add an annotation:
A simple example method to drop a pin and zoom in to its location:
func createAnnotation(from coordinate: CLLocationCoordinate2D, title: String) -> MKPointAnnotation {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = title
return annotation
}
func dropPinAndZoomIn(to coordinate: CLLocationCoordinate2D){
var spanDelta = 0.035 //the width/height of the map area in degrees
let annotation = createAnnotation(from: coordinate, title: "My Location")
mapView.removeAnnotations(mapView.annotations) //clear any prev annotations
mapView.addAnnotation(annotation)
let span = MKCoordinateSpan(latitudeDelta: spanDelta, longitudeDelta: spanDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
let displayRegion = mapView.regionThatFits(region) //ensure the region can be displayed in the mapView's view
mapView.setRegion(displayRegion, animated: true)
}
For some reason, when I try to draw two MKPolygons on a mapView (MKMapView) I end up with the two polygons connected. Drawing each polygon individually works fine. And I've verified that each of the polygons don't contain any of the coordinates to form the connection between the two. I've attached an image with the two polygons connected
For reference, here's where I call to add the polygons.
func addPeakTimePolygon(from coordinatesArray: [CLLocationCoordinate2D], title: Int){
let polygon = MKPolygon(coordinates: coordinatesArray, count: coordinatesArray.count)
polygon.title = String(title)
//Should refactor to use .contains(where:
var shouldAdd = true
for polygon in self.currentPolygons{
if polygon.title == String(title){
shouldAdd = false
}
}
if shouldAdd{
self.currentPolygons.append(polygon)
self.mapView.add(polygon)
}
}
And here's my rendererFor code:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = #colorLiteral(red: 0, green: 0.6862745098, blue: 0.7607843137, alpha: 1)
renderer.lineWidth = 5.0
return renderer
}
else if overlay is MKPolygon {
let renderer = MKPolygonRenderer(overlay: overlay)
renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 2
return renderer
}
return MKOverlayRenderer()
}
It seems like you're making one overlay consisting of two polygons. You can't do that with an MKPolygonRenderer; you will get one polygon, as you are observing.
You will need separate overlays, one for each polygon. Unless you are using iOS 13! In that case, you are in luck: New in iOS 13, multiple polygons or polylines can be combined into an MKMultiPolygon or MKMultiPolyline and drawn by an MKMultiPolygonRenderer or MKMultiPolylineRenderer.
I forgot to check / post the code that was calling addPeakTimePolygon. Here is the problematic code below:
var locationList: [CLLocationCoordinate2D] = []
var title = 0
if let peakTimeCampaignList = data["PeakTimeRewardCampaignList"] as? [[AnyHashable:Any]]{
for campaign in peakTimeCampaignList{
if let polygonPoints = campaign["CampaignPolygon"] as? [[AnyHashable:Any]]{
for polygonPoint in polygonPoints{
let polygonPoint = CLLocationCoordinate2D(latitude: polygonPoint["Latitude"] as! CLLocationDegrees, longitude: polygonPoint["Longitude"] as! CLLocationDegrees)
locationList.append(polygonPoint)
}
}
if let id = campaign["Id"] as? Int{
title = id
}
mapBundle.addPeakTimePolygon(from: locationList, title: title)
}
}
As you can see locationList wasn't being cleared out within the loop, causing whatever we sent over to addPeakTimePolygon to have coordinates from two polygons and MapKit was trying it's best to form a polygon between them.
This was a dumb mistake, but hoping someone else sees this with the same problem!
I am using the Google Maps SDK for iOS - https://developers.google.com/maps/documentation/ios-sdk/marker#use_the_markers_icon_property
Combined with the Maps SDK for iOS Utility Library https://developers.google.com/maps/documentation/ios-sdk/utility/kml-geojson#render-kml-data
I am trying to use the utility library to render a kml file on a map. It mostly works, however the custom icons for the markers are not loading. The markers with their titles, snippets, and locations all load correctly. The only thing that does not work is the custom icon for the marker.
Originally, I thought it was an issue with the utility library, so I spent some time trying to write my own code to go through the kml file and add the custom markers myself. However, before I got too far I noticed that even when I try to add a basic marker with a custom icon, I cannot. This led me to believe it was an issue not with the utility library but with the Maps SDK for iOS. I've tried moving the folder that the image is in, and making sure that the code can see the path to the images, but I cannot get it to work.
This is the code that I have in my project
let path = Bundle.main.path(forResource: testFile, ofType: "kml")
let url = URL(fileURLWithPath: path!)
let kmlParser = GMUKMLParser(url: url)
kmlParser.parse()
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: long, zoom: zoom)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.mapType = GMSMapViewType.terrain
mapView.isMyLocationEnabled = true
mapView.settings.zoomGestures = true
mapView.settings.myLocationButton = true
let renderer = GMUGeometryRenderer(map: mapView, geometries: kmlParser.placemarks, styles: kmlParser.styles, styleMaps: kmlParser.styleMaps)
renderer.render()
This also does not work
let position = CLLocationCoordinate2D(latitude: lat, longitude: long)
let marker = GMSMarker(position: position)
marker.title = "Test"
marker.icon = UIImage(named: "icon-1")
marker.map = mapView
Thanks in advance for any help
I haven't figured out why the utils library wasn't working, but I did come up with my own fix. It's horrible, but I can come back and make it better later after we've finished adding all the other necessary features to the app and can focus on cleaning up the code .
First, I made a new array of placemarks that had everything except the map markers. I then used this array of placemarks instead of kmlParser.placemarks, so that everything else could be added by the utility library.
//Removing markers without icons
var myIndex = 0
var removed = [GMUGeometryContainer]()
for mark in kmlParser.placemarks{
if(mark.geometry.type != "Point"){
removed.append(kmlParser.placemarks[myIndex])
}
myIndex += 1
}
let renderer = GMUGeometryRenderer(map: mapView, geometries: removed, styles: kmlParser.styles, styleMaps: kmlParser.styleMaps)
renderer.render()
After that, I made my own horrible horrible method that reads the kml file again, and only picks out the placemarks and styles for them and returns an array of Markers.
func addMarkers(fileName:String) -> [GMSMarker]{
var markers = [GMSMarker]()
if let path = Bundle.main.path(forResource: fileName, ofType: "kml"){
do{
let data = try String(contentsOfFile: path, encoding: .utf8)
let myStrings = data.components(separatedBy: .newlines)
var styleToIcon = [String: String]()
var lineNum = 0
for line in myStrings{
//Detecting new style that will be used in placemarks
if line.contains("Style id") && line.contains("normal") && !line.contains("line-"){
let newKey = String(line.split(separator: "\"")[1])
let newValue = String(myStrings[lineNum+4].split(separator: ">")[1].split(separator: "/")[1].split(separator: "<")[0])
styleToIcon[newKey] = newValue
}
//Detecting new placemark on map
else if(line.contains("<Placemark>") && !myStrings[lineNum+2].contains("#line")){
//Get name
var name = myStrings[lineNum+1].split(separator: ">")[1].split(separator: "<")[0]
//Sometimes name has weird CDATA field in it that needs to be removed
if(name.contains("![CDATA")){
name = name.split(separator: "[")[2].split(separator: "]")[0]
}
//Get snippet (description)
var snippet = myStrings[lineNum+2].split(separator: ">")[1].split(separator: "<")[0]
//Sometimes snippet has weird CDATA field in it that needs to be removed
if(snippet.contains("![CDATA")){
snippet = snippet.split(separator: "[")[2].split(separator: "]")[0]
}
//Get style
let style = String(myStrings[lineNum+3].split(separator: ">")[1].split(separator: "#")[0].split(separator: "<")[0] + "-normal")
//Get Coordinates
let coordStringSplit = myStrings[lineNum+6].split(separator: ",")
var lat = 0.0
var long = 0.0
if(coordStringSplit[0].contains("-")){
long = Double(coordStringSplit[0].split(separator: "-")[1])! * -1.0
}else{
long = Double(coordStringSplit[0])!
}
if(coordStringSplit[1].contains("-")){
lat = Double(coordStringSplit[1].split(separator: "-")[1])! * -1.0
}else{
lat = Double(coordStringSplit[1])!
}
//Create marker and add to list of markers
let position = CLLocationCoordinate2D(latitude: lat, longitude: long)
let marker = GMSMarker(position: position)
marker.title = String(name)
marker.snippet = String(snippet)
marker.icon = UIImage(named: styleToIcon[style]!)
markers.append(marker)
}
lineNum += 1
}
}catch{
print(error)
}
}
return markers
}
This is so heavily related to how my kml files look that I doubt it will help anyone else, but I thought I should post it just in case.
Now that we have that method, all we need to do is go back to where we were rendering all of the kml data and render those markers on the map
//Adding markers with icons
let newMarkers = addMarkers(fileName: courseName)
for mark in newMarkers{
mark.map = mapView
}
I also had to go through my kml files manually and fix some of the image names, but that wasn't a big deal. Even if the utility library worked I would need to do that because the utility library only does kml files and not kmz, so each kml file references the same folder for images and uses the same names for images. It's fine, only takes a few minutes per file. Would be nice if there was a kmz library but oh well.
Hopefully this helps someone else, and hopefully I can find the real solution soon (unless its a problem with the utility library in which case hopefully it's fixed soon).
//call method by passing ;
if userLocation.coordinate.latitude != 0.0 && userLocation.coordinate.longitude != 0.0
{
self.updateCurrentPositionMarker(currentLocation: CLLocation(latitude: userLocation.coordinate.latitude, longitude:userLocation.coordinate.longitude))
}
//methods
func updateCurrentPositionMarker(currentLocation: CLLocation) {
self.currentPositionMarker.map = nil
self.currentPositionMarker = GMSMarker(position: currentLocation.coordinate)
if self.imageDataUrl != ""
{
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude, zoom: 18.0)
self.mapView.camera = camera
//self.imageDataUrl == image to show
self.currentPositionMarker.iconView = self.drawImageWithProfilePic(urlString:self.imageDataUrl,image: UIImage.init(named: “backgroungImage”)!)
self.currentPositionMarker.zIndex = 1
}
self.currentPositionMarker.map = self.mapView
self.mapView.reloadInputViews()
}
func drawImageWithProfilePic(urlString:String, image: UIImage) -> UIImageView {
let imgView = UIImageView(image: image)
imgView.frame = CGRect(x: 0, y: 0, width: 90, height: 90)
let picImgView = UIImageView()
picImgView.sd_setImage(with:URL(string: urlString))
picImgView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
imgView.addSubview(picImgView)
picImgView.center.x = imgView.center.x
picImgView.center.y = imgView.center.y-10
picImgView.layer.cornerRadius = picImgView.frame.width/2
picImgView.clipsToBounds = true
imgView.setNeedsLayout()
picImgView.setNeedsLayout()
// let newImage = imageWithView(view: imgView)
// return newImage
return imgView
}
Im building an app with google maps, and i would like to show a route between 2 static points
I was folling this tutorial but i can't make it yet, for some reason it dont show the route.
I dont want to make an dinamic route i just want it from two point that i´ve define
here's some code
GMSServices.provideAPIKey("MY API KEY")
let camera = GMSCameraPosition.camera(withLatitude: 19.0660043, longitude: -98.12050499999998, zoom: 18.0)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view = mapView
var source = CLLocationCoordinate2DMake(19.060914, -98.125935)
var destination = CLLocationCoordinate2DMake(19.1660043, -98.13000)
plx help
You need to create a path first and then use that path to create a polyline
let path = GMSMutablePath()
path.add(source)
path.add(destination)
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.red
polyline.strokeWidth = 3.0
polyline.map = self.map