Google Maps Connectivity

Part 3: Advanced Google Maps Integration with Ionic and Remote Data



·

In Part 1 of this tutorial series we looked at how to set up a basic Google Maps integration within an Ionic application. In Part 2, we took that a little further and looked at how we might load in map markers dynamically using the $http service.

In this tutorial we’re going to complete the transition from basic Google Maps implementation to an advanced and production ready implementation. We’re going to tackle the two major remaining issues with integrating Google Maps into an Ionic application which are:

  1. Dealing with a user who has no Internet connection or loses their Internet connection
  2. Only loading markers that need to be loaded, not loading every single marker that exists at once

To do this we’re going to make some modifications to what we have already done, so if you want to follow along make sure you have completed Part 1 and Part 2 already.

Dealing with Online and Offline States

We’re using the Google Maps JavaScript SDK which needs to be loaded from Googles servers. So what happens when a user tries to access our application when they are not connected to the Internet?

If we haven’t added any mechanism to deal with that scenario, then the app will break. The app will try to access the google object at some point which will not exist because the SDK could not be loaded and it will throw an error.

Alternatively, what if the user did have an Internet connection initially but loses it later? An Internet connection is needed to pull in new data to the map so again, it won’t work.

To deal with this, we’re going to implement functionality within our application that will:

  • Only load the Google Maps SDK if the user is online
  • If the user is not online, wait until they are online and then load the Google Maps SDK
  • If the user was online but goes offline, disable the map with a friendly error message
  • If the user was offline but comes back online, reenable the map and remove the error message.

and it’s going to look something like this:

Google Maps Not Available

There are ways we can detect if the user is online both by using a Cordova plugin, and also by just using simple JavaScript. As well as detecting if they are online, we can also detect when they go online or offline. I’ve talked about how to do this in depth in this tutorial but in short the code looks like this:

      // listen for Online event
      $rootScope.$on('$cordovaNetwork:online', function(event, networkState){
        doSomething();
      });

      // listen for Offline event
      $rootScope.$on('$cordovaNetwork:offline', function(event, networkState){
        doSomething();
      });

when using the $cordovaNetwork plugin, or like this:

      window.addEventListener("online", function(e) {
        doSomething();
      }, false);    

      window.addEventListener("offline", function(e) {
        doSomething();
      }, false);  

when just using plain old JavaScript. So we’re going to use this concept to dynamically load the Google Maps SDK based on when an Internet connection is available, and disable and reenable the map based on the users current connection.

REMOVE the following line from index.html

<script src="http://maps.google.com/maps/api/js?&sensor=true"></script>

Since we want to load the Google Maps SDK based on when a connection is available, we have to remove it from index.html which will try to load it right away.

NOTE: This code is based on Rohde Fischer’s solution to this problem in Sencha Touch.

First we are going to create another factory to give us a consistent way to tell if the user is online or offline (since which method we use depends on whether it is running on a device or not). Before we do that though, let’s install the $cordovaNetwork plugin.

Run the following command to install the $cordovaNetwork plugin

cordova plugin add cordova-plugin-network-information

Create a ConnectivityMonitor factory that looks like this:

.factory('ConnectivityMonitor', function($rootScope, $cordovaNetwork){

  return {
    isOnline: function(){

      if(ionic.Platform.isWebView()){
        return $cordovaNetwork.isOnline();    
      } else {
        return navigator.onLine;
      }

    },
    ifOffline: function(){

      if(ionic.Platform.isWebView()){
        return !$cordovaNetwork.isOnline();    
      } else {
        return !navigator.onLine;
      }

    }
  }
})

Now we can simply call the isOnline or isOffline functions from this factory and it will return the network status no matter what platform we are running on.

Modify your GoogleMaps factory to reflect the following

.factory('GoogleMaps', function($cordovaGeolocation, $ionicLoading, 
$rootScope, $cordovaNetwork, Markers, ConnectivityMonitor){

  var apiKey = false;
  var map = null;

  function initMap(){

    var options = {timeout: 10000, enableHighAccuracy: true};

    $cordovaGeolocation.getCurrentPosition(options)
.then(function(position){

      var latLng = new google.maps.LatLng(position.coords.latitude, 
position.coords.longitude);

      var mapOptions = {
        center: latLng,
        zoom: 15,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };

      map = new google.maps.Map(document.getElementById("map"), mapOptions);

      //Wait until the map is loaded
      google.maps.event.addListenerOnce(map, 'idle', function(){
        loadMarkers();
        enableMap();
      });

    }, function(error){
      console.log("Could not get location");
    });

  }

  function enableMap(){
    $ionicLoading.hide();
  }

  function disableMap(){
    $ionicLoading.show({
      template: 'You must be connected to the Internet to view this map.'
    });
  }

  function loadGoogleMaps(){

    $ionicLoading.show({
      template: 'Loading Google Maps'
    });

    //This function will be called once the SDK has been loaded
    window.mapInit = function(){
      initMap();
    };  

    //Create a script element to insert into the page
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.id = "googleMaps";

    //Note the callback function in the URL is the one we created above
    if(apiKey){
      script.src = 'http://maps.google.com/maps/api/js?key=' + apiKey 
+ '&sensor=true&callback=mapInit';
    }
    else {
script.src = 'http://maps.google.com/maps/api/js?sensor=true&callback=mapInit';
    }

    document.body.appendChild(script);

  }

  function checkLoaded(){
    if(typeof google == "undefined" || typeof google.maps == "undefined"){
      loadGoogleMaps();
    } else {
      enableMap();
    }       
  }

  function loadMarkers(){

      //Get all of the markers from our Markers factory
      Markers.getMarkers().then(function(markers){

        console.log("Markers: ", markers);

        var records = markers.data.result;

        for (var i = 0; i < records.length; i++) {

          var record = records[i];   
          var markerPos = new google.maps.LatLng(record.lat, record.lng);

          // Add the markerto the map
          var marker = new google.maps.Marker({
              map: map,
              animation: google.maps.Animation.DROP,
              position: markerPos
          });

          var infoWindowContent = "<h4>" + record.name + "</h4>";          

          addInfoWindow(marker, infoWindowContent, record);

        }

      }); 

  }

  function addInfoWindow(marker, message, record) {

      var infoWindow = new google.maps.InfoWindow({
          content: message
      });

      google.maps.event.addListener(marker, 'click', function () {
          infoWindow.open(map, marker);
      });

  }

  function addConnectivityListeners(){

    if(ionic.Platform.isWebView()){

      // Check if the map is already loaded when the user comes online, 
//if not, load it
      $rootScope.$on('$cordovaNetwork:online', function(event, networkState){
        checkLoaded();
      });

      // Disable the map when the user goes offline
      $rootScope.$on('$cordovaNetwork:offline', function(event, networkState){
        disableMap();
      });

    }
    else {

      //Same as above but for when we are not running on a device
      window.addEventListener("online", function(e) {
        checkLoaded();
      }, false);    

      window.addEventListener("offline", function(e) {
        disableMap();
      }, false);  
    }

  }

  return {
    init: function(key){

      if(typeof key != "undefined"){
        apiKey = key;
      }

      if(typeof google == "undefined" || typeof google.maps == "undefined"){

        console.warn("Google Maps SDK needs to be loaded");

        disableMap();

        if(ConnectivityMonitor.isOnline()){
          loadGoogleMaps();
        }
      }
      else {
        if(ConnectivityMonitor.isOnline()){
          initMap();
          enableMap();
        } else {
          disableMap();
        }
      }

      addConnectivityListeners();

    }
  }

});

There’s a bunch of code that has been added to the factory so I’ve added some comments in the parts that might not be obvious. But essentially we have just created some functions to only load the SDK when the user is online, disable the map if they go offline, and reenable it when they come back online.

Also note that we have injected a few extra services into the Factory:

  • $ionicLoading allows us to display a nice loading mask
  • $rootScope and $cordovaNetwork allow us to monitor the network status
  • ConnectivityMonitor is the factory we just created

We’ve also added the ability to supply an API Key by initlaising the factory like this:

GoogleMaps.init("API KEY GOES HERE");

if an API key is not supplied it will initialise without one. If you try loading your application without an Internet connection now you should see something like this:

No Internet Connection

and if you come back online, even without refreshing the app, the map should start working as normal.

Only Loading On-Screen Markers

If your map only has a few markers, even 50 or a 100 markers then this isn’t really a problem. You could simply load all the markers at once and be done with it.

But loading each marker and adding it to the map does mean a small performance hit. If you’re loading 1000 markers then this performance hit is no longer insignificant. What if you allow users of your application to submit markers and you end up with a total of 500,000 markers? You certainly don’t want to load all of those in at once.

To deal with this, we are going to figure out the area of the map that the user is currently looking at and only load in markers that are contained within that area. The process will look something like this:

  1. Send the current bounds of the map to the server that contains the markers
  2. Only return markers that fall within those bounds
  3. Add those markers to the map

This process will be triggered again whenever the user drags the map, which changes the bounds of the map. The end result is that markers will drop in just as the user is looking at that portion of the map.

NOTE: This code is based on the Ext.ux.MapLoader plugin by SwarmOnline for Sencha Touch.

Modify your GoogleMaps factory to reflect the following:

.factory('GoogleMaps', function($cordovaGeolocation, $ionicLoading, 
$rootScope, $cordovaNetwork, Markers, ConnectivityMonitor){

  var markerCache = [];
  var apiKey = false;
  var map = null;

  function initMap(){

    var options = {timeout: 10000, enableHighAccuracy: true};

    $cordovaGeolocation.getCurrentPosition(options)
.then(function(position){

      var latLng = new google.maps.LatLng(position.coords.latitude, 
position.coords.longitude);

      var mapOptions = {
        center: latLng,
        zoom: 15,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };

      map = new google.maps.Map(document.getElementById("map"), 
mapOptions);

      //Wait until the map is loaded
      google.maps.event.addListenerOnce(map, 'idle', function(){
        loadMarkers();

        //Reload markers every time the map moves
        google.maps.event.addListener(map, 'dragend', function(){
          console.log("moved!");
          loadMarkers();
        });

        //Reload markers every time the zoom changes
        google.maps.event.addListener(map, 'zoom_changed', function(){
          console.log("zoomed!");
          loadMarkers();
        });

        enableMap();

      });

    }, function(error){
      console.log("Could not get location");
    });

  }

  function enableMap(){
    $ionicLoading.hide();
  }

  function disableMap(){
    $ionicLoading.show({
      template: 'You must be connected to the Internet to view this map.'
    });
  }

  function loadGoogleMaps(){

    $ionicLoading.show({
      template: 'Loading Google Maps'
    });

    //This function will be called once the SDK has been loaded
    window.mapInit = function(){
      initMap();
    };  

    //Create a script element to insert into the page
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.id = "googleMaps";

    //Note the callback function in the URL is the one we created above
    if(apiKey){
      script.src = 'http://maps.google.com/maps/api/js?key=' + apiKey 
+ '&sensor=true&callback=mapInit';
    }
    else {
script.src = 'http://maps.google.com/maps/api/js?sensor=true&callback=mapInit';
    }

    document.body.appendChild(script);

  }

  function checkLoaded(){
    if(typeof google == "undefined" || typeof google.maps == "undefined"){
      loadGoogleMaps();
    } else {
      enableMap();
    }       
  }

  function loadMarkers(){

      var center = map.getCenter();
      var bounds = map.getBounds();
      var zoom = map.getZoom();

      //Convert objects returned by Google to be more readable
      var centerNorm = {
          lat: center.lat(),
          lng: center.lng()
      };

      var boundsNorm = {
          northeast: {
              lat: bounds.getNorthEast().lat(),
              lng: bounds.getNorthEast().lng()
          },
          southwest: {
              lat: bounds.getSouthWest().lat(),
              lng: bounds.getSouthWest().lng()
          }
      };

      var boundingRadius = getBoundingRadius(centerNorm, boundsNorm);

      var params = {
        "centre": centerNorm,
        "bounds": boundsNorm,
        "zoom": zoom,
        "boundingRadius": boundingRadius
      };

      var markers = Markers.getMarkers(params).then(function(markers){
        console.log("Markers: ", markers);
        var records = markers.data.result;

        for (var i = 0; i < records.length; i++) {

          var record = records[i];

          // Check if the marker has already been added
          if (!markerExists(record.lat, record.lng)) {

              var markerPos = new google.maps.LatLng(record.lng, record.lat);
              // add the marker
              var marker = new google.maps.Marker({
                  map: map,
                  animation: google.maps.Animation.DROP,
                  position: markerPos
              });

// Add the marker to the markerCache so we know not to add it again later
              var markerData = {
                lat: record.lat,
                lng: record.lng,
                marker: marker
              };

              markerCache.push(markerData);

              var infoWindowContent = "<h4>" + record.name + "</h4>";          

              addInfoWindow(marker, infoWindowContent, record);
          }          

        }

      });    
  }

  function markerExists(lat, lng){
      var exists = false;
      var cache = markerCache;
      for(var i = 0; i < cache.length; i++){
        if(cache[i].lat === lat && cache[i].lng === lng){
          exists = true;
        }
      }

      return exists;
  }

  function getBoundingRadius(center, bounds){
    return getDistanceBetweenPoints(center, bounds.northeast, 'miles');    
  }

  function getDistanceBetweenPoints(pos1, pos2, units){

    var earthRadius = {
        miles: 3958.8,
        km: 6371
    };

    var R = earthRadius[units || 'miles'];
    var lat1 = pos1.lat;
    var lon1 = pos1.lng;
    var lat2 = pos2.lat;
    var lon2 = pos2.lng;

    var dLat = toRad((lat2 - lat1));
    var dLon = toRad((lon2 - lon1));
    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
    Math.sin(dLon / 2) *
    Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c;

    return d;

  }

  function toRad(x){
      return x * Math.PI / 180;
  }

  function addInfoWindow(marker, message, record) {

      var infoWindow = new google.maps.InfoWindow({
          content: message
      });

      google.maps.event.addListener(marker, 'click', function () {
          infoWindow.open(map, marker);
      });

  }

  function addConnectivityListeners(){

    if(ionic.Platform.isWebView()){

      // Check if the map is already loaded when the user comes online, 
//if not, load it
      $rootScope.$on('$cordovaNetwork:online', function(event, networkState){
        checkLoaded();
      });

      // Disable the map when the user goes offline
      $rootScope.$on('$cordovaNetwork:offline', function(event, networkState){
        disableMap();
      });

    }
    else {

      //Same as above but for when we are not running on a device
      window.addEventListener("online", function(e) {
        checkLoaded();
      }, false);    

      window.addEventListener("offline", function(e) {
        disableMap();
      }, false);  
    }

  }

  return {
    init: function(key){

      if(typeof key != "undefined"){
        apiKey = key;
      }

      if(typeof google == "undefined" || typeof google.maps == "undefined"){

        console.warn("Google Maps SDK needs to be loaded");

        disableMap();

        if(ConnectivityMonitor.isOnline()){
          loadGoogleMaps();
        }
      }
      else {
        if(ConnectivityMonitor.isOnline()){
          initMap();
          enableMap();
        } else {
          disableMap();
        }
      }

      addConnectivityListeners();

    }
  }

});

Again, there’s a lot going on here but the key parts are:

  • We supply the bounds of the map as parameters when loadining markers (which we will make use of shortly)
  • We are keeping track of markers we have already added with markerCache to make sure we don’t add the same ones twice
  • We listen for when the map changes its bounds (when the user drags or zooms) and then load more markers by supplying the new bounds information
  • We’ve added some functions like getDistanceBetweenPoints which are just using standard formulas for calculating distances between points (it’s the Haversine Formula for those interested). We do this so we can calculate the distance for which we want to load markers: if the map was zoomed in the distance from the center to the corner might be 1km, but zoomed out it could be 100km
  • We are recreating our own boundsNorm, centerNorm etc. objects because the object supplied by Google has nonsensical names.

Since we are now supplying a params object to the Markers factory, we need to alter the Markers factory a little bit

Modify your Markers factory to reflect the following:

.factory('Markers', function($http) {

  var markers = [];

  return {
    getMarkers: function(params){

      return $http.get("http://example.com/markers.php",{params:params}).then(function(response){
          markers = response;
          return markers;
      });

    }
  }

})

Finally, we need to modify the server side code to take the area of the map we are looking at into account. Again, my example is in PHP but you can implement this with whatever you would like.

Modify your markers.php file or similar to reflect the following

<?php

    //Create a connection to the database
    $mysqli = new mysqli("localhost", "username", "password", "database");

    //The default result to be output to the browser
    $result = "{'success':false}";

    //Grab the extra params
    $centre = json_decode(stripslashes($_GET["centre"]), true);
    $bounds = json_decode(stripslashes($_GET["bounds"]), true);
    $boundingRadius = $_GET["boundingRadius"];

    //Select everything from the table containing the marker informaton
    $query = sprintf("SELECT *, ( 3959 * acos( cos( radians('%s') ) * cos( radians( lat ) ) 
* cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) 
* sin( radians( lat ) ) ) ) AS distance FROM markers ",
      $mysqli->real_escape_string($centre['lat']),
      $mysqli->real_escape_string($centre['lng']),
      $mysqli->real_escape_string($centre['lat']));

    //Restrict query to those within bounding radius
    $maxDistance = $mysqli->real_escape_string($boundingRadius);

    $query .= " HAVING distance < '".$maxDistance."'";
    $query .= " ORDER BY distance";
    $query .= " LIMIT 300";

    //Run the query
    $dbresult = $mysqli->query($query);

    //Build an array of markers from the result set
    $markers = array();

    while($row = $dbresult->fetch_array(MYSQLI_ASSOC)){

        $markers[] = array(
            'id' => $row['id'],
            'name' => $row['name'],
            'lat' => $row['lat'],
            'lng' => $row['lng']
        );
    }

    //If the query was executed successfully, create a JSON string containing the marker information
    if($dbresult){
        $result = "{'success':true, 'markers':" . json_encode($markers) . "}";              
    }
    else
    {
        $result = "{'success':false}";
    }

    //Set these headers to avoid any issues with cross origin resource sharing issues
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
    header('Access-Control-Allow-Headers: Content-Type,x-prototype-version,x-requested-with');

    //Output the result to the browser so that our Ionic application can see the data
    echo($result);

?>
BONUS CONTENT: Grab the entire source code for this tutorial by entering your email address below:

Now in the code above we’re calculating the distance of each marker from the center of the map, and only returning those that have a distance that is less than the bounding radius of the map, which is the distance from the center of the map to the corner. Just in case there is still a lot of markers being returned, we set a hard limit of 300 markers per load.

If you load up your application now you should see the markers appear only as you look at a specific location. That brings us to the end of this tutorial series, but if there’s anything else you think would be useful to cover leave it in the comments below.

What to watch next...

  • Pingback: Part 1: Using the $http Service in Ionic to Dynamically Load Google Map Markers | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Фаёзжон Бердиев

    Hi there!
    First thank you for tutorial.
    I have build an app using this tutorial, have compiled android and installed.
    When i debug it via Chrome Inspect (crosswalk version of app) its throws console.log message

    Google Maps SDK needs to be loaded

    Could not get location

    GPS is turned on
    There is an indicator on the top of device is flashing, but anyway its a white screen.

    Please help me to implement google maps on my app

  • masai2k

    the code in php to restrict the markers is wrong: you inverted lng and lat. This is the correct code:

    $query = sprintf(“SELECT *, ( 3959 * acos( cos( radians(‘%s’) ) * cos( radians( lat ) ) * cos( radians( lng ) – radians(‘%s’) ) + sin( radians(‘%s’) ) * sin( radians( lat ) ) ) ) AS distance FROM markers “,$mysqli->real_escape_string($centre[‘lat’]), $mysqli->real_escape_string($centre[‘lng’]),$mysqli->real_escape_string($centre[‘lat’]));

  • Pingback: Dealing with a user who has no Internet connection or loses their Internet connection & Only loading markers that need to be loaded, not loading every single marker that exists at once | Aprender IONIC()

  • Emil Eubboline

    I implemented everything successfull but when I change to another tab in the app and then come back to the map the old markers won’t load anymore.

  • GettingLostAU

    Hey Josh. Great tutorial! I’ve just begun learning Ionic/Angular/Mobile Dev and this is one of the best actual easy to understand tutorials I’ve come across. It’s also relevant to an app idea I have so works in well 😉 One thing I’d be interested in that follows on with the online/offline bit is how would you cache to local storage the markers/info for an area so the app could still be used offline once the connection goes down? I see markerCache stores markers, however not sure how to set how long for or how much info is kept. I’m thinking that it might be useful to store a larger bounding area containing markers (up to a certain size) when the connection is there, and then be able to reference those markers from then on without hitting the server again. Would this be getting into adding a dedicated local DB as storage for the app?

    On another note … see you are travelling AU! We are as well … currently in Broome WA and heading across the top end once the wet finishes. Follow us on facebook.com/GettingLostAU if you want. Might bump into you somewhere 🙂

  • Pingback: Using Http to Fetch Remote Data from a Server in Ionic 2 | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • shehan mark

    i cant put a marker to my current location everything else is working fine can you give me a help with this ?

  • navid sheydaei

    Hey Josh
    thank you for these tutorial. i have used your article and every things work fine in browse but in mobile it does not work truly. and always it shows this error : ‘you must be connected to the internet to view this map’. mobile is connected and gps is on.
    could you please help me to solve this.
    thank you in advance.

  • Pingback: Creating an Advanced Google Maps Component in Ionic 2 | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • dsb mac

    if your markers are not showing up correctly try the following: there is a typo on line 131 of the last posted loadMarkers() definition. It should be “var markerPos = new google.maps.LatLng(record.lat, record.lng);”. The tutorial’s code has the parameters reversed in error.

  • andi

    In the function loadMarkers() i get the following Error:

    Error: records is undefined
    loadMarkers/markers<@http://localhost:8100/js/app.js:183:25

    The markers object is filled with data, what does "var records = markers.data.result;" do?

    Thank you for you help

  • Emil Gabriel Kitua

    TypeError: Cannot read property ‘length’ of undefined…

    i get that error on the for loop code:
    for (var i = 0; i < records.length; i++) { .. }

    whats wrong?
    and i cannot display markers even for my current location

    • codemonkey

      app.js, Line 186
      change var records = markers.data.result;
      TO
      var records = markers.data.markers;

      You are trying to find the length of the markers node. There is no “result” node. (“{‘success’:true, ‘markers’:” id…….)

      • Emil Gabriel Kitua

        Thanks

      • Asterix

        Thanks, that removed my error message. I don’t see any markers now though. I saw the markers in tutorial 2… now disappeared…

  • codemonkey

    If you get an a error: Unexpected token ‘ in JSON at position 1 while running the Developer Console..

    check the markers.php file .. line 47 and 51. The output isnt valid json.
    change: $result = “{‘success’:true, ‘markers’:” . json_encode($markers) . “}”;
    TO
    $result = ‘{“success”:true, “markers”:’ . json_encode($markers) . ‘}’;

    You need to flip the single and double quotes to form valid JSON.

    • Asterix

      Thanks it worked!

  • Brian McGrath

    Probably a stupid question but where exactly is the Gmaps API key value entered on this script??

    • Mono

      same erros

    • Asterix

      I added my API key here

      var apiKey = “123456ertyuiioppp”;

      I don’t get any errors but I don’t know if it’s sent either

  • disak

    Hi, thank you very much for this great tutorial, it has helped me a lot but I have left a doubt,

    Is 3959 in the query?

  • Hugo Mosh Cardoza

    Do you have this for ionic2?

  • Bert Carremans

    I think there is an error in the function loadMarkers in the GoogleMaps factory. At line 140, it says var markerPos = new google.maps.LatLng(record.lng, record.lat);
    However, I think the lng and lat arguments need to be inverted as follows var markerPos = new google.maps.LatLng(record.lat, record.lng);

    I changed this in my code because no markers showed up on the map while they were extracted from my database. I guess these markers were shown on the map, but in another location not visible in my screen.

    Nevertheless, a very big thank you for this excellent tutorial!

  • Mono

    not show markers, only map

  • valmy roi

    How to check if the gps is turn on ? Cause i’ve noticed that when the app is launched with gps already on, it works well. But when the gps is not on, even if it is turned on later it doesn’t show the map.

  • ronjark

    Really nice tutorial. I’m not using Ionic but AngularJS and Cordova. And i’m trying to use an ui-sref link or ng-click in the infowindows so i can open them in another state and read more information about them, but it seems there is some sort of compliation error or something that i can’t seem to work out. Do you have any experience with this issue?

  • sym ian

    Hi guys, if anyone of you can assists me on following issue. As I’m totally new in ionic. I have followed the steps closely.
    And the map is successfully shown on Android devices. But not in iPhone with iOS 10.3.1.
    When I opened the app, it shown “Loading Google Maps” message and then it disappeared with blank page (no map shown at all).
    May I know what’s the problem. Thanks.
    And really sorry for my poor English. Thanks in advanced.