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
}
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
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 !
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
I have an app that displays the locations the user has walked on an MKMapView. When the user leaves the map view the app grabs the screen and saves the image on disk. Up til iOSS 10.3 this method was always successful. With iOS 11.0 the screen grab is a blank image. I get no notification from xcode that there were some changes and that I need to adjust the code.
Interestingly, screen grabs from text pages are still grabbed and saved successfully.
Did anyone encounter the same problem and got the solution?
The code that has always been successful up til now, is:
override func viewWillDisappear(_ animated: Bool) {
//Set the full file name under which the track will be saved.
let fileBaseName = self.imageName.appending(String(describing: (self.display?.trackDate)!))
let fileFullName = fileBaseName.appending(".png")
//Check if the image already has been saved
if !AuxiliaryObjects.shared.getImageFileName(with: fileFullName ) {
//Create the sizes of the capture
let screenRect = self.trackMapView.frame
let screenSize = screenRect.size
let screenScale = UIScreen.main.scale
var grabRect = self.trackMapView.convertRegion(self.mapRegion, toRectTo: self.view)
var heightAdjustment : CGFloat = 0.0
//Grab the image from the screen
UIGraphicsBeginImageContextWithOptions(screenSize, false, screenScale)
self.trackMapView.drawHierarchy(in: screenRect, afterScreenUpdates: true)
let myImage = UIGraphicsGetImageFromCurrentImageContext()
grabRect.origin.x *= (myImage?.scale)!
grabRect.origin.y *= (myImage?.scale)!
grabRect.size.width *= (myImage?.scale)!
grabRect.size.height *= (myImage?.scale)!
let grabImage = (myImage?.cgImage)!.cropping(to: grabRect)
let mapImage = UIImage(cgImage: grabImage!)
UIGraphicsEndImageContext()
AuxiliaryObjects.shared.save(image: mapImage, with: fileFullName, and: self.imageName)
self.display?.displayImage = AuxiliaryObjects.shared.getImage(with: fileFullName, with: self.tableImageRect)!
} else {
self.display?.displayImage = AuxiliaryObjects.shared.getImage(with: fileFullName, with: self.tableImageRect)!
}
}
I submitted a code level support request at Apple to get the answer to the question. Apple does not support the use of drawHierarhy in grabbing a MapKit screen. The way to go is using the MKMapSnapshotter utility to create an MKMapSnapshot and then draw in the lines and annotations by converting all the map coordinates to view coordinates.
Since this gave me some problems with getting the a mirrored image and translating the coordinates properly, I decided to use the layer method render(in: CGContext) this provided me a well functioning very efficient screen grab.
func creatSnapshot(with fileName: String) {
UIGraphicsBeginImageContextWithOptions(self.trackMapView.frame.size, false, UIScreen.main.scale)
let currentContext = UIGraphicsGetCurrentContext()
self.trackMapView.layer.render(in: currentContext!)
let contextImage = (UIGraphicsGetImageFromCurrentImageContext())!
UIGraphicsEndImageContext()
let region = self.trackMapView.region
var cropRect = self.trackMapView.convertRegion(region, toRectTo: self.trackMapView.superview)
cropRect.origin.x *= contextImage.scale
cropRect.origin.y *= contextImage.scale
cropRect.size.height *= contextImage.scale
cropRect.size.width *= contextImage.scale
let cgMapImage = contextImage.cgImage?.cropping(to: cropRect)
let mapImage = UIImage(cgImage: cgMapImage!)
AuxiliaryObjects.shared.save(image: mapImage, with: fileName, and: self.imageName)
self.displayTrack?.displayImage = AuxiliaryObjects.shared.getImage(with: fileName, with: self.tableImageRect)!
NotificationCenter.default.post(name: self.imageSavedNotification, object: self)
}
I use a Google Maps Api for maps and markers.
I enable accessibility for markers by setting: mapView.accessibilityElementsHidden = false
Now all my custom markers on map have accessibility ids like: myappname.GMSPlaceMarker_somenumbers, for example myappname.GMSPlaceMarker_0x600000170200.
How could i set a one accessibilityIdentifier for all pins, for example Map pin?
I already tried:
marker.accessibilityLabel = "Map pin" but it set label value, not id
marker.title = "Map pin" nothing changes
marker.setValue("Map pin", forKey: "accessibilityIdentifier") nothing changes
My marker is let marker = GMSPlaceMarker() where class GMSPlaceMarker: GMSMarker
try this,
func markPoints() {
var annotationCoord : CLLocationCoordinate2D = CLLocationCoordinate2D()
annotationCoord.latitude = (selectedLocation.latitude as NSString).doubleValue
annotationCoord.longitude = (selectedLocation.longitude as NSString).doubleValue
let annotationPoint: MKPointAnnotation = MKPointAnnotation()
annotationPoint.coordinate = annotationCoord
annotationPoint.title = selectedLocation.name
annotationPoint.subtitle = "Anand: 7348858742"
theMap.addAnnotation(annotationPoint)
}
If you are using swift 4 try this one,
var destionationMarker: GMSMarker!
func setupDriverMarker(coordinate: CLLocationCoordinate2D) {
destionationMarker = GMSMarker(position: coordinate)
for pin: GMSMarker in self.DestinationMarkerArray {
if pin.userData as! String == "drivermarker" {
pin.map = nil
}
}
destionationMarker.title = "Your Title"
destionationMarker.appearAnimation = GMSMarkerAnimation.pop
let images = #imageLiteral(resourceName: "ic_map_marker")
destionationMarker.icon = images
destionationMarker.userData = "drivermarker"
destionationMarker.opacity = 1
destionationMarker.map = journeyMapView
}
Implementing like this
DriverLocation = CLLocationCoordinate2D(latitude: 21.2362546, longitude: 72.8751862)
setupDriverMarker(coordinate: DriverLocation)
I had a similar issue as well, there is no accessibilityIdentifier for GMSMarker. What I did, setting either an accessibilityIdentifier to icon or iconView such as:
marker.icon?.accessibilityIdentifier = "something" or
marker.iconView?.accessibilityIdentifier = "something"