17

I want to rotate GMap by changing the bearing angle value, so the camera rotates around the center point (360-Degree one full round ). When we change the bearing, there is a easing effect at camera start and end points. How can I control/change that in order to make the rotation smooth when change Bearing values (in order to rotate map in 360 Degree, smooth animation)?

Required this for all languages as it appears the easing effect is different in different language libraries. e.g. Swift, Android, PHP, JS, Node.js, React.

Swift Example (running OK in Linear Animation):

Note that initially the animation did had jerks in iOS as well, but when we make use of CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear along its CATransaction properties then the GMap animation turned into smooth animation. so now if you see the code below, the change in Bearing value does not create jerky effect (due to the easing effect in GMap animation). I am looking for appropriate solution for Android and Web as well.

//Move the map around current location, first loop
let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
CATransaction.begin()
CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
CATransaction.setAnimationTimingFunction(timingFunction)
CATransaction.setCompletionBlock({
    //Move the map around current location, second loop
    let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    CATransaction.begin()
    CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
    CATransaction.setAnimationTimingFunction(timingFunction)
    CATransaction.setCompletionBlock({
        //Move the map around current location, third loop
        let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        CATransaction.begin()
        CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
        CATransaction.setAnimationTimingFunction(timingFunction)
        CATransaction.setCompletionBlock({
            UIView.animate(withDuration: 0.5, animations: {
                self.findingYourLocation.alpha = 0.0
            })
            //TODO: Set nearest branch
            // Zoom in one zoom level
            let zoomCamera = GMSCameraUpdate.zoomIn()
            self.mapView.animate(with: zoomCamera)

            // Center the camera on UBL Branch when animation finished
            //let nearestBranch = CLLocationCoordinate2D(latitude: 24.850751, longitude: 67.016589)
            let nearestBranch = CLLocationCoordinate2D.init(latitude: 24.806849, longitude: 67.038734)
            let nearestBranchCam = GMSCameraUpdate.setTarget(nearestBranch)



            CATransaction.begin()

            let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
            CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
            CATransaction.setAnimationTimingFunction(timingFunction)
            CATransaction.setCompletionBlock({
                self.nextButton.alpha = 1.0
            })
            self.mapView.animate(with: nearestBranchCam)
            self.mapView.animate(toZoom: 15)
            self.mapView.animate(toBearing: 0)
            self.mapView.animate(toViewingAngle: 0)

            CATransaction.commit()

        })
        self.mapView.animate(toBearing: self.mapView.camera.bearing + 120)
        CATransaction.commit()

    })
    self.mapView.animate(toBearing: self.mapView.camera.bearing + 120)
    CATransaction.commit()

})
self.mapView.animate(toBearing: self.mapView.camera.bearing + 120)
CATransaction.commit()

The Android example code (has problem):

The Android example/sample code can be found here: https://issuetracker.google.com/issues/71738889

Which also includes an .apk file, an .mp4 video of sample app output. Which clearly shows jerky effects when Bearing value changes while rotating the map in 360-Degree.

halfer
  • 19,824
  • 17
  • 99
  • 186
Nah
  • 1,690
  • 2
  • 26
  • 46
  • From a bit of googling you might want to set your heading to change the map rotation; `map.setHeading( + )`. Let me know if this works! – JillevdW Apr 03 '18 at 12:10
  • thanks for reply, I want to rotate this map view: https://developers.google.com/maps/documentation/android-api/views#3d_buildings_on_the_map and not the imagery. Will appreciate if you prepare a demo example. – Nah Apr 03 '18 at 12:27
  • Thanks for showing all your upvotes, to this issue. Google has finally taken this issue into their consideration and they will add this feature, if you hit *star* icon in order to show your interest: https://issuetracker.google.com/u/1/issues/71738889 – Nah May 18 '18 at 10:48
  • Finally, the Google team had recently released support for above feature. Check above issue tracker link for solution details on the issue. – Nah May 25 '21 at 08:32

4 Answers4

6

Giving this as an answer as a comment would be rather hard to read; this is taken from the google documentation.

Consider this code:

CameraPosition cameraPosition = new CameraPosition.Builder()
    .target(MOUNTAIN_VIEW)      // Sets the center of the map to Mountain View
    .zoom(17)                   // Sets the zoom
    .bearing(90)                // Sets the orientation of the camera to east
    .tilt(30)                   // Sets the tilt of the camera to 30 degrees
    .build();                   // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

This code creates a new camera position, and that's exactly what you're trying to mutate: the bearing of the camera. So if you create a new camera position like this:

CameraPosition cameraPosition = new CameraPosition.Builder()
    .bearing(50)
    .build();

and then animate the camera to that position:

map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

That should do the trick. To give some more information on what you will need to use as bearing:

a bearing of 90 degrees results in a map where the upwards direction points due east.

Good luck.

JillevdW
  • 1,087
  • 1
  • 10
  • 21
  • Appreciate your effort, but if you run it, it will not be a smooth linear animation. There will be jerks due to the `easing` effect in start and at end of animation. When you change `Bearing` value to further rotate (in order to complete 360Deg cycle) you will see number of jerks. That is main issue. – Nah Apr 03 '18 at 12:44
  • @MuhammadHannan if you have a problem with the way the Map's `animateCamera` function works you might want to try to override it, but I think you're diving into a very deep rabbit hole. Good luck! – JillevdW Apr 03 '18 at 12:47
  • You might be right, but I want to remove that jerk that appears during the map animation (when we change Bearing value), which is probably the easing effect in the animation. I have managed to remove those in `iOS` app by applying / enforcing linear animation but could not control in `JS` and `Android`. – Nah Apr 04 '18 at 03:43
  • @MuhammadHannan can you send me links/data about how you enforced linear animation? I will then take a look at the `JS` and `Android` side of things. – JillevdW Apr 05 '18 at 09:08
  • I have added `swift` example in my question. I can send you video of `iOS` sample animation if require, as a working proof if required (but not sure how to share the 6MB video) – Nah Apr 05 '18 at 10:18
  • @MuhammadHannan so if I understand correctly, you're using the native Animation API to divide the animation into three equal animations with linear interpolation. On Android I usually use [AnimatorSet](https://developer.android.com/reference/android/animation/AnimatorSet.html) to deal with animations, you might be able to replicate the desired behaviour with that. – JillevdW Apr 05 '18 at 10:52
  • I have not used `AnimatorSet`, so will be wonderful if you could provide its usage in my particular case. The above example shows only rotate animation (which should be smooth), and after 2,3 or 5 rotations we take camera back to 90Degree bird eye view, so we need to manage that part as well along (I have not provided that part of code in above example), but the objective is same, to have a smooth animation while camera/Bearing change. – Nah Apr 05 '18 at 11:19
  • still waiting for example. – Nah Apr 10 '18 at 04:18
  • @MuhammadHannan I can't go very in depth, but what you could try it so create an `ObjectAnimator` objects, say `ObjectAnimator.ofFloat(_,_,_)`, and when you've created all the desired animations you can add them to an `AnimatorSet` object, with `animatorSetObj.play(objectAnimatorObj)` or if you have multiple `animatorSetObj.playTogether(objectAnimatorObj1, objectAnimatorObj2)`. – JillevdW Apr 10 '18 at 07:52
  • i have a problem, what i want is to keep direction of location point towards north all time if location changes towards left then it should align map towards north like in google maps driving mode,,,https://stackoverflow.com/questions/55699977/how-to-keep-map-orientation-fixed-towards-north-in-android/55701370?noredirect=1#comment98088368_55701370 – Wahdat Jan Apr 16 '19 at 14:40
2

I'm going to write this as another answer as I'd like to take the time to write a wall of text, whereas I'd like to keep the other answer as short as possible since it might still help other people with a similar problem.

The problem

So if I understand correctly, what you're trying to do is build an application with Google maps for different platforms. You're running into an issue with Google maps (the jerky movement) and you're trying to find a fix for all the platforms.

My proposed solutions

I'll divide this into a few sections, because I see different ways to go forward.

  • Find a solution for all the platforms.

This one seems like the most straightforward, but it could be akin to the XY problem. I've tried to introduce you to some ways of animating views, and you've solved the problem in your iOS app, but at the core what you're dealing with is a flaw in the Google maps animation when changing the bearing. I am not sure if there is a way to tackle this problem on every platform, as I haven't tried.

  • Use a different map

This sounds like a big step, and depending on your usage something you don't want to do. However, I've successfully used Leaflet (a JS map) with a WKWebView on iOS and Android and that all worked pretty well (this obviously also works fine in the browser). Keep in mind that some other maps might also have the jerky animation.

Wrapping it up

I hope I've given some insight. I'll add to this answer as we find out new things about the problem. Could you try to provide a minimal reproducible example? That way I can give it a better try. Good luck!

JillevdW
  • 1,087
  • 1
  • 10
  • 21
  • I have provided the minimal level example code for android in a link in my question, you can play on that. – Nah Apr 16 '18 at 20:27
  • @MuhammadHannan I am currently working in your provided Android project to try and find a solution. – JillevdW Apr 19 '18 at 07:30
  • Will love to see the solution. – Nah Apr 19 '18 at 07:34
  • @MuhammadHannan I'm sorry to say that as of now, I have not been able to find a solution. I'm looking for a proper equivalent to CATransaction so I can imitate the behaviour displayed in your iOS example. I'll continue my search. – JillevdW Apr 20 '18 at 07:55
0

After going through all possible cases, I came to know that GMap is not built with required feature of Rotating map in 360-Degree in one go with custom animation. Don't know if this appears in next GMap api version.

For now, the possible solution is to change the animation logic, and instead of rotating 360-Degree we can rotate to e.g. 180-Degree with reduced animation speed (animation duration time).

This allows us to bring required map surrounding search experience to the user.

(For now I am posting this temporary answer, and allow others to update or post latest answer in future).


I have submitted this animation control issue on GMap issue tracker, please START this issue in order to show your interest and feedback, so Google team can take this feature into their final consideration. https://issuetracker.google.com/u/1/issues/71738889

halfer
  • 19,824
  • 17
  • 99
  • 186
Nah
  • 1,690
  • 2
  • 26
  • 46
  • Folks, after submitting the issue/feature request to google they are now supporting this feature. Details can be found in the above-given link. – Nah Feb 17 '22 at 09:24
0

Here is my approach:

  • tilt = 45f
  • zoom = 18
  • target = currentLocation
  • bearing = lastKnownLocation.bearingTo(currentLocation)
  1. Use map.animateCamera(); // instead of move camera
CameraPosition cameraPosition;
        if (currentLocation.distanceTo(lastKnownLocation) > 50) {
            cameraPosition = new CameraPosition.Builder()
                    .target(currentLatLng).zoom(20).tilt(45f).bearing(bearingValue).build();
        } else {
            cameraPosition = new CameraPosition.Builder()
                    .target(currentLatLng).zoom(20).tilt(45f).build();
        }

        map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

What else you have to do? You must call this method from LocationCallback (where you are getting location updates) and then we can have a smooth experience.

So if the user moved (walked or driving) and the distance between lastKnownLocation and currentLocation is greater than 50 meters then only we will set bearing otherwise we keep changing the targetLocation only.

In this way, we can show heading direction like in google maps.

Vijay
  • 1,163
  • 8
  • 22