Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

Rotating map according to device heading

Hi there,

Does anyone know's if there is an function like in the native maps app, that let's you rotate the map according the heading from the compass? And is it for android and iPhone?

— asked March 23rd 2010 by Michiel Neelen
  • compass
  • maps
  • mobile
0 Comments

6 Answers

  • Sure thing. Try this:

    Ti.Geolocation.addEventListener('heading', function(e)
    {
        if (e.success)
        {
    
            var t = Ti.UI.create2DMatrix();
                t = t.rotate(360-e.heading.magneticHeading);
    
            map.transform = t;
    
        }
    });
    
    — answered August 9th 2010 by jordi martinez
    permalink
    0 Comments
  • This helped me, but when I change the map orientation, it doesn't change the controls orientation; so, the map changes fine but the controls are disordered. Is there any solution?

    — answered March 19th 2012 by Alvaro López
    permalink
    1 Comment
    • The rotation essentially rotates the mapview, and not the map itself. You would result in the primitive map controls rotating together with the mapview. One of the workarounds is to create custom controls (i.e. buttons) independent of the mapview, and have it trigger methods for the map.

      — commented January 5th 2013 by Kenneth Wang
  • Anyone?

    — answered March 31st 2010 by Michiel Neelen
    permalink
    0 Comments
  • The heading can be jerky if the device's orientation is changing quickly. To smooth out the readings, you can
    use a lowpass filter or this handy compassSmoother

    //Smoothe MagneticHeading
    function createCompassSmoother(smoothingFactor) {
        var compassSmoother = {};
        var headingVector = [];
    
        compassSmoother.smooth = function(newHeading) {
            var newHeadingVector = [ Math.cos(newHeading * Math.PI / 180), Math.sin(newHeading * Math.PI / 180)];
    
            if (headingVector.length == 0) {
                headingVector = newHeadingVector;
            } else {
                headingVector[0] = headingVector[0] * (1-smoothingFactor) + smoothingFactor * newHeadingVector[0];      
                headingVector[1] = headingVector[1] * (1-smoothingFactor) + smoothingFactor * newHeadingVector[1];
            }
            return ((Math.atan2(headingVector[1], headingVector[0]) * 180/Math.PI) + 360) % 360;
        }
        return compassSmoother;
    }
    
    var compassSmoother = createCompassSmoother(0.22);
    
    var t = Ti.UI.create2DMatrix();
    t = t.rotate(bearing - compassSmoother.smooth(e.heading.magneticHeading));
    

    SmoothingFactor should be between (0 and 1]

    By choosing an appropriate smoothingFactor you can determine how quickly the compass settles in on a reading. Hi factors settle quickly, but jerky, low factors slowly and smoother

    — answered January 25th 2014 by Mark Riggins
    permalink
    4 Comments
    • Very interesting. What is the value of bearing?

      — commented January 26th 2014 by Danny Pham
    • Bearing is the direction that you want to point. For example, if you want the compass to point North, then bearing would be 0.

      In my app, we calculate the bearing to the direction of intended travel. The code above would cause the compass to always point to your destination if you use the Geolocation service to update bearing periodically.

      — commented January 26th 2014 by Mark Riggins
    • The compassSmoother above overcomes a common bug – when smoothing a reading that is nearly North, the magnetic heading will JUMP between readings as it crosses the threshold of 360. 0.5 and 359.5 etc. The AVERAGE would be 180 which is EXACTLY wrong !!. The average should be either.

      That's why sometimes you'll see the pointer suddenly point in the opposite direction if you try to use a linear smoothing function on compass readings

      — commented January 26th 2014 by Mark Riggins
    • I tried this and didn't notice any change in the jerky movement.. I am using it for a compass needle and rotating an image.

      exports.showCompass = function(view){
          var imgCompass = Ti.UI.createImageView({
              image: 'images/needle.png'
          }); 
          if (Titanium.Geolocation.hasCompass)
          {
              Titanium.Geolocation.showCalibration = false;
      
              Ti.Geolocation.getCurrentHeading(function(e)
              {
                  if (e.error)
                  {
                      alert('error: ' + e.error);
                      return;
                  }
      
                  var x = e.heading.x;
                  var y = e.heading.y;
                  var z = e.heading.z;
                  var magneticHeading = e.heading.magneticHeading;
                  var accuracy = e.heading.accuracy;
                  var trueHeading = e.heading.trueHeading;
                  var timestamp = e.heading.timestamp;
      
                  var t = Ti.UI.create2DMatrix();
                  t = t.rotate(360-magneticHeading);
      
                  imgCompass.transform = t;
              });
      
              Titanium.Geolocation.addEventListener('heading',function(e)
              {
                  if (e.error)
                  {
                      alert('error: ' + e.error);
                      return;
                  }
      
                  var x = e.heading.x;
                  var y = e.heading.y;
                  var z = e.heading.z;
                  var magneticHeading = e.heading.magneticHeading;
                  var accuracy = e.heading.accuracy;
                  var trueHeading = e.heading.trueHeading;
                  var timestamp = e.heading.timestamp;
      
                  var t = Ti.UI.create2DMatrix();
      
                  var spin = Titanium.UI.createAnimation();
      
                  //Smoothe MagneticHeading
                  function createCompassSmoother(smoothingFactor) {
                      var compassSmoother = {};
                      var headingVector = [];
      
                      compassSmoother.smooth = function(newHeading) {
                          var newHeadingVector = [ Math.cos(newHeading * Math.PI / 180), Math.sin(newHeading * Math.PI / 180)];
      
                          if (headingVector.length == 0) {
                              headingVector = newHeadingVector;
                          } else {
                              headingVector[0] = headingVector[0] * (1-smoothingFactor) + smoothingFactor * newHeadingVector[0];      
                              headingVector[1] = headingVector[1] * (1-smoothingFactor) + smoothingFactor * newHeadingVector[1];
                          };
                          return ((Math.atan2(headingVector[1], headingVector[0]) * 180/Math.PI) + 360) % 360;
                      };
                      return compassSmoother;
                  }
                  var compassSmoother = createCompassSmoother(0.55);
      
                  t = t.rotate(0 - compassSmoother.smooth(magneticHeading));
                 imgCompass.transform = t;
              });
          }
          else
          {
              alert("No Compass on device");
          }
          view.add(imgCompass);
      };
      

      — commented March 3rd 2014 by Dennis Megarry
  • The readings are not frequent enough or accurate enough to smoothly animate. But at least is doesn't do the 180 degree flip flop as it crosses from 359 to 0. I think we might be able to smooth it further by reading the accelerometer.

    — answered March 3rd 2014 by Mark Riggins
    permalink
    0 Comments
  • We probably need to use an animation instead of just the transform.

    — answered March 3rd 2014 by Mark Riggins
    permalink
    0 Comments
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.