PDA

View Full Version : Maps and Airplane Mode (PhoneGap)



squarefan
2 May 2012, 4:26 AM
Hi,

I am using the Google Maps plugin that comes with Sencha Touch 2 and facing the problem that when I package the app with PhoneGap everything works fine... except when I try to start the app with Airplane Mode enabled (no connectivity) - map is nested within a TabPanel btw.

The app starts and hangs at a "blank" screen - basically the app cannot load the external google maps js file that I have to include from the google server. My question is now is there a possibility with Sencha to circumvent this problem and make the application load gracefully and then when connectivity is reestablished tries to load the Maps plugin again?

I am also using the Traffic plugin provided within the samples for the overlay.

Thanks!

mitchellsimoens
4 May 2012, 5:49 AM
Google Maps need a connection to load. After load you can see the map that you first get but without a connection you won't get any update to the map if you drag around

squarefan
4 May 2012, 10:07 AM
Hi,

thanks for the answer. Maybe I was a bit unclear.

I am aware that Google Maps needs the internet - but the problem I am facing is that when starting the app in Airplane mode the app won't start at all... meaning tab panel etc. is not loaded (from what I could gather it is because of Google Maps not being available).

So basically my questions are:
1. is there a way to circumvent Touch 2 to fail to load the components in case Maps is used, packaged with PhoneGap and Airplane mode enabled (hindering external google maps js file to load)
2. if point 1 can be resolved is there a possiblity to force a reload of google maps when internet connectivity is re-established (which I can check via PhoneGap).

thanks again for your help!

mitchellsimoens
4 May 2012, 10:34 AM
Are you getting any JS errors like google is undefined?

rohdef
5 Jun 2012, 3:22 AM
I'm playing a bit with this problem, currently I haven't got a final solution, but you can see what Iv'e got so far:

My view:


Ext.define('Foo.view.FooView', {
extend: 'Ext.Panel',
xtype: 'fooView',
requires: [ 'Foo.model.Foo', 'Ext.Img' ],


config: {
store: 'Foos',
foo: null,
layout: 'vbox',


items: [ {
xtype: 'panel',
html: '<h1>Shopinfo</h1>',
flex: 2
}, {
xtype: 'container',
id: 'mapContainer',
flex: 5,
layout: 'fit',


items: [ {
xtype: 'image',
src: 'maps_offline.png',
} ],


masked: {
xtype: 'loadmask',
message: 'Google Maps isn\'t downloaded yet.',
indicator: false,
},
} ],
},
});


The controller that does all the work:


// placeholder for the Google Maps callback. There might be a better way?
var mapInit;

Ext.define('Foo.controller.FooViewController', {
extend: 'Ext.app.Controller',
requires: [],


config: {
refs: {
fooView: 'navigationView fooView',
mapContainer: 'navigationView fooView #mapContainer',
},


control: {
fooView: {
show: 'showFoo',
},
},
},


/**
* Show the foo data. Most importantly set up the map in a nice way even if
* the phone is offline.
*/
showFoo: function()
{
this.addConnectivityListeners();
this.online = this.hasConnection();


if (typeof google == "undefined" || typeof google.maps == "undefined") {
console.log("Google maps JavaScript needs to be loaded.");


this.disableMap();
if (this.online) {
this.loadGoogleMaps(this);
}
} else {
if (this.online) {
this.showMap();
this.enableMap();
} else {
this.disableMap();
}
}
},


/**
* Asks phonegap if there's a connection to the internet.
*
* @returns {Boolean} true if connected.
*/
hasConnection: function()
{
var networkState = navigator.network.connection.type;


var states = {};
states[Connection.UNKNOWN] = 'Unknown connection';
states[Connection.ETHERNET] = 'Ethernet connection';
states[Connection.WIFI] = 'WiFi connection';
states[Connection.CELL_2G] = 'Cell 2G connection';
states[Connection.CELL_3G] = 'Cell 3G connection';
states[Connection.CELL_4G] = 'Cell 4G connection';
states[Connection.NONE] = 'No network connection';


return networkState != Connection.NONE && networkState != Connection.UNKNOWN;
},


/**
* Listen for connection changes from PhoneGap
*/
addConnectivityListeners: function()
{
var me = this;


var onOnline = function()
{
console.log('online');
if (typeof google == "undefined" || typeof google.maps == "undefined") {
me.loadGoogleMaps(me);
} else {
if (!this.online) {
me.showMap();
this.online = true;
}
me.enableMap();
}
};


var onOffline = function()
{
console.log('offline');
me.disableMap();
};


document.addEventListener("online", onOnline, false);
document.addEventListener("offline", onOffline, false);
},


/**
* Download the Google Maps JavaScript and execute it.
*
* @param me
* the current class or another place where the functions showMap and
* enableMap resides.
*/
loadGoogleMaps: function(me)
{
console.log('Load Google APIs');


this.getMapContainer().setMasked({
xtype: 'loadmask',
message: 'Loading Google Maps.',
indicator: true,
});

var api_key = ''; // Insert your google API key here.
var script = document.createElement("script");
script.type = "text/javascript";
script.src = 'http://maps.google.com/maps/api/js?key=' + api_key + '&sensor=true&callback=mapInit';
document.head.appendChild(script)

},


/**
* Remove the dummy picture and show the real map with a marker on the foo
* place.
*/
showMap: function()
{
console.log('Showing map');
var me = this;


var map = Ext.create('Ext.Map', {
useCurrentLocation: true,
layout: 'fit',


mapOptions: {
mapTypeId: google.maps.MapTypeId.ROADMAP,
streetViewControl: false,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL,
position: google.maps.ControlPosition.RIGHT_BOTTOM,
}
},


listeners: {
maprender: function(extMap, googleMap, opts)
{
var foo = me.getFooView().getFoo();


var point = new google.maps.LatLng(foo.get("latitude"), foo
.get("longitude"));
var marker = new google.maps.Marker({
position: point,
});


marker.setMap(googleMap);
},
},
});


this.getMapContainer().removeAt(0);
this.getMapContainer().add([ map ]);
this.getMapContainer().setActiveItem(map);


this.map = map;
},


/**
* Unmask the map container, so the map can be used.
*/
enableMap: function()
{
this.getMapContainer().setMasked(false);
},


/**
* Masks the map container with a user friendly message when offline or maps
* isn't loaded.
*/
disableMap: function()
{
this.getMapContainer().setMasked({
xtype: 'loadmask',
message: 'Internet connection is needed to show map',
indicator: false,
});
},
});



Although currently there is one major problem that occurs in the case when the phone haven't loaded anyting at startup, when maps is downloaded all seems fine and dandy except that the showMap call clears all layout on the screen including my navigation panel. I haven't figured out why yet.

squarefan
5 Jun 2012, 4:02 AM
Hi,

great thanks. Sorry for the late reply - I was quite busy with a different project but now are getting back onto this problem. I will have a look at it and let you know if I get further with it.

Thanks a lot though for your input! Really appreciate it!

cheers,
Alex

rohdef
5 Jun 2012, 6:30 AM
Ok this time I remembered to do all the tests :p Seems to work nicely now.

What I changed (if you've already copied my code) is:
1) I added a placeholder for the Google Maps callback.
2) I reimplemented the way loadGoogleMaps downloads the map, and use the callback to show the map.

If you don't have an api key for maps, go to for instructions: https://developers.google.com/maps/documentation/javascript/tutorial#api_key
Or cheat - go to https://code.google.com/apis/console enable maps and read your key under API access. Good luck

digeridoopoo
5 Jun 2012, 11:59 PM
I don't know if you have tried leaflet but over the last few weeks I have successfully implemented a leaflet map using the plugin from GitHub (there are several, and one works better than the other). After much research it seems openlayers, leaflet etc are better suited for offline map storage, being open source there is no limitation for storing stuff offline.

Leaflet allows you to use any custom map tiles.....the way I have done it is to download a beautiful map from mapbox (.mbtiles format). Then using the mbutil command line tool using the default settings I was able to unarchive the mbtiles format so I had a folder of .pngs. These were then referenced in the leaflet URL link.

It's great, I have a fully zoomable blue marble beautiful map that works offline 0-15 zoom levels that should work with phonegap. Maybe I will post an example somewhere...

:-)

rohdef
6 Jun 2012, 12:54 AM
Is that a PhoneGap plugin? I for one have considered doing it with other than Google Maps, since the solution I provided is more complex than I feel should be necessary. Do you have any example code?

digeridoopoo
7 Jun 2012, 8:39 AM
Hi,

I knocked up a quick MVC example with a leaflet external URL layer:

http://wtrns.fr/- (http://wtrns.fr/-XaW1HjM1xL9Xd3)XaW1HjM1xL9Xd3

(http://wtrns.fr/-XaW1HjM1xL9Xd3)This one uses a remote layer from Mapbox but it should be easy to reference your own local one. You can download any .mbtiles file and extract using mbutil to create a folder of local tiles if you like so you can have beautiful maps.

I've also just found this leaflet extension on github: vrs.ux.touch

I was interested in it for the potential for Sencha popups on map markers but it seems like it doesn't work 100%.

If you have any success adding layers control or sencha popups please post it here, I've been struggling with it a little other than using the standard leaflet popups (they are nice but I want something even better!)

There was a solution for Touch v1 if you search, but I have not managed to adapt it yet for v2.
Also refer to my other post here: http://www.sencha.com/forum/showthread.php?206065-Solved-Leaflet-offline-map-graphical-glitch-with-offline-tiles

Cheers

:-)