Search code examples
javascriptgoogle-mapscordovaphonegapgeofencing

Cannot get Geofence to appear in the google map


I am trying to get Google Maps Geofencing to appear in my map.

The rest of the code all works except the Geofencing Section which is not appearing in the map.

Here is the code which I'm using in my mobile:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
        <title>Blank App</title>
        <script src="http://maps.google.com/maps/api/js" type="text/javascript"></script>
    </head>
    <body>
    <div id="map" style="width: 100%; height: 100vh;"></div>

    <div id="geolocation"></div>

<script type="text/javascript" src="cordova.js"></script>

<script>

class CircularGeofenceRegion {
  constructor(opts) {
    Object.assign(this, opts)
  }

  inside(lat2, lon2) {
    const lat1 = this.latitude
    const lon1 = this.longitude
        const R = 63710; // Earth's radius in m

    return Math.acos(Math.sin(lat1)*Math.sin(lat2) +
    Math.cos(lat1)*Math.cos(lat2) *
    Math.cos(lon2-lon1)) * R < this.radius;
  }
}

</script>


<script type="text/javascript">

    document.addEventListener("deviceready", onDeviceReady, false);
    function onDeviceReady() {
        console.log("navigator.geolocation works well");
        getMapLocation();
        watchMapPosition();
    }

    var Latitude = undefined;
    var Longitude = undefined;

    // Get geo coordinates

    function getMapLocation() {

        navigator.geolocation.getCurrentPosition
        (onMapSuccess, onMapError, { enableHighAccuracy: true });
    }

    // Success callback for get geo coordinates

    var onMapSuccess = function (position) {

        Latitude = position.coords.latitude;
        Longitude = position.coords.longitude;

        getMap(Latitude, Longitude);

    }

    // Get map by using coordinates

    function getMap(latitude, longitude) {

        var mapOptions = {
            center: new google.maps.LatLng(0, 0),
            zoom: 1,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };

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


        var latLong = new google.maps.LatLng(latitude, longitude);

        var marker = new google.maps.Marker({
            position: latLong
        });

        marker.setMap(map);
        map.setZoom(15);
        map.setCenter(marker.getPosition());

        //////////////////////////////////////////////////////////////////////////////////////

        var locations = [
            ['Location1', -25.363, 131.044, 1]
        ];

        var infowindow =  new google.maps.InfoWindow({});
        var marker, count;
        for (count = 0; count < locations.length; count++) {
            marker = new google.maps.Marker({
              position: new google.maps.LatLng(locations[count][1], locations[count][2]),
              map: map,
              title: locations[count][0]
            });
        google.maps.event.addListener(marker, 'click', (function (marker, count) {
              return function () {
                infowindow.setContent(locations[count][0]);
                infowindow.open(map, marker);
              }
            })(marker, count));
          }


        //////////////////////////////////////////////////////////////////////////////////////
      // FENCING SECTION:

      const fenceA = new CircularGeofenceRegion({
        name: 'myfence',
        latitude: 85.363,
        longitude: 31.044,
        radius: 100 // meters
      });

      const fences = [fenceA]
      const fenceOptions = {}


      navigator.geolocation.watchPosition(({coords}) => {
        for (const fence of fences) {
          const lat = coords.latitude
          const lon = coords.longitude

          if (fence.inside(lat, lon)) {
            // do some logic
          }
        }
      }, console.error, fenceOptions);


      // END OF FENCING SECTION
      ///////////////////////////////////////////////////////////////////////////////////////


    }

    // Success callback for watching your changing position

    var onMapWatchSuccess = function (position) {

        var updatedLatitude = position.coords.latitude;
        var updatedLongitude = position.coords.longitude;

        if (updatedLatitude != Latitude && updatedLongitude != Longitude) {

            Latitude = updatedLatitude;
            Longitude = updatedLongitude;

            getMap(updatedLatitude, updatedLongitude);
        }
    }

    // Error callback

    function onMapError(error) {
        console.log('code: ' + error.code + '\n' +
            'message: ' + error.message + '\n');
    }

    // Watch your changing position

    function watchMapPosition() {
        return navigator.geolocation.watchPosition
        (onMapWatchSuccess, onMapError, { enableHighAccuracy: true, maximumAge: 3600000 });
    }


</script>

    </body>
</html>

I have marked the beginning and the end of the Fencing section commented in the code.

There is also a class that is used called CircularGeofenceRegion and is that the start of the code.

Can anyone help to get the fencing to work/appear?


Solution

  • Not answering your question directly but FYI you can just place a circle on the map and get the perimeter/circumference yourself. I use a Web Worker to free up the UI thread while I filter.

    importScripts("Tier3Toolbox.js");
    
    var currVintage = 0;
    var inBounds = false;
    var facFilter = [];
    var imageProlog = "<div style='height:5em; width:5em; display:inline-block;vertical-align:middle;'>" +
                      "<img style='height:100%; width: 100%; max-height:100%; max-width:100%' src='";
    var imageEpilog = "' ></div>";
    var facilityTable, lineBreak;
    
    self.addEventListener('message', function(e) 
    {
      var data = e.data;
      switch (data.cmd) {
        case 'init':
          initThread(data.load);
          break;
        case 'initFilter':
          for (var i=0; i<data.filterTable.length; i++) {
            facFilter[data.filterTable[i].locTypeId] = {'icon':data.filterTable[i].icon};
          }
          break;
        case 'filter':
          facFilter = [];
          for (var i=0; i<data.filterTable.length; i++) {
            if (data.filterTable[i].facSelected)
              facFilter[data.filterTable[i].locTypeId] = {'icon':data.filterTable[i].icon};
          }
          break;
        case 'search':
          var searchVintage = ++currVintage;
          var tableSearch = new searcher(searchVintage, data);
          break;
        case 'reset':
          reset();
          self.postMessage({'reset': true});
          break;
        case 'stop':
          self.postMessage({'success' : true});
          self.close(); 
          break;
        default:
          self.postMessage({'success' : false, 'msg' : data.msg});
      };
    }, false);
    
    function initThread(msg) 
    {
        facilityTable = JSON.parse(msg);
        reset();
    
        self.postMessage({'success' : true, 
                          'cnt'     : facilityTable.length
                        });     
    }   
    
    function reset() 
    {
        for (var i=0; i<facilityTable.length; i++) {
            facilityTable[i].visible=false
        }
        currVintage = 0;
    }   
    
    function searcher(searchVintage, msg)
    {
        var myVintage = searchVintage;
        var facIndex  = -1;
        var msg       = msg;
    
        var checkLoop = function()
        {
            if (myVintage != currVintage)
                return;
    
            if (++facIndex == facilityTable.length)
                return;
    
            inBounds = geoFencer.call(this, msg);
    
            if (inBounds) {
                var facMatch = 0;
                var bubbleHTML = "";
                for (var i=0; i<facilityTable[facIndex].facilities.length; i++){
                    var currFac = facilityTable[facIndex].facilities[i];
                    if (facFilter[currFac.locTypeId] != undefined) {
                        if (facMatch != 0) {
                            lineBreak = (facMatch / 3);
                            if (lineBreak == lineBreak.toFixed(0)) {
                                bubbleHTML += "<br />";
                            }
                        }
                        facMatch++;
                        bubbleHTML += imageProlog + facFilter[currFac.locTypeId].icon + imageEpilog;
    
                    }
                }
                if (facMatch == 0) {
                    inBounds = false;
                }
            }
    
            if (inBounds != facilityTable[facIndex].visible) {
                self.postMessage({'match'       : inBounds,
                                  'facIndex'    : facIndex,
                                  'scopeVintage': msg.scopeVintage,
                                  'bubbleHTML'  : bubbleHTML,
                                  'success'     : true
                                }); 
                facilityTable[facIndex].visible = inBounds;
            }
    
            setTimeout(checkLoop,0);
        }
    
        var circleCheck = function(msg) 
        {
            var diff = Tier3Toolbox.calculateDistance(
                            msg.centerLat,
                            msg.centerLng,
                            facilityTable[facIndex].searchLat,
                            facilityTable[facIndex].searchLng);
    
            if (msg.radius > diff)
                return true;        
    
            return false;
        }
    
        var rectangleCheck = function(msg) 
        {
            if (facilityTable[facIndex].searchLat > msg.SWLat &&
                facilityTable[facIndex].searchLat < msg.NELat &&
                facilityTable[facIndex].searchLng > msg.SWLng &&
                facilityTable[facIndex].searchLng < msg.NELng)
                return true;        
    
            return false;
        }
    
        var GEOFENCER = [circleCheck,rectangleCheck];
        var geoFencer = GEOFENCER[msg.checker];
    
        setTimeout(checkLoop,0);
        return this;
    
    }
    

    Invoke it from the mainline like: -

    function createFacilityMarkers(xmlhttp){
        facFinder = new Worker("facfinder.js");
        facFinder.addEventListener('message', workerInit, false);
    
        facFinder.postMessage({'cmd' : 'init', 'load' : xmlhttp.responseText});
    

    And the calculate distance function is: -

    Tier3Toolbox.calculateDistance =
    function(lat1, lon1, lat2, lon2){
        var dLat = this.toRad(lat2 - lat1);
        var dLon = this.toRad(lon2 - lon1);
        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.toRad(lat1)) * 
                Math.cos(this.toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
        var distance = this.EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return distance;
    }
    

    This set up is very useful if the user will move the circle around the map with their finger/mouse. (Good news is Google Maps finally fixed the bug stopping the map scrolling when the circle is dragged to the border!)

    PS. In case you don't know how to do the Circle bit: -

        var circleOptions = {
          clickable: true,
          draggable: true,
          editable: true,
          visible: false,
          strokeColor: 'gray',
          strokeOpacity: 0.8,
          strokeWeight: 2,
          fillColor: 'gray',
          fillOpacity: 0.35,
          center: marker.getPosition()
        };
    
        radarCircle = new radarView(new google.maps.Circle(circleOptions));
    
        google.maps.event.addDomListener(radarCircle,    'center_changed', reScope);
        google.maps.event.addDomListener(radarCircle,    'radius_changed', reScope);
    
    function radarView(superBase)
    {
        this.__proto__ = superBase;
    
        if (this instanceof google.maps.Circle) {
            augmentCircle.apply(this);
        } else if (this instanceof google.maps.Rectangle) {
            augmentRectangle.apply(this);
        } else {
            Tier3Toolbox.reportError({header:"Internal error", 
                    message:"Inheriting from unknown object"});
        }
    
        this.doX = function(x){return "x is>" + x;};
    
        return this;
    }
    
    function augmentCircle()
    {
        this.moveBorder = function()
        {
            google.maps.event.trigger(radarCircle,"center_changed");
        }
    }
    
    function augmentRectangle()
    {
        this.moveBorder = function()
        {
            google.maps.event.trigger(radarRectangle,"bounds_changed");
        }
    }
    
    
    function reScope() {
    
        var searchReq = {'cmd':'search', 'scopeVintage':scopeVintage};
        if (radarShape.getCenter) {
            searchReq.checker = 0;
            var currCenter = radarCircle.getCenter();
            searchReq.centerLat = currCenter.lat();
            searchReq.centerLng = currCenter.lng();         
            searchReq.radius = radarCircle.getRadius();
        } else {
            searchReq.checker = 1;
            searchReq.SWLat = radarShape.getBounds().getSouthWest().lat();
            searchReq.SWLng = radarShape.getBounds().getSouthWest().lng();
            searchReq.NELat = radarShape.getBounds().getNorthEast().lat();
            searchReq.NELng = radarShape.getBounds().getNorthEast().lng();
        }
    
        facFinder.postMessage(searchReq);
    }