I'm using the ArcGIS 100.6 iOS SDK and am populating the map with markers on a Graphics Overlay Layer
whose locations are stored in a database common to all users of my application. Each marker is stored in a unique record and each record contains the Latitude & Longitude of the marker.
When the App is launched it reads the location of all markers in the database and adds each one to the Graphics Overlay as shown below:
let areaMarker = AGSPictureMarkerSymbol(image: UIImage(named: "CustomMarker")!)
let areaMarkerLocation = AGSPointMakeWGS84(y ?? 0.0, x ?? 0.0)
let markerIcon = AGSGraphic(geometry: areaMarkerLocation, symbol: areaMarker, attributes: >["marker": markerKey])
self.overlay.graphics.add(markerIcon)
As shown above, each marker is assigned an attribute "marker:markerKey" that is the unique database record number (key) where the marker location information is stored and serves as the marker ID.
Once the initial markers are added to the Overlay, the App "Listens" to the database for the following events:
A new marker is added
An existing marker is moved to a new location
An existing marker is deleted
When a marker is moved or deleted the database listener is notified and passed the record number (key) of the marker that was moved (or deleted). If the marker was moved, the record will then contain the new Latitude & Longitude information.
I have experimented with reading the graphics overlay and determined that it is a Collection contained in a NSMutable Array. I can read all attributes as follows:
let graphicsCollection = self.overlay.graphics.mutableArrayValue(forKey: "attributes")
print(graphicsCollection)
The result is:
(
{
marker = "-KlRW2_rba1zBrDPpxSl";
},
{
marker = "-Lu915xF3zQp4dIYnsP_";
}
)
I can do the same for "geometry" and get the array of AGSPoints:
let graphicsCollection = self.overlay.graphics.mutableArrayValue(forKey: "geometry")
print(graphicsCollection)
The result is:
(
"AGSPoint: (-117.826127, 44.781139), sr: 4326",
"AGSPoint: (-112.056906, 33.629829), sr: 4326"
)
I have been unable to determine how to get the "index" of the attribute array (e.g. marker "-KlRW2_rba1zBrDPpxSl" above should have an index of [0]) so I can then use that "index" to access the appropriate AGSPoint and update the Latitude & Longitude or remove the marker.
Thanks in advance for the help.
If you want to move the marker (i.e. an AGSGraphic) you'll want to get the AGSGraphic itself and modify the geometry property. I think that by jumping to the "geometry" in your mutableArrayValue() call you're kind of shooting yourself in the foot a bit.
I would tackle it this way:
let searchMarker = "-KlRW2_rba1zBrDPpxSl"
let newLocation = AGSPointMakeWGS84(40.7128, -74.0060) // NYC
if let graphic = (overlay.graphics as? [AGSGraphic])?.first(where: {
($0.attributes["marker"] as? String) == searchMarker
}) {
// Move the graphic
graphic.geometry = newLocation
// Or remove the graphic
overlay.graphics.remove(graphic)
}
Related
I want to hide the marker after the zoom level reach 17, someone suggested I use clear method, but the issue with it that I have different marker that will show after some event so clear is not going to work any idea how can I made this possible?
To remove a specific marker
myMarker.map = nil
as far as I know there are no definite references to hiding markers, but you can manipulate marker data displayed on the map #CMIIW
as an example
var markers: [GMSMarker] = []
var tempMarker: [GMSMarker] = []
if zoom == 17 {
// TODO: Create tempMarker filter from markers
} else {
// TODO: Create tempMarker filter from markers
}
// TODO:
// mapView.clear()
// Mapview show markers from tempMarker
iOS Swift Google Maps SDK showing markers at specific zoom level?
//To delete
marker.map = nil
//to hide
marker.opacity = 0.0
I'm using the following code to see when markers enter the screen :
let visibleRegion = mapView.projection.visibleRegion()
let bounds = GMSCoordinateBounds(region: visibleRegion)
for i in stride(from: 0, to: markers.count, by: 1){
let marker = markers[i]
if bounds.contains(marker.position) {
print("Is present on screen")
print(marker.position)
} else {
// Marker not on the screen
}
}
This works and when I scroll the map on top of a marker I get the printout.
I've got 30k markers that I'm needing to potentially place onto the map. The markers show up at different zoom levels, and only need to be loaded once the user is able to see them.
The marker is a rounded image view, so as you an imagine loading 30 thousand pictures into a map is a huge task.
I have JSON that I am loading in the Lon/Lat/ImageURL.
Do I need to deinit markers as they leave the screen and init them as they come onto the screen? Are google map annotations reused like a tableview cell? Should I only create the marker once a location from my JSON is in the bounds of the map, or can I create them and only add them to the map once they're in the bounds? What sort of optimization tools should I use?
Thanks for any tips here
I'm trying to draw a polyline on the Google map as a object travels, sometimes the coordinates sent can get duplicated. I want to prevent duplicate coordinate from being added to the GMSMutablePath. Anyway this be achieved?
Currently I use the following method to add the coordinate to the GMSMutablePath. It adds duplicate values as well!
self.path.addLatitude(coordinate.latitude, longitude: coordinate.longitude)
After doing some digging in GoogleMaps SDK, I arrived at this solution. It may not be the perfect one but you can give it a try.
You can iterate through all the coordinates of the path by using coordinate(at:index) method of GMSMutablePath
Iterating the GMSMutablePath coordinates.
//Here path is your GMSMutablePath
for i in 0..<path.count() {
let coordinate = path.coordinate(at: i)
//The coordinate received is a CLLocationCoordinate2D type from which you can get the latitude and longitude.
//Here check the coordinate latitude and longitude is same as your received coordinate, make a return else add to your path.
//You can also keep a flag variable and at the end of all iterations, you can check whether the coordinate is present or not.
print(coordinate)
}
I've been experimenting with Geofire for iOS but can't seem to find any way of returning the distance from the search position in a circle query. The GFQueryResultBlock only returns the key and position. Am I right in assuming that I have to calculate the distance myself?
Let's say I am making a nearby restaurant app with Firebase, and want to display the 20 closest restaurants to the user, ordered by how close they are. I could create a circle query and then increase the search radius via a loop until I find 20 restaurants. Then calculate the distance for each one and sort them before displaying them to the user. Is this a reasonable approach, given that a large amount of work is being done in the app to structure the data (calculating distance & sorting)?
I've noticed that javascript Geofire queries return distance from the center, but I guess the iOS and android versions are different from this.
When you are querying from Geofire, relevant results are automatically ordered by ascending order. So in order to get the distance , Im just using the distanceFromLocation function:
Here my code:
func getGeoFirePlaces(){
let geofireRef = FIRDatabase.database().reference().child("testForGeofire")
let geoFire = GeoFire(firebaseRef: geofireRef)
//geoFireRef is pointing to a firebase reference where I previously set all places' location
let userPosition = CLLocation(latitude: 32.0776067, longitude: 34.78912)
let circleQuery = geoFire.queryAtLocation(userPosition, withRadius: 2)
circleQuery.observeEventType(.KeyEntered, withBlock: { (key: String!, location: CLLocation!) in
print("Key '\(key)' entered the search area and is at location '\(location)'")
//getting distance of each Place return with the callBack
let distanceFromUser = userPosition.distanceFromLocation(location)
print(distanceFromUser)
})
}
Hope this help!
I have multiple arrays that I want to iterate over and plug in as values later in my code for markers on a map. I have the markers populated but can't figure out how to apply the text marker title and subtitle to the marker associated with the location.
iOS and Swift 2
edit: using Mapbox iOS SDK as well.
How do I write the for loop that will do this?
My code trimmed down so far.
Arrays taken from geojson file:
//place variable to array of strings
var place = [String]()
//result from this array ["park", "playground", "parking"]
//pass variable to array of strings
var pass = [String]()
//result from this array ["True", "False", ""]
I have also done this with latitude and longitude but won't add that here. But I do need to add the for loop for it.
//lat and long is served and then all the r,z points, then another lat, long...need to fix this and it may work...
for var (i,x) in zip(lat, long) {
print(i)
print(x)
//print(i + ", " + x)
// String to double conversion
let lati = NSString(string: i).doubleValue
let longi = NSString(string: x).doubleValue
var point = MGLPointAnnotation()
point.coordinate = CLLocationCoordinate2D(latitude: lati, longitude: longi)
let r = place
print(r)
point.title = r
print(r)
let z = pass
print(z)
point.subtitle = z
mapView.addAnnotation(point)
}
So the markers are all added correctly to the map based on lat and long. The marker text however is only added once, and uses the last record served. I am betting I am missing something simple here, as I am newish to coding with Swift 2.
I am receiving this error message "Cannot assign value of type '[String]' to type 'String?'" associated with this line "point.title = r" and this line "point.subtitle = z" which complicates things.
Any suggestions will be greatly appreciated.
If you need more information I can edit as needed but hopefully you have everything you need to help me set up this for loop.
What is the data type of title and subtitle in point.title and point.subtitle respectively?
You are assigning a String Array place to r. Whereas, point.title is not an array type. I'm still missing your question on where do you want to add the loop?