Sample Sensors App: Windows 8* Compass and Inclinometer driving Google Street View* API using WinJS
←
→
Page content transcription
If your browser does not render page correctly, please read the page content below
Sample Sensors App: Windows 8* Compass and Inclinometer driving Google Street View* API using WinJS Introduction MapPanningJS is an example Windows Store app written in WinJS using two sensors on Ultrabook™ devices to control Google's popular Street View API. The Compass class is used to control horizontal panning (heading), and the Inclinometer class is used to control vertical tilt (pitch). The user can see different views by pointing in different directions or looking up and down simply by physically manipulating the Ultrabook. Technical Overview The Compass and Inclinometer classes are part of the Windows.Devices.Sensors namespace available in Windows 8 via the WinRT API used by Windows Store apps. This namespace is available to all WinRT language projections including C#, C/C++, and JavaScript*. This sample is written in the Windows Library for JavaScript commonly referred to as WinJS. The sensors in our example send data to a full-screen viewport rendering of street level pictures using Google Maps Street View API. The Street View API is a web service freely provided by Google. If you are going to use Google Street View API in your business applications, you will need an API key. Use the link below to find out more. https://developers.google.com/maps/documentation/streetview/ Technical Details The following pseudo-code describes building the sample app at a high level. See the supplied sample code for concrete implementation details. 1. Initialize the Compass and Inclinometer 2. Initialize the Street View Google API viewport 3. Process ReadingChanged events for each sensor 4. Create a low-pass filter 5. Start a timer and communicate with the viewport Initialize the Compass and Inclinometer Both the Compass and Inclinometer are members of the Windows.Devices.Sensors namespace. Simply assign a variable to the getDefault() for each sensor. The sensor will not generate readings until a reportInterval is set. Luckily the sensor can provide the minimum reporting interval so we don't need to guess.
default.js var compass = Windows.Devices.Sensors.Compass.getDefault(); var inclinometer = Windows.Devices.Sensors.Inclinometer.getDefault(); compass.reportInterval = compass.minimumReportInterval; inclinometer.reportInterval = inclinometer.minimumReportInterval; Initialize the Street View Google API viewport Google Street View Image API is a service that renders a viewport based on a number of parameters. Using external APIs with WinJS can be tricky. Google Street View does not play nicely with WinJS out of the box, but there is a simple way for them to co-exist and that is by using an iFrame to host the viewport. To ensure that the Google API works properly it needs to run under the web context (as opposed to local context) so our iFrame src attribute must be prefixed with the ms-appx-web schema. The loading of Google API will happen in the iFrame while the sensor processing will happen in the host app. We'll communicate between our app JS and the iFrame JS using HTML5 Web Messaging.
default.html map.html map.js var loc = new google.maps.LatLng(40.758692, -73.985341, true); //Times Square var pov = { heading: 0, pitch: 0, zoom: 0 }; var streetViewOptions = { position: loc, pov: pov, linksControl: false, panControl: false, zoomControl: false, disableDefaultUI: true, clickToGo: false, addressControl: false, scrollwheel: false }; var streetView = new google.maps.StreetViewPanorama(document.getElementById("map"), streetViewOptions); Once initialized, the full screen viewport will render a panorama of Times Square, New York.
Process ReadingChanged events for each sensor We have two options for getting readings from the sensors: build a "game loop" and periodically pole the sensors by calling getCurrentReading() or respond to the sensors readingchanged event. We'll do the latter and collect readings in arrays for later processing. When the readingchanged event fires it sends a specific class as the argument depending on the sensor type. For the Compass it's the CompassReadingChangedEventArgs, and for the Inclinometer we'll see InclinometerReadingChangedEventArgs. Both of these classes contain a reading property of type CompassReading or InclinometerReading, respectively. For the Compass we are interested in the readings headingMagneticNorth property and for the Inclinometer we are interested in pitchDegrees. We also want to record the timestamp in both cases. default.js var headings = new Array(); var pitches = new Array(); // listen to sensor readingchanged events // record the reading and the timestamp compass.addEventListener("readingchanged", function (args) { headings[headings.length] = { reading: args.reading.headingMagneticNorth, timeStamp: args.reading.timestamp }; }); inclinometer.addEventListener("readingchanged", function (args) { pitches[pitches.length] = { reading: args.reading.pitchDegrees, timeStamp: args.reading.timestamp }; }); Create a low-pass filter Sensor data can be chatty and noisy. Physically moving the Ultrabook can fire sensors every dozen milliseconds, and the magnitude of those readings can be surprisingly varied. To get a good reading that reflects the intention of the user, we'll pass this array of readings through a low-pass filter. Filtering the readings will reduce the noise and result in a less shaky experience with our viewport. There are many implementations of low-pass filtering, and we'll use one of the simplest for this example. Our low-pass filter takes two arguments: the array of reading/timestamp pairs and a smoothing factor. We arbitrarily use a smoothing factor of 2 that can be modified to increase/decrease the smoothing. I'm not going to explain the algorithm in detail, but I'll sum it up by saying it looks at the array and splits the difference between readings accounting for time between samples thus removing exaggerations and outliers.
default.js function lowPassfilter(readings, smoothing) { var value = readings[0].reading; var lastUpdate = readings[0].timeStamp; for (var i = 1; i < readings.length; ++i) { var now = readings[i].timeStamp; var currentValue = readings[i].reading; var elapsedTime = (now - lastUpdate) / 1000; var filteredValue = value + (currentValue - value) / (smoothing / elapsedTime); readings[i].reading = filteredValue; lastUpdate = now; value = filteredValue; } } Create a timer Timers in JavaScript are easy to create using setInterval(). We’ll use a timer to send messages to the viewport hosted in the iFrame. Once every second feels like a proper frame rate for a full-screen Google Street View app. Sending orientation data to the viewport at a faster rate than it can generate the images results in a loss of fidelity. You may see partially rendered views and/or image panels that don't line up properly. Play around with frame rates until you find one that best suits your needs. During every iteration of the timer, both reading arrays are processed through our filter, and the final result is sent on to the iFrame using HTML Web Messaging. Inside the iFrame our message is picked up, and the new orientation data is sent on to the Google API to render a new viewport.
default.js setInterval(function () { if (pitches.length > 0) { lowPassfilter(pitches, 2); var pitch = pitches[pitches.length - 1].reading; if (settings.walking) { if (pitch > 10) { iframe.postMessage("walk", "*"); } } else { iframe.postMessage("pitch:" + pitch, "*"); } } if (headings.length > 0) { lowPassfilter(headings, 2); iframe.postMessage("heading:" + headings[headings.length - 1].reading, "*"); } pitches = new Array(); headings = new Array(); }, 1000); map.js function receiveMessage(sender) { var opts = sender.data.split(":"); switch (opts[0]) { case "heading": pov.heading = parseFloat(opts[1]); streetView.setPov(pov); break; case "pitch": pov.pitch = parseFloat(opts[1]); streetView.setPov(pov); break; } } Performance / Results On reference Ultrabook hardware, the sensor readingchanged events fire at 16-ms intervals. This rate is much faster than the refresh rate of the viewport that is streaming image panels over the Internet from Google’s API. Observations indicate the readingchanged events are also queued. Attempting to perform viewport updates with each reading change has two undesirable effects: partial updates to the viewport resulting in image misalignment and continued viewport updates after physical orientation changes have stopped. Introducing a reading collection mechanism and passing readings through a low-pass filter combined with matching the viewport update interval to the observed frame rate mitigated these challenges. Conclusion Driving publically available APIs from Ultrabook sensors is straightforward in Windows 8. The Windows Runtime (WinRT) provides easy access to the sensors via the Windows.Devices.Sensors namespace.
Notices INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT. UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR. Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information. The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request. Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order. Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark* and MobileMark*, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products. Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license. Intel, the Intel logo, and Ultrabook are trademarks of Intel Corporation in the US and/or other countries. Copyright © 2012 Intel Corporation. All rights reserved. *Other names and brands may be claimed as the property of others.
You can also read