From 2ca44285fa4431f87943378ad77d9245cb0ff0b3 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Thu, 15 Jan 2015 10:32:49 -0500 Subject: [PATCH] Improve Geolocation package. Add the ability to tweak the PositionOptions used for Geolocation, for instance to use low-accuracy position to save battery on mobile devices. The options parameter is reactive. Add a `pause` feature to temporarily halt position updates, again to allow better power management on mobile devices. The paused status is also reactive. Stop position watcher when there are no dependencies of the location, so that we automatically save power if (for example) a reactive map view is not visible. --- examples/simple-map/.meteor/versions | 2 +- examples/simple-map/map.html | 7 ++- examples/simple-map/map.js | 19 ++++++- packages/mdg:geolocation/README.md | 18 +++++- packages/mdg:geolocation/geolocation.js | 76 +++++++++++++++++++++---- packages/mdg:geolocation/package.js | 4 +- 6 files changed, 104 insertions(+), 22 deletions(-) diff --git a/examples/simple-map/.meteor/versions b/examples/simple-map/.meteor/versions index 94beaa1..a3298b7 100644 --- a/examples/simple-map/.meteor/versions +++ b/examples/simple-map/.meteor/versions @@ -25,7 +25,7 @@ jquery@1.0.1 json@1.0.1 livedata@1.0.11 logging@1.0.4 -mdg:geolocation@1.0.2 +mdg:geolocation@1.0.3 meteor-platform@1.1.2 meteor@1.1.2 minifiers@1.1.1 diff --git a/examples/simple-map/map.html b/examples/simple-map/map.html index 4116445..43d111b 100644 --- a/examples/simple-map/map.html +++ b/examples/simple-map/map.html @@ -7,7 +7,12 @@

Where am I?

+ + {{#if error}}

Error: {{error.message}}

{{/if}} - \ No newline at end of file + diff --git a/examples/simple-map/map.js b/examples/simple-map/map.js index 6e27d4b..e106741 100644 --- a/examples/simple-map/map.js +++ b/examples/simple-map/map.js @@ -1,9 +1,22 @@ if (Meteor.isClient) { + Template.body.events({ + "change .high-accuracy input": function (event) { + Session.set("highAccuracy", event.target.checked); + } + }); Template.body.helpers({ loc: function () { + // options is *optional*. We're including it in this demo + // to show how it is reactive. + var options = { + enableHighAccuracy: !!Session.get("highAccuracy") + }; // return 0, 0 if the location isn't ready - return Geolocation.latLng() || { lat: 0, lng: 0 }; + return Geolocation.latLng(options) || { lat: 0, lng: 0 }; }, - error: Geolocation.error + error: Geolocation.error, + highAccuracy: function() { + return Session.get("highAccuracy"); + } }); -} \ No newline at end of file +} diff --git a/packages/mdg:geolocation/README.md b/packages/mdg:geolocation/README.md index d586d61..ca487b5 100644 --- a/packages/mdg:geolocation/README.md +++ b/packages/mdg:geolocation/README.md @@ -10,10 +10,22 @@ There are currently no options to set. Every method is reactive using [Tracker]( Returns the [position error](https://developer.mozilla.org/en-US/docs/Web/API/PositionError) that is currently preventing position updates. -### Geolocation.currentLocation() +### Geolocation.currentLocation(options) Returns the [position](https://developer.mozilla.org/en-US/docs/Web/API/Position) that is reported by the device, or null if no position is available. -### Geolocation.latLng() +The `options` parameter is optional; if provided it as if `Geolocation.setOptions` was called before returning the position. -A simple shorthand for currentLocation() when all you need is the latitude and longitude. Returns an object that has `lat` and `lng` keys. \ No newline at end of file +### Geolocation.latLng(options) + +A simple shorthand for currentLocation() when all you need is the latitude and longitude. Returns an object that has `lat` and `lng` keys. + +The `options` parameter is optional; if provided it as if `Geolocation.setOptions` was called before returning the position. + +### Geolocation.setOptions(options) + +Provide [PositionOptions](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions) to manage power consumption on mobile devices. The options can be reactive. + +### Geolocation.setPaused(boolean) + +If the parameter is `true`, temporarily halts reactive position updates to reduce power consumption. diff --git a/packages/mdg:geolocation/geolocation.js b/packages/mdg:geolocation/geolocation.js index 87616a6..8ee7643 100644 --- a/packages/mdg:geolocation/geolocation.js +++ b/packages/mdg:geolocation/geolocation.js @@ -8,24 +8,53 @@ var location = new ReactiveVar(null); var error = new ReactiveVar(null); // options for watchPosition -var options = { +var options = new ReactiveVar({ enableHighAccuracy: true, maximumAge: 0 -}; +}, function(a, b) { + // Reject attempts to set this to a non-object. + if (a == null || b == null || typeof a !== 'object' || typeof b !=='object') { + return true; + } + return ( + a.enableHighAccuracy === b.enableHighAccuracy && + a.maximumAge === b.maximumAge && + a.timeout === b.timeout + ); +}); + +// pause geolocation updates +var paused = new ReactiveVar(false); var onError = function (newError) { error.set(newError); + Tracker.afterFlush(checkDependents); }; var onPosition = function (newLocation) { location.set(newLocation); error.set(null); + Tracker.afterFlush(checkDependents); +}; + +var checkDependents = function() { + if (location.dep.hasDependents() || error.dep.hasDependents()) { + return; + } + watchingPosition.stop(); + watchingPosition = false; }; var startWatchingPosition = function () { - if (! watchingPosition && navigator.geolocation) { - navigator.geolocation.watchPosition(onPosition, onError, options); - watchingPosition = true; + if (watchingPosition === false && navigator.geolocation) { + watchingPosition = Tracker.autorun(function() { + if (paused.get()) { return; } + var watchId = + navigator.geolocation.watchPosition(onPosition, onError, options.get()); + Tracker.onInvalidate(function() { + navigator.geolocation.clearWatch(watchId); + }); + }); } }; @@ -43,29 +72,37 @@ Geolocation = { * that is currently preventing position updates. */ error: function () { - startWatchingPosition(); + Tracker.nonreactive(startWatchingPosition); return error.get(); }, /** * @summary Get the current location + * @param {PositionOptions} options Optional geolocation options + * @param {Boolean} options.enableHighAccuracy + * @param {Number} options.maximumAge + * @param {Number} options.timeout * @return {Position | null} The * [position](https://developer.mozilla.org/en-US/docs/Web/API/Position) * that is reported by the device, or null if no position is available. */ - currentLocation: function () { - startWatchingPosition(); + currentLocation: function (_options) { + if (_options != null) { + Geolocation.setOptions(_options); + } + Tracker.nonreactive(startWatchingPosition); return location.get(); }, + // simple version of location; just lat and lng - + /** * @summary Get the current latitude and longitude * @return {Object | null} An object with `lat` and `lng` properties, * or null if no position is available. */ - latLng: function () { - var loc = Geolocation.currentLocation(); + latLng: function (options) { + var loc = Geolocation.currentLocation(options); if (loc) { return { @@ -75,5 +112,20 @@ Geolocation = { } return null; + }, + + /** + * @summary Set the PositionOptions used for geolocation. + */ + setOptions: function(_options) { + options.set(_options); + }, + + /** + * @summary Allow temporarily halting reactive updates to position. + */ + setPaused: function(_paused) { + paused.set(_paused); } -}; \ No newline at end of file + +}; diff --git a/packages/mdg:geolocation/package.js b/packages/mdg:geolocation/package.js index 66294d8..cce8811 100644 --- a/packages/mdg:geolocation/package.js +++ b/packages/mdg:geolocation/package.js @@ -1,7 +1,7 @@ Package.describe({ name: "mdg:geolocation", summary: "Provides reactive geolocation on desktop and mobile.", - version: "1.0.2", + version: "1.0.3", git: "https://github.com/meteor/mobile-packages" }); @@ -14,4 +14,4 @@ Package.on_use(function (api) { api.versionsFrom("METEOR@0.9.2"); api.add_files(["geolocation.js"], "client"); api.export("Geolocation", "client"); -}); \ No newline at end of file +});