I was wondering if was possible to check if a marker is outside of the region.
I can check if the user left the region, but I want to also check if any marker on the map to the same thing as well, I want to check the geofence region between to markers.
func setUpGeofence() {
let geofenceRegionCenter = CLLocationCoordinate2DMake(getLatitude(),getLongitude());
let geofenceRegion = CLCircularRegion(center: geofenceRegionCenter, radius: 400, identifier: "Geofence");
geofenceRegion.notifyOnExit = true;
geofenceRegion.notifyOnEntry = true;
self.locationManager.startMonitoring(for: geofenceRegion)
}
}
You can use CLLocation.distance(from:) to calculate each marker's distance from your geofence center.
let center = CLLocation(latitude: getLatitude(), longitude: getLongitude())
for marker in markers {
if marker.location.distance(from: center) > radius {
// outside
} else {
// inside
}
}
Related
I'm working on an App where I get the user location and some custom pins (Restaurants) that I added. What I want to do is set radius (between 5km and 10km) distance between the user and the restaurants and then show those that are within the area.
My question is, how can I set that radius and get the locations within it
Best regards!
You can do some distance calculations to determine what pins to show.
let pins = Array<MKPointAnnotation>() // This is an empty array, so just use your array of locations or add to this.
if let currentLocation = locationManager.location?.coordinate {
for pin in pins {
let locationMapPoint = MKMapPointForCoordinate(currentLocation)
let pinMapPoint = MKMapPointForCoordinate(pin.coordinate)
let distance = MKMetersBetweenMapPoints(locationMapPoint, pinMapPoint)
if distance >= 5000 && distance <= 10000 {
self.map.addAnnotation(pin)
}
}
}
If you wanted something tidier, you could also do this.
let pins = Array<MKPointAnnotation>()
if let currentLocation = locationManager.location?.coordinate {
let filtered = pins.filter { $0.coordinate.distance(to: currentLocation) >= 5000 && $0.coordinate.distance(to: currentLocation) <= 10000 }
self.map.addAnnotations(filtered)
}
You'll need this extension aswell.
extension CLLocationCoordinate2D {
func distance(to coordinate: CLLocationCoordinate2D) -> Double {
return MKMetersBetweenMapPoints(MKMapPointForCoordinate(self), MKMapPointForCoordinate(coordinate))
}
}
I'm working on iOS app that draw polyline on the map to highlight the route user has taken. I can draw the poly line with no problem. But, the line is not a clean line that you see on Googlemap. It's a bit squiggly like handdrawn as you can see in the pic. Apology for big images as I don't know how to resize them in SO.
How can I acheive the result like this one in the picture :
This is my code that is responsible for drawing poly line :
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = manager.location {
if (firstLoad) {
firstLoad = false
recentCoordinate = location.coordinate
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: Constants.MapView.Zoom, bearing: 0, viewingAngle: Constants.MapView.ViewingAngle)
}
else {
mapView.animateToLocation(CLLocationCoordinate2D(latitude: location.coordinate.latitude,longitude: location.coordinate.longitude))
}
// this is suppose to filter out noise. I don't think it's working
let age = 0 - location.timestamp.timeIntervalSinceNow
if (age > 120) {
return; // ignore old (cached) updates
}
if (location.horizontalAccuracy < 0) {
return; // ignore invalid updates
}
if (location.horizontalAccuracy <= 10){
// this is a valid update
// Draw route as user moves along
path.addCoordinate(CLLocationCoordinate2D(latitude: location.coordinate.latitude,
longitude: location.coordinate.longitude))
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.redColor()
polyline.strokeWidth = 3
polyline.map = mapView
// Save the coordinates to array
coordinateArray.append([location.coordinate.latitude, location.coordinate.longitude])
}
}
}
How do you center the google map as user is moving, like driving? The code I have does not center the user, and the user just goes toward the border and disapper. How do you keep user in center all the time?
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = manager.location {
if (firstLoad) {
firstLoad = false
recentCoordinate = location.coordinate
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: Constants.MapView.Zoom, bearing: 0, viewingAngle: Constants.MapView.ViewingAngle)
}
else {
// This line of code suppose to center the user as user moves along while keeping the zoom and angle state user has chosen
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: mapView.camera.zoom, bearing: 0, viewingAngle: mapView.camera.viewingAngle)
}
// Filter out noise. Currently not working
let age = 0 - location.timestamp.timeIntervalSinceNow
if (age > 120) {
return; // ignore old (cached) updates
}
if (location.horizontalAccuracy < 0) {
return; // ignore invalid updates
}
if (location.horizontalAccuracy <= 10){
// this is a valid update
// Draw route as user moves along
path.addCoordinate(CLLocationCoordinate2D(latitude: location.coordinate.latitude,
longitude: location.coordinate.longitude))
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.redColor()
polyline.strokeWidth = 3
polyline.map = mapView
// Save the coordinates to array
coordinateArray.append([location.coordinate.latitude, location.coordinate.longitude])
}
}
There are couple other ways you can try.
let update = GMSCameraUpdate.setTarget(location.coordinate)
mapView.moveCamera(update)
or
mapView.camera = GMSCameraPosition.camera(target: location.coordinate, zoom: 15.0)
or
let update = GMSCameraUpdate.setTarget(location.coordinate)
mapView.animate(with: update)
If anybody looking for swift 2.3 solution like myself, here is the solution :
mapView.animateToLocation(CLLocationCoordinate2D(latitude: location.coordinate.latitude,longitude: location.coordinate.longitude))
I am displaying a marker in a particular place, along with displaying the current address in the address label on Google Maps.
Now, I want to change the location by moving the Google Map, but the problem is that when I am moving the map, I should simultaneously move the marker along with the map, and I should display the address of that location in the address label.
How can I do that?
I tried this:
let destinationMarker = GMSMarker(position: self.destinationLocation.coordinate)
let image = UIImage(named:"sourcemarker")
destinationMarker.icon = image
destinationMarker.draggable = true
destinationMarker.map = self.viewMap
//viewMap.selectedMarker = destinationMarker
destinationMarker.title = "hi"
destinationMarker.userData = "changedestination"
func mapView(mapView: GMSMapView, didEndDraggingMarker marker: GMSMarker)
{
if marker.userData as! String == "changedestination"
{
self.destinationLocation = CLLocation(latitude: marker.position.latitude, longitude: marker.position.longitude)
self.destinationCoordinate = self.destinationLocation.coordinate
//getAddressFromLatLong(destinationCoordinate)
}
}
// UpdteLocationCoordinate
func updateLocationoordinates(coordinates:CLLocationCoordinate2D) {
if destinationMarker == nil
{
destinationMarker = GMSMarker()
destinationMarker.position = coordinates
let image = UIImage(named:"destinationmarker")
destinationMarker.icon = image
destinationMarker.map = viewMap
destinationMarker.appearAnimation = kGMSMarkerAnimationPop
}
else
{
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
destinationMarker.position = coordinates
CATransaction.commit()
}
}
// Camera change Position this methods will call every time
func mapView(mapView: GMSMapView, didChangeCameraPosition position: GMSCameraPosition) {
let destinationLocation = CLLocation()
if self.mapGesture == true
{
destinationLocation = CLLocation(latitude: position.target.latitude, longitude: position.target.longitude)
destinationCoordinate = destinationLocation.coordinate
updateLocationoordinates(destinationCoordinate)
}
}
Update for Swift 4:
First you need to conform with the GMSMapViewDelegate:
extension MapsVC: GMSMapViewDelegate{
And then set your VC as the delegate of viewMap in the viewDidLoad()
viewMap.delegate = self
After that you just need to use the following method to get updates from the camera position and set that as the new position for the marker:
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
destinationMarker.position = position.target
print(destinationMarker.position)
}
There is one trick that can help you out here. Instead of using a GMSMarker here, put an image pointing to the center, over your Google MapView.
You can easily find the coordinates of Map's center using this :
double latitude = mapView.camera.target.latitude;
double longitude = mapView.camera.target.longitude;
Or this
GMSCoordinateBounds *bounds = nil;
bounds = [[GMSCoordinateBounds alloc] initWithRegion: visibleRegion];
CLLocationCoordinate2D centre = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude) / 2);
Now you can get the location address by using Geocoding API by google.
Here is the reference :
https://developers.google.com/maps/documentation/ios-sdk/reference/interface_g_m_s_geocoder
You can refresh Address when this delegate method is called :
- (void) mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position
Hope this helps.
Based from Release Notes made in Maps SDK for IOS, changing a markers position will cause the marker to animate to the new location.
To resolve this, you may use new features for GMSMarker as stated in Release Version 1.5 such as:
Markers can be made draggable using the draggable property, and new drag delegate methods have been added to GMSMapViewDelegate. (Issue 4975)
Added GMSMarkerLayer, a custom CALayer subclass for GMSMarker that supports animation of marker position and rotation. (Issue 4951, Issue 5743)
In addition to that, this post in GitHub - GoogleMapsAnimationGlitch
and this SO post - How to smoothly move GMSMarker along coordinates in Objective c
might also help.
objc_sync_enter(self)
CATransaction.begin()
CATransaction.setCompletionBlock { [weak self] in
guard let `self` = self else { return }
DispatchQueue.main.delay(0.5) {
self.mapViewService.setCenter(location.coordinate, animate: true, zoom: nil)
}
objc_sync_exit(self)
}
CATransaction.setAnimationDuration(vm.continunousLocationUpdateTimeInterval)
self.mapViewService.moveMarker(position: location.coordinate, marker: userMarker)
CATransaction.commit()
the code above is useful to me, the moveMarker method just packaged the code marker.position = newCoordinate
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
}