Titanium Community Questions & Answer Archive

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

Titanium.Geolocation.getCurrentPosition won't work within a function

I must be misunderstanding something fundamental. Calling Titanium.Geolocation.getCurrentPosition with code right on a page works. But, putting the same code in a function/event handler fails.

Sample code on pastie http://pastie.org/1113838

The first chunk of code outputs the location data as expected. The second block attaches the same code to a button. When I click the button and I get 'undefined' values.

So, what am I doing wrong? Or, what am I misunderstanding?

My reason for putting the code in a handler (goal is actually a function called by the handler) is so that I determine the location & take action only when someone clicks the button. I figure that's nicer to battery life in case they leave the app open while walking around – the phone won't be constantly determining its location.

— asked August 24th 2010 by Tim Poulsen
  • android
  • gps
  • location
0 Comments

6 Answers

  • I believe the getCurrentPosition function it's async, so you will need to put the logging function inside it. not outside.

      else {
                if (e.coords.accuracy <= 500) {
                   mylongitude = e.coords.longitude;
                   mylatitude = e.coords.latitude;
                   Titanium.Geolocation.reverseGeocoder(mylatitude, mylongitude, function(evt){
                      var places = evt.places;
                      entry_address = places[0].address;
                   });
    
     Titanium.API.info('Event handler: ' + mylongitude + ' (long) ' + mylatitude + ' (lat) ')
    
    
                }
             }
    
          });
    
    — answered August 25th 2010 by Dan Tamas
    permalink
    3 Comments
    • Tamas, you're correct. Making that change let's me put the getCurrentPosition into the click handler. But, I still can't get it to work in a separate function (so a click handler calls a common geocoding function and processes the results). It must be something to do with passing by reference that I'm not understanding. I've tried various ways to assign the results to a variable in the function, then return that variable. The values are defined within my function, but the return value is always empty. I'll have to dig deeper to refresh my understanding of javascript variables. – Thanks, Tim

      — commented August 25th 2010 by Tim Poulsen
    • BTW, in cleaning up my code to put it on pastie I introduced at least one error, a missing }

      — commented August 25th 2010 by Tim Poulsen
    • If you can encapsulate the specific handling needed for each place you put a "Location" button in a local function, you could pass that function (handle) to the reverseGeocoder function so it could call once it has the result.

      — commented August 27th 2010 by Richard Baughman
  • Can you post your part of the code that is not working? May be some variables scope issue

    — answered August 27th 2010 by Dan Tamas
    permalink
    2 Comments
    • I've since rewritten the code. So, below is my attempt to re-create what I had. I also put it on pastie at http://pastie.org/1120594 in case formatting gets messed up.

      The starting block is the location code within the button's event listener. It's working. I'd like to have it in a function, so that I can call the same code from more than one button without having to duplicate & maintain separate blocks of code.

      In a function, the return value is always undefined if the location detection works. I think it's something to do with the way Javascript references complex data times. I tried casting the values to strings using String() but that didn't help.

      ~~~
      myButton.addEventListener('click', function() {
      // This code works within the button's event listener
      Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST;
      Titanium.Geolocation.distanceFilter = 10;
      Ti.Geolocation.purpose = "Populate Address Fields Based on Your Location";
      if (Titanium.Geolocation.locationServicesEnabled == false) {
      Titanium.UI.createAlertDialog({
      title: 'GPS Error',
      message: 'Your GPS is turned off. Please switch it on.'
      }).show();
      openSaleWindow('','','');
      } else {
      Titanium.Geolocation.getCurrentPosition(function(e){
      if (e.error) {
      alert(e.error);
      openSaleWindow('','',''); // error, so call with no values
      } else {
      if (e.coords.accuracy <= 500) {
      Titanium.API.info('Event handler: ' + e.coords.longitude + ' (long) ' + e.coords.latitude + ' (lat) ');
      Titanium.Geolocation.reverseGeocoder( e.coords.latitude, e.coords.longitude, function(evt){
      var places = evt.places;
      Titanium.API.info('Address = ' + places[0].address);
      openSaleWindow(e.coords.latitude, e.coords.longitude, places[0].address);
      }); // end reverseGeocoder
      } else {
      openSaleWindow('','',''); // poor accuracy so call with no values
      }
      } // end if/else
      }); // end getCurrentPosition
      } // end locationServicesEnabled
      });
      win.add(myButton);

      function getMyLocation() {
      // Essentially the same code, put into a function fails
      Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST;
      Titanium.Geolocation.distanceFilter = 10;
      Ti.Geolocation.purpose = "Populate Address Fields Based on Your Location";
      if (Titanium.Geolocation.locationServicesEnabled == false) {
      Titanium.UI.createAlertDialog({
      title: 'GPS Error',
      message: 'Your GPS is turned off. Please switch it on.'
      }).show();
      return 'gpsdisabled';
      } else {
      Titanium.Geolocation.getCurrentPosition(function(e){
      if (e.error) {
      alert(e.error);
      return 'gpserror';
      } else {
      if (e.coords.accuracy <= 500) {
      Titanium.API.info('Event handler: ' + e.coords.longitude + ' (long) ' + e.coords.latitude + ' (lat) ');
      Titanium.Geolocation.reverseGeocoder( e.coords.latitude, e.coords.longitude, function(evt){
      var places = evt.places;
      Titanium.API.info('Address = ' + places[0].address);
      var theLocation = new Array();
      theLocation['latitude'] = e.coords.latitude;
      theLocation['longitude'] = e.coords.longitude;
      theLocation['address'] = e.coords.places[0].address;
      return theLocation;
      }); // end reverseGeocoder
      } else {
      return 'gpsnolocation'; // poor accuracy
      }
      } // end if/else
      }); // end getCurrentPosition
      } // end locationServicesEnabled
      } // end getMyLocation()

      var retValue = getMyLocation();
      alert(typeof(retValue)); // if GPS is on and working, this says 'undefined'

      — commented August 27th 2010 by Tim Poulsen
    • Oops, I guess you can't do code in a comment. And I can't edit! Good thing I put it on pastie http://pastie.org/1120594

      — commented August 27th 2010 by Tim Poulsen
  • Ok
    I did not test it, but what I saw at a first glance is that you are trying to return the values on an async function ( which doesn't work ).

    You are doing something like this

    var retValue = getMyLocation()
    

    What really happens is this:

    the getMyLocation() function starts, initiate the Titanium.Geolocation.getCurrentPosition() function async then exists.
    the Titanium.Geolocation.getCurrentPosition function starts the gps, do it's stuf and finishes a lot later than your function closes. That means to be async.
    You will have to call all your location dependent function like the

    alert(typeof(retValue));
    

    when Titanium.Geolocation.getCurrentPosition finishes, so instead of

     return theLocation;
    

    and of course to handle the error case too.

    Actually you are doing it very well in the button event where you triggering the openSaleWindow.

    Now if you cannot wait you could "fool" a little the user, starting the geolocation exactly whan the app opens and cache it, being async you can go on with the rest of the interface, and when u have the location data use openSaleWindow

    If you need more time, use a activity indicator.

    Let me know if it makes sense.

    — answered August 27th 2010 by Dan Tamas
    permalink
    1 Comment
    • Thanks Daniel. I need to brush up on my understanding of async functions!

      My reason to put the getlocation stuff in a function is two-fold:

      (1) so that I can reuse the same code from multiple places in my app. I want a couple of buttons to trigger the location detection. I don't want to duplicate the code, which would give me more code to debug, maintain, update, etc.

      (2) I figure the user will leave the app open, move from location to location, and click the buttons. I'm thinking it would be nicest for battery life to not setup an event listener to constantly track their location (triggered in the app.js for example).

      So, maybe there's another way to achieve my goals that doesn't involve putting the code into a function like I was trying. Maybe there's not the battery life hit that I'm thinking in having the event listener going.

      I really appreciate your help and clarifications. Thank you very much!

      Tim

      — commented August 27th 2010 by Tim Poulsen
  • Well you can do this for sure. But I think it's against the user's expectations to need to refresh the location by himself.
    If you want to do this(get the location by request ),the best way is to let the user his location is updated with an activity indicator, that it's hidden when the function() the async one ends and you open or refresh the saleswindow.

    — answered August 27th 2010 by Dan Tamas
    permalink
    0 Comments
  • Hi Tim,

    I am experiencing the same issue. Did you get to a solution here?

    João

    — answered October 25th 2010 by Joao Silva
    permalink
    0 Comments
  • duplicate

    — answered August 27th 2010 by Dan Tamas
    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.