Mapview on Android

I am testing the new Mapview for Android and have ran into some difficulty. The following code displays the map and the user’s current location correctly on IOS devices. The print statements verify that the correct lat/long is being used. However, if I run this same code on an Android device, the map displays with the user’s current location correctly but the print statements show lat/lnog as “nil” and I get a Lua runtime error that says “Function mapView:setCenter() was given an invalid ‘latitude’ argument. Was expecting a number.” Which I assume is because lat/long is nil. The permissions in the build.settings are correct with the ACCESS_FINE_LOCATION entry. Any ideas on why this runs correctly on IOS but not Android? All ideas are greatly appreciated!

Thanks,
Scott

[code]
– Load external Lua libraries
local ui = require( “ui” )

– define variables
local currentLocation, currentLatitude, currentLongitude

– create map
myMap = native.newMapView( 20, 20, 300, 220 )
myMap.mapType = “standard”
myMap.x = display.contentWidth / 2
myMap.y = 120

– get user’s current location
local function callMap()
currentLocation = myMap:getUserLocation()
currentLatitude = currentLocation.latitude
currentLongitude = currentLocation.longitude
print ("currentLatitude = ", currentLatitude)
print ("currentLongitude = ", currentLongitude)

– Move map so that current location is at the center
myMap:setCenter( currentLatitude, currentLongitude, true )
end

timer.performWithDelay(1000, callMap)
[/code] [import]uid: 85963 topic_id: 31115 reply_id: 331115[/import]

This means that Android has not got a fix on your current location yet. That is why it is returning nil values. The device’s current location is unknown. You have to wait longer for the device to get a fix via GPS, WiFi, or cellular service. Have a look at sample app “Interface\MapView” that is included with the Corona SDK on how to check for errors. We’ve updated this sample app to properly handle error conditions for both iOS and Android.

Note that your app will crash on iOS if the current location is unknown too… or if the end-user did not give your app permission to retrieve current location.

Also, on Android, it will automatically center to your current location if you’ve never called [lua]setCenter()[/lua] or [lua]setRegion()[/lua]. It will do so as soon as your current location is known. So, I recommend that you comment out your timer and wait. [import]uid: 32256 topic_id: 31115 reply_id: 124450[/import]

Joshua, thanks for the “Quick” reply! I took a look at the sample code particularly the button2Release function where it fetches the user’s current location. That code appears to give the user an error message (showAlert) if the location is unknown. Instead of giving an error how would I wait until the device has a fix on the location? I have tried several variations of “do while” loops and timer delays but can’t quite get it. I know it is something simple that I am just missing. Thanks again! Scott
[import]uid: 85963 topic_id: 31115 reply_id: 124543[/import]

Greetings mobile36.

I’ve got a test in my code that seems to work well:

if( tonumber(event.accuracy) < 101 ) then – Wait for good accuracy – 100 meters or less
– do your stuff, GPS is accurate enough…
end

The event is of course a location event… Seems to work for me on both iOS and Droid… My current Android phone takes a while to get a good fix (indoors), but if I move into a clear area, it seems to get a fix much quicker.

Good Luck! [import]uid: 79933 topic_id: 31115 reply_id: 124565[/import]

Thanks mpappas, I understand your logic but if the event.accuracy is >= 101 how do you wait and keep forcing a read from the GPS until it is < 101? I’m having trouble with the “wait” part. Thanks!
[import]uid: 85963 topic_id: 31115 reply_id: 124602[/import]

There’s not much we can do about the wait part, except tell the user we’re waiting for the device.

The iPhones I’ve tested on seem to have superior hardware to the androids… They seem to be able to get GPS coords right quick, no matter how deep I’m inside a building.

My current Android phone… I have to move near a window before it gets it…

Not sure why – I know there’s a few ways the iPhone can try to get the GPS coords— either through the actual GPS hardware, or through the cell network, or even through your wifi (iPods don’t have GPS hardware, and do it through a complex wifi process).

My guess is that android only uses the GPS hardware, which needs to be able to receive a signal from 3(?) satellites to determine it’s position… I don’t believe there’s a technical solution to this currently.

EDIT: To get regular GPS updates, as the hardware gets more accurate, and the user moves around, set the event handler for location to have your app updated periodically:

Runtime:addEventListener( “location”, locationHandler ) [import]uid: 79933 topic_id: 31115 reply_id: 124604[/import]

I have the location event listener setup and it is getting updates. My accuracy is in the 2000’s for awhile and eventually, in 10 seconds or so, it will get down to under 50.

What I don’t understand is how to code this so my app waits until the accuracy is under say 101 before it moves on. I have tried variations of the following with no luck.

[code]
– setup map
myMap = native.newMapView( 20, 20, 300, 220 )
myMap.mapType = “standard”
myMap.x = display.contentWidth / 2
myMap.y = 120
currentLocation = myMap:getUserLocation()

– function to get user’s current location
local locationHandler = function( event )
while event.accuracy > 100 or event.latitude == nil do
– my intent with the next line is to keep forcing updates but I’m not
– sure it is really doing as I intended
currentLocation = myMap:getUserLocation()
end
currentLatitude = currentLocation.latitude
currentLongitude = currentLocation.longitude

– Move map so that current location is at the center
myMap:setCenter( currentLatitude, currentLongitude )
end

Runtime:addEventListener(“location”, locationHandler)
[/code] [import]uid: 85963 topic_id: 31115 reply_id: 124616[/import]

I’d suggest you remove the while loop from your location handler… There’s a couple reasons why, but I digress…

Instead, the quick and dirty way would be to put your location handler in a separate lua file, and make a couple global variables (initialized to nil) in there (such as gps.long, gps.lat, gps.acc - or just save a copy of the last event struct if you want it all) …

Your main code can just keep checking them until not nil / accuracy is less than 100 or whatever your threshold is.

While it’s checking them and waiting, it could show a nice display of the current accuracy or something…

EDIT: You can use the runtime enterFrame event in your main app to regularly check on the gps accuracy, show messages, and/or call functions once the threshold has been triggered. [import]uid: 79933 topic_id: 31115 reply_id: 124622[/import]

Calling [lua]myMap:getUserLocation()[/lua] right after you created your map is *never* going to work on Android. You have to wait for the Android device’s GPS to startup and then get a fix on your location. And how long it will take will vary between different Android devices since they use different GPS sensors. This is just how it is and you need to write your code handle the case where the current location is unknown… because that same issue *can* happen on an iOS too, such as when the user puts the device into Airplane mode.

So, if you want to set up your code to fetch the user’s current location only once, then set up your timer to occur at regular intervals (not just a 1 shot timer) that continually calls the [lua]myMap:getUserLocation()[/lua] function. Once it receives the user’s current location, disable the timer.

Also, I highly recommend that you add the following 2 permission to your “build.settings” file…
[lua]settings =
{
androidPermissions =
{
“android.permission.ACCESS_FINE_LOCATION”,
“android.permission.ACCESS_COARSE_LOCATION”,
},
}[/lua]

The ACCESS_FINE_LOCATION gives your app permission to use the GPS. This provides the most precise current location measurements, but it takes time to get an initial fix on your current location. It also requires line-of-sight with the GPS satellites and generally does not work that well indoors, depending on how good the GPS sensor is.

However, the ACCESS_COARSE_LOCATION gives your app permission to retrieve your current location via WiFi and your cellular service. This is the fastest way to retrieve your current location, but it lacks precision.

Now, if you have both permission defined, then you’ll find that your app will first retrieve your current location via WiFi/cellular-service first and fairly quickly. Once the Android device gets a fix on the GPS satellites, it will provide GPS data instead. So, I highly recommend that you set both permission for the fast response time.

I hope this helps! [import]uid: 32256 topic_id: 31115 reply_id: 124623[/import]

One more thing. You should check out our updated map view documentation available on our daily build web page. That documentation has been heavily updated, provides more examples, and indicates what happens in the failure cases. It documents everything that I’ve mentioned above and should be a big help for you.

Please note that this documentation update is currently only available on our daily build page. The API documentation that you see on our website is only for our release build (#840) and we only update it per release. [import]uid: 32256 topic_id: 31115 reply_id: 124624[/import]

This means that Android has not got a fix on your current location yet. That is why it is returning nil values. The device’s current location is unknown. You have to wait longer for the device to get a fix via GPS, WiFi, or cellular service. Have a look at sample app “Interface\MapView” that is included with the Corona SDK on how to check for errors. We’ve updated this sample app to properly handle error conditions for both iOS and Android.

Note that your app will crash on iOS if the current location is unknown too… or if the end-user did not give your app permission to retrieve current location.

Also, on Android, it will automatically center to your current location if you’ve never called [lua]setCenter()[/lua] or [lua]setRegion()[/lua]. It will do so as soon as your current location is known. So, I recommend that you comment out your timer and wait. [import]uid: 32256 topic_id: 31115 reply_id: 124450[/import]

Gentlemen, thank you both for your help but I am still struggling on this one. I have read over all of the available documentation and I am simply missing something.

It boils down to this: how do I “wait until” the GPS has a fix on the device location? Sounds simple but I have used timers, runtime event listeners, etc and no matter how I code it I can’t seem to get my program to “wait until” the GPS has a fix. Do either of you have a small working sample that shows how you wait on the GPS?

I promise I’m not a total dummy! I wrote the app listed below and all I had to do with IOS devices was call getUserLocation() twice and the device would get a location fix. Not the case with Android. Thanks! Scott

http://itunes.apple.com/us/app/nc-mobile-sex-offender-registry/id496296578?mt=8 [import]uid: 85963 topic_id: 31115 reply_id: 124899[/import]

If you’re not getting any GPS data, then are you testing your app indoors? Perhaps your Android device actually can’t get fix on your location. Try going outside and testing your app there. Remember that GPS requires line-of-sight to work… especially for the cheaper GPS sensors.

Also, did you add those 2 permission that I suggested up above? That 2nd permission will fetch your location from wifi and you’ll usually get location data within a few seconds.

One more thing, and this may sound like a stupid question, but are you sure location services are enabled on your Android device? Go to your device’s Settings screen, select “Location services” from the list, and make sure the below items are checked on:

  • Use wireless networks
  • Use GPS satellites

You might also want to run the Google maps app that came with your device to see if it can retrieve your current location too.
[import]uid: 32256 topic_id: 31115 reply_id: 124900[/import]

Joshua, no problems with the device or the signal. I am getting GPS data. All is setup correctly even the build.settings. I just cant seem to figure out how to write the code that will “wait until” I get a location fix before continuing on. For example, the “Location and Maps” sample that comes with the SDK initializes the map with preset hardcoded coordinates. How would I write the code to wait until I got the user’s current location and initialized it with that instead of the preset coordinates? It’s the “wait until” piece I am missing. Scott
[import]uid: 85963 topic_id: 31115 reply_id: 124902[/import]

No small working example – but I’ll write a little psuedo-code for you…

The main thing is – asynchronous. The GPS will respond when it wants. Your main program cannot just hard core loop and wait… So throw up a waiting screen if you have to, that show an accuracy figure (which will be modified as per the psudeo-code below).

local gpsLong = 0  
local gpsLat = 0  
local gpsAcc = 10000 -- some enormous value...  
  
function locationHandler(event)  
 gpsLong = event.longitude  
 gpsLat = event.latitude  
 gpsAcc = event.accuracy   
end  
  
function screenUpdate(event)  
  
 if( gpsAcc \< 101 ) then  
 print(" -- Accuracy acceptable, starting up") -- Let's get the party started...  
 else  
 print(" -- Accuracy still ", gpsAcc) -- Update user on how we are doing...  
 end  
end  
  
function main()  
  
 Runtime:addEventListener( "location", locationHandler )  
 Runtime:addEventListener( "enterFrame", screenUpdate )  
  
end  

No guarantees on the actual code above, it’s off the cuff. But the idea, events – are one way to do it, pretty easily. [import]uid: 79933 topic_id: 31115 reply_id: 124906[/import]

Bad post :frowning:

see below [import]uid: 79933 topic_id: 31115 reply_id: 124903[/import]

mpappas solution will work. (Thanks for that!)

The other way to do it based on your original code is like this…
[lua]-- Create the map view.
local myMap = native.newMapView( 20, 20, 300, 220 )

– Poll for current location and show it on the map.
local myTimerId = nil
local function callMap()
local currentLocation = myMap:getUserLocation()
if currentLocation.errorCode then
– Current location has not been received yet.
else
– Current location received. Show it on the map and disable the timer.
timer.cancel(myTimerId)
myMap:setCenter(currentLocation.latitude, currentLocation.longitude, true)
end
end
myTimerId = timer.performWithDelay(1000, callMap, -1)[/lua]

The above will poll the map view for the device’s current location via the map view. Once the current location has been discovered, it will display it in the map view and disable the timer. Now, if you want to continuously track the user’s current location, then remove the [lua]timer.cancel()[/lua] function. [import]uid: 32256 topic_id: 31115 reply_id: 124913[/import]

Joshua, thanks for the “Quick” reply! I took a look at the sample code particularly the button2Release function where it fetches the user’s current location. That code appears to give the user an error message (showAlert) if the location is unknown. Instead of giving an error how would I wait until the device has a fix on the location? I have tried several variations of “do while” loops and timer delays but can’t quite get it. I know it is something simple that I am just missing. Thanks again! Scott
[import]uid: 85963 topic_id: 31115 reply_id: 124543[/import]

Greetings mobile36.

I’ve got a test in my code that seems to work well:

if( tonumber(event.accuracy) < 101 ) then – Wait for good accuracy – 100 meters or less
– do your stuff, GPS is accurate enough…
end

The event is of course a location event… Seems to work for me on both iOS and Droid… My current Android phone takes a while to get a good fix (indoors), but if I move into a clear area, it seems to get a fix much quicker.

Good Luck! [import]uid: 79933 topic_id: 31115 reply_id: 124565[/import]

Thanks mpappas, I understand your logic but if the event.accuracy is >= 101 how do you wait and keep forcing a read from the GPS until it is < 101? I’m having trouble with the “wait” part. Thanks!
[import]uid: 85963 topic_id: 31115 reply_id: 124602[/import]