Re: [ServiceWorker] Expose GeoLocation to workers (#745)

I found this a useful layman's reference for hardware support of geofencing: -
http://gpsworld.com/putting-the-ultra-low-power-in-geofence/

Explains a lot to me especially about power consumption,

I maintain that the SW architecture, in combination with any Android support for position batching, that subsequently offloads geo-fence processing to a central server in order to determine alarm/other processing for ALL users, is a viable if not preferable alternative. 

Whether or not a wake-lock or cpu-lock is a retrograde step in power consumption, its implementation and availability pays homage to the user's right to self-determination. Likewise the user should be empowered to authorize GPS tracking while the phone is off.

Surely a myopic cluster of GNSS ICs is a less than optimal solution for fleet management or social networking, and an instance of functionality-devolution gone too far? IMHO, other than for monitoring prisoners on probation, the current geofence solution is a textbook case of the tail wagging the dog. 

A Service Worker should be able to subscribe to a PositionManager optionally specifying min distance and/or seconds between updates to the position. A positionChanged event will be sufficient to re-instantiate a terminated SW.

Below is how I throttle GeoLocation.watchPosition now: -

 Tier3Toolbox.prototype.LocationThrottle = 
  
  function(initHndlr, moveHndlr, standHndlr, errorHndlr, userOptions)
  {
   if(!navigator.geolocation) {
    throw new Error("Unsupported browser - No Geolocation support");
   }

   if (arguments.length < 4) {
    throw new Error("Insufficient call arguments");
   }
   
   if (typeof initHndlr  != "function" ||
    typeof moveHndlr  != "function" || 
    typeof standHndlr != "function" ||
    typeof errorHndlr != "function") {
    throw new Error("The Init, Move, Stand, and Error handler parameters must be functions");
   }
   
   var lastPos, trackerId, loiterTimer, deltaMetres,
    recalibrateTimer, lastOptions, lastDrop, replayTimer
    ;
   
   var MAXSILENCE    = 30;
   var watchCount    = 1;
   var acceptCount   = 1;
   var lastUpdate    = 0;
   
   var initHndlr     = initHndlr;
   var moveHndlr     = moveHndlr;
   var standHndlr    = standHndlr;
   var errorHndlr    = errorHndlr;
   
   var options, defMaxSilence, timeDetent, spaceDetent,
    loiterDetent, maxLocAge, accurate, maxSilence, lastOptions
    ;
       
   function moveReplay()
   {
    replayTimer = null;
    
    if ((lastDrop.timestamp > lastPos.timestamp)) {
     lastDrop.timestamp = Date.now();
     filterLocation(lastDrop);
    }      
   }
   
   function loiterLimit()
   {
    loiterTimer = null;
    standHndlr.call(null);
   }
   
   var recalibrate = 
    function()
    {
     if (window.console) console.log("recalibrating");
     
     recalibrateTimer = null;
     
     if (trackerId) navigator.geolocation.clearWatch(trackerId);
     
     getCurrent(initAcc);
    }
   
   var getCurrent = 
    function(success) 
    {
     navigator.geolocation.getCurrentPosition(success,locError,{
        enableHighAccuracy: false,
        maximumAge: Number.POSITIVE_INFINITY,
        timeout: 10000
       });        
    }
    
   var initLoc = 
    function(position) 
    {
     lastPos = position;  
     initHndlr.call(null, position); 
    }
   
   var initAcc = 
    function(position) 
    {
     watchCount++;
     
     trackerId = navigator.geolocation.watchPosition(filterLocation, locError, {
        enableHighAccuracy: accurate,
        maximumAge: maxLocAge
       });
       
     recalibrateTimer = setTimeout(recalibrate, maxSilence);     
    }
    
   var start =
    function(userOptions)
    {
     parseOptions(userOptions);
     
     trackerId = navigator.geolocation.watchPosition(filterLocation, locError, {
        enableHighAccuracy: accurate,
        maximumAge: maxLocAge,
        timeout: Number.POSITIVE_INFINITY
       });
     
     loiterTimer = setTimeout(loiterLimit, loiterDetent);
     recalibrateTimer = setTimeout(recalibrate, maxSilence);
    }
    
   var parseOptions =
    function(userOptions)
    {
     options       = userOptions || lastOptions; 
   
     defMaxSilence = (("maxSilence"  in options) ? options.maxSilence        : MAXSILENCE    );
     timeDetent    = (("minSilence"  in options) ? options.minSilence  :     0 ) * 1000;
     spaceDetent   = (("minProgress" in options) ? options.minProgress   :      0 );
     loiterDetent  = (("maxSnail"    in options) ? options.maxSnail          : defMaxSilence ) * 1000;
     maxLocAge     = (("maxAge"      in options) ? options.maxAge        :     0 );
     if (maxLocAge != Number.POSITIVE_INFINITY) maxLocAge *= 1000;
     accurate      = (("accurate"    in options) ? options.accurate      : true          );
     maxSilence    = defMaxSilence * 1000;
   
     lastOptions   = options;
    }
   
   var locError = 
    function(error) 
    {
     errorHndlr.call(null, error);
    }
    
   var filterLocation = 
    function(position) 
    {
     if (!lastPos) return;
     
     watchCount++;

     if (position.timestamp <= lastPos.timestamp)
      return;

     var currTime = Date.now();     
     var dropping = false;
      
     if (((position.timestamp - lastPos.timestamp) < timeDetent) ||
      ((currTime           - lastUpdate       ) < timeDetent)) {
      dropping = true;
     } else {       
      clearTimeout(recalibrateTimer);
      recalibrateTimer = setTimeout(recalibrate, maxSilence);
     }
      
     deltaMetres = Tier3Toolbox.calculateDistance(
         position.coords.latitude,
         position.coords.longitude,
         lastPos.coords.latitude,
         lastPos.coords.longitude)
         
     if (deltaMetres.toFixed() < spaceDetent) {
      return;
     }
     
     if (dropping) {
      lastDrop = position;
      clearTimeout(replayTimer);
      replayTimer = setTimeout(moveReplay, timeDetent);
      return;
     }
     
     acceptCount++;
     lastPos = position;
     lastUpdate = currTime;
     
     clearTimeout(loiterTimer);
     loiterTimer = setTimeout(loiterLimit, loiterDetent);
     
     moveHndlr.call(null, position, deltaMetres);   
    }
    
   var stop = 
    function()
    {
     if (trackerId) navigator.geolocation.clearWatch(trackerId);
     
     clearTimeout(recalibrateTimer);
     clearTimeout(loiterTimer);
     clearTimeout(replayTimer);
    }
    
   parseOptions(userOptions);          
   getCurrent(initLoc);
   
   return {start : start, stop : stop};
  };


---
Reply to this email directly or view it on GitHub:
https://github.com/slightlyoff/ServiceWorker/issues/745#issuecomment-181645416

Received on Tuesday, 9 February 2016 00:42:13 UTC