Get google map currently rotated angle by user in iOS - ios

I am using google map in the application. When I do not rotate map everything works fine. But when I rotate map I am getting problem as shown in image. To solve that I need to get the google map currently rotated angle by the user. I need to get this so that I can place the marker back on the map at the same angle. Currently my map angle after rotation and my overlay seems to be different after placing plan image on map even when I am getting correct top left & bottom right corner coordinates.
My code
let topCoordinate = CLLocationCoordinate2D(latitude: topLattitude, longitude: topLongitude)//top
let bottomCoordinate = CLLocationCoordinate2D(latitude: bottomLattitude, longitude: bottomLongitude)//bottom
overlayBounds = GMSCoordinateBounds(coordinate: topCoordinate, coordinate:
bottomCoordinate)
let overlay = GMSGroundOverlay(bounds: overlayBounds, icon: planImage)
overlay.map = mapView

GMSMapViewDelegate
Bearing of the camera, in degrees clockwise from true north.
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
print(position.bearing)
}

I have solved my problem by changing the overlay method as
let zoomLevel = (object["image_zoom_level"] as? NSString)?.doubleValue
let overlay = GMSGroundOverlay(position: centreCoordinate, icon: planImage, zoomLevel: CGFloat(zoomLevel!))
overlay.map = mapView
if let angle = (object["rotation_angle"] as? NSString)?.doubleValue{
overlay.bearing = angle
}
Instead of using Overlay bounds I have just used center coordinate and placed overlay image using image zoom level and rotation angle. By this, my image is placing in proper angle and at a proper place. This works fine if the Image size is correct as you want place and the center point is accurate and precise.

Related

How to get a reliable rect of the whole mapview (not just visible) with Mapbox?

So I've been struggling with this for some time. I wish to know the CGRect of the whole map. The issue I am facing right now is that due to the wrapping of the map, the x of the top-left coordinate keeps switching between a negative and a positive value as I move around the map. This results in the width of the map also changing.
I tried restricting the camera bounds to prevent wrapping and it does that job perfectly, but it still doesn't help with x switching between positive/negative as I move around the map.
let extremeBounds = CoordinateBounds(
southwest: CLLocationCoordinate2D(latitude: -62.59334083012023, longitude: -141.328125),
northeast: CLLocationCoordinate2D(latitude: 82.85338229176081, longitude: 167.34375)
)
try? view.mapboxMap.setCameraBounds(with: CameraBoundsOptions(bounds: extremeBounds))
This is how I am calculating the rect on cameraChange:
mapView.mapboxMap.rect(for: self.extremeBounds)
Is there an official way of disabling the wrapping of the map so that the northwest coordinate always appears at the top left side of the map? Or maybe my approach is incorrect and there's an easier way of finding out the rect of the map?
you can try setting ConstrainModeto widthAndHeight, by default this value is heightOnly-> only y axis is constrained.
let mapInitOptions = MapInitOptions(mapOptions: MapOptions(constrainMode: .widthAndHeight))
let mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions)

Calculate bearing in MKMapView gives wrong value while crossing 180 meridian

I need to draw lines to demonstrate transportation of goods on apple maps. To clarify start- and end-point, I draw a little arrowhead on the destination side.The arrowhead is drawn separately but it is reversed in one case.
>-->-->-->-
instead of
<--<--<--<-
I am using MKMapView and MKPolyline to draw lines. I am using MKOverlay to add direction arrows. The steps I follow are,
calculate bearing of
Source : CLLocationCoordinate2D(latitude: -33.8392932, longitude: 151.21519799999999)
Destination: CLLocationCoordinate2D(latitude: 39.645516999999998, longitude: -104.598724)
using the following function
open static func getDirectionOf( _ supplyLineWithCoordinates: [CLLocationCoordinate2D]) -> CGFloat {
guard let sourceCoordniate = supplyLineWithCoordinates.first,
let destinationCoordniate = supplyLineWithCoordinates.last else {
fatalError("Coordinates of supply line not found")
}
let sourcePoint: MKMapPoint = MKMapPointForCoordinate(sourceCoordniate)
let destinationPoint: MKMapPoint = MKMapPointForCoordinate(destinationCoordniate)
let x: Double = destinationPoint.x - sourcePoint.x
let y: Double = destinationPoint.y - sourcePoint.y
var arrowDirection = CGFloat(fmod(atan2(y, x), 360.0))
if arrowDirection < 0.0 {
arrowDirection += 2 * .pi
}
return arrowDirection
}
Rotate the arrow image and add it as the map overlay. The directions are calculated correctly in most of the cases, however, when I select the line shown below the direction is displayed 180 opposite. It starts from Sydney, Australia and ends in Denver, US
When trying to display the region with this two locations in mapView.setVisibleMapRect these region is not displayed, mapview tries to display region starting from Sydney (Australia) to Denver(US) through Asia and Europe, while it should display the map area I have attached above. If you have suggestions for optimisation, feel free to mention it.
I think this might be the reason, the direction should be calculated along the red line but it being calculated along the green line. Both lines are drawn by connecting same location coordinates in map. Any known workaround for this?
I solved it in a dirty way by converting coordinate to CGPoint and then calculating bearing between Points.
let destinationPoint = mapView.convert(destination, toPointTo: nil)
let sourcePoint = mapView.convert(source, toPointTo: nil)
let bearing = atan2(sourcePoint.y - destinationPoint.y, sourcePoint.x - destinationPoint.x) - .pi
Caution: This calculation will go wrong when map is rotated

Swift mkmapview get zoom level, width or radius of visible map area from latitude longitude delta

I am in a confusion, on how to get a zoom level and radius of a visible area of the map (using mapkit mapview).
Here is what I am looking for (either of them or both, as needed) -
Zoom level, is to see at what level of the map is being shown to the users and with that information, I want to display the custom pins in the visible area. If zoom level is high, I need to show the actual logos of some commerce stores as pins. If zoom level is low, I need to show colored dots instead of logos.
As of now, I am using let updatedRadius = (mapView.camera.altitude)/1000 to get altitude of the camera, and if the updatedRadius value is > 25.0, I am showing colored dots. Below 25.0, I show the logos. I am doing this in regionDidChanged
Is this approach correct?
Radius, is to send it as a parameter to my REST API to fetch the list of places within that radius. When user zooms out on the map, visible area increases and so the REST API needs bigger radius to return the places covered in that area.
Ultimately, what should happen is, whenever user zooms out, then the radius changes. I need to send this changed radius to my REST to get an updated list.
What are latitude longtitude deltas, can we get radius/width of visible area using these values?
let latitudeDeltaVal = mapView.region.span.latitudeDelta
let longitudeDeltaVal = mapView.region.span.longitudeDelta
Can someone throw some light on what needs to be done please?
Since you need to call the api when the region changes you need to calculate the radius in mapView's delegate function, RegionDidChange.
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let centralLocation = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
self.centralLocationCoordinate = mapView.centerCoordinate
print("Radius - \(self.getRadius(centralLocation))")
}
func getRadius(centralLocation: CLLocation) -> Double{
let topCentralLat:Double = centralLocation.coordinate.latitude - mapView.region.span.latitudeDelta/2
let topCentralLocation = CLLocation(latitude: topCentralLat, longitude: centralLocation.coordinate.longitude)
let radius = centralLocation.distanceFromLocation(topCentralLocation)
return radius / 1000.0 // to convert radius to meters
}
To account for both landscape and portrait orientations, and/or situations where the map orientation is close to Northeast, Northwest, Southwest, Southeast, and to enclose the full screen up to the corners, one should consider both latitudeDelta and longitudeDelta:
func getRadius() -> Double{
let centralLocation = CLLocation(latitude: mapView.region.center.latitude, longitude: mapView.region.center.longitude)
let cornerOfMap = CLLocation(latitude: centralLocation.coordinate.latitude + mapView.region.span.latitudeDelta , longitude: centralLocation.coordinate.longitude + mapView.region.span.longitudeDelta)
let radius = centralLocation.distance(from: cornerOfMap)
return radius / 1000.0 // to convert radius to meters
}

Limit map visibility to its overlay

I want to place an image over my map and limit the map visibility to the image overlay, so that the user can only see my image and not the map behind it. I tried to used the solution offered by Google Maps API with the GMSGroundOverlay, like so:
let southWest = CLLocationCoordinate2D(latitude: 40.712216, longitude: 32.22655)
let northEast = CLLocationCoordinate2D(latitude: 40.773941, longitude: 23.12544)
let overlayBounds = GMSCoordinateBounds(coordinate: southWest, coordinate: northEast)
let icon = UIImage(named: “someImg”)
let overlay = GMSGroundOverlay(bounds: overlayBounds, icon: icon)
overlay.bearing = 0
overlay.map = mapView
Also, I set the cameraTarget of the map to the bounding box:
mapView.cameraTargetBounds = overlayBounds
This does limit the user from scrolling the map infinitely, but I expected these limits to go as far as the overlay goes. Now the image looks like a sticker on the map, as I can still see the map around the image.
I am not sure I fully understand the way GMSCoordinateBounds and GMSGroundOverlay work..so I'm asking here if anyone can give me some hints on the adjustments that I can make to get what I want.

Tilt map in Mapkit programmatically using Swift

I'd like to tilt the map at startup. (Same as the user does when scrolling up or down with 2 fingers)
Is this possible using Swift?
MKMapView Class Reference : http://goo.gl/djHXPn
Look at the camera property :
A camera object defines a point above the map’s surface from which to view the map. Applying a camera to a map has the effect of giving the map a 3D-like appearance. You can use a camera to rotate the map so that it is oriented to match the user’s heading or to apply a pitch angle to tilt the plane of the map.
Assigning a new camera to this property updates the map immediately and without animating the change. If you want to animate changes in camera position, use the setCamera:animated: method instead.
You must not set this property to nil. To restore the map to a flat appearance, apply a camera with a pitch angle of 0, which yields a camera looking straight down onto the map surface.
Try to create and set a camera (animated or not).
Edit :
I tried myself. Here is an example of how to use it :
let userCoordinate = CLLocationCoordinate2D(latitude: 58.592725, longitude: 16.185962)
let eyeCoordinate = CLLocationCoordinate2D(latitude: 58.571647, longitude: 16.234660)
let mapCamera = MKMapCamera(lookingAtCenterCoordinate: userCoordinate, fromEyeCoordinate: eyeCoordinate, eyeAltitude: 400.0)
let annotation = MKPointAnnotation()
annotation.setCoordinate(userCoordinate)
mapView.addAnnotation(annotation)
mapView.setCamera(mapCamera, animated: true)
You'll have to find your right eyeCoordinate depending on your location and tilt effect you want to have.
Swift 4
This is an easier way: you can set distance, pitch and heading:
let mapCamera = MKMapCamera(lookingAtCenter: userCoordinate, fromDistance: 10000, pitch: 65, heading: 0)
map.setCamera(mapCamera, animated: true)

Resources