В Твиттере Снехал обратился ко мне с интересным вопросом. Учитывая местоположение X, он хотел отслеживать местоположение пользователя и знать, когда он находился на определенном расстоянии от X. Само по себе это не очень сложная задача. Тебе нужно:
- Отслеживайте местоположение пользователя — что легко с геолокации и интервалом.
- Получить расстояние от местоположения пользователя до вашей цели, что также легко. (Хорошо, я лгу. Это сумасшедшая математика, но вы можете скопировать и вставить решение, так что давайте назовем это легко.)
- Скажите пользователю, когда они рядом — опять легко.
Что было не особенно легко для меня, чтобы обернуть голову, как построить это в Ionic, или, в частности, в Angular. Как я уже неоднократно говорил, я могу писать на Angular, но я все еще борюсь с тем, как лучше организовать и скоординировать различные аспекты моего приложения. В этом случае меня особенно смутило то, как я буду обрабатывать интервальный процесс. Мне также нужно что-то, что будет работать все время, а не только для определенного представления / контроллера.
Я застрял — но потом подумал — если я знаю, что, возможно, я сделаю это неправильно, позвольте мне сделать удар, и пусть мои умные читатели скажут мне, что я сделал неправильно.
Я начал с создания нового приложения Ionic. Я позволил ему использовать шаблон по умолчанию Tabs, поэтому у меня было бы «настоящее» приложение с несколькими представлениями. Затем я создал новый сервис в services.js под названием GeoAlert. GeoAlert будет иметь простой API:
- begin: Это инициировало бы отслеживание и передавало бы целевое местоположение и обратный вызов, чтобы выстрелить, когда пользователь «достаточно близко». В итоге я жестко запрограммировал, что такое «достаточно близко», но это тоже могло быть аргументом. То же самое, как часто он проверял местоположение.
- конец: это просто останавливает отслеживание.
- setTarget: метод, который я создал и отказался, но я подумал, что он имеет смысл, поэтому я оставил его. Это позволяет вам изменить цель.
Вот мой сервис:
.factory('GeoAlert', function() { console.log('GeoAlert service instantiated'); var interval; var duration = 6000; var long, lat; var processing = false; var callback; var minDistance = 10; // Credit: http://stackoverflow.com/a/27943/52160 function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) { var R = 6371; // Radius of the earth in km var dLat = deg2rad(lat2-lat1); // deg2rad below var dLon = deg2rad(lon2-lon1); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(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; // Distance in km return d; } function deg2rad(deg) { return deg * (Math.PI/180) } function hb() { console.log('hb running'); if(processing) return; processing = true; navigator.geolocation.getCurrentPosition(function(position) { processing = false; console.log(lat, long); console.log(position.coords.latitude, position.coords.longitude); var dist = getDistanceFromLatLonInKm(lat, long, position.coords.latitude, position.coords.longitude); console.log("dist in km is "+dist); if(dist <= minDistance) callback(); }); } return { begin:function(lt,lg,cb) { long = lg; lat = lt; callback = cb; interval = window.setInterval(hb, duration); hb(); }, end: function() { window.clearInterval(interval); }, setTarget: function(lg,lt) { long = lg; lat = lt; } }; })
Итак, несколько замечаний по этому поводу. Поскольку Geolocation является асинхронным, я использовал переменную processing, чтобы определить, когда она была активна, чтобы моя функция сердцебиения (hb) не запрашивала ее снова. Я мог бы также переключиться с setInterval на setTimeout. Я бы просто вызвал setTimeout в функции успеха запроса геолокации. Я не обязательно уверен, что это имеет большое значение, но это то, что я мог бы изменить в будущем. Как уже упоминалось, логику «как далеко» я просто вырезал и вставил из хорошего ответа StackOverflow . Как я уже говорил выше, интервал и минимальное расстояние для матча (10 километров) жестко заданы, но вместо этого они могут быть аргументами.
На данный момент у меня есть служба, которая в основном будет запускаться каждые N секунд и определять, когда я нахожусь в пределах X километров от цели. Как мне это использовать?
Я решил ввести сервис по run
методу моего основного модуля Angular. Я не знаю, почему было странно работать там, но это было так. Обычно я использую сервисы в своих контроллерах, но, очевидно, вы можете использовать их и здесь. Черт, Ионик делает это с помощью сервиса $ ionicPlatform. Что касается того, как сообщить пользователю, что что-то произошло, у меня было несколько вариантов. Я мог бы использовать Ionic Modal или Popup, но они чувствовали себя неправильно со мной. У меня нет веских причин, почему они чувствовали себя неправильно, но я решил использовать плагин Dialogs. Очевидно, это личный выбор. Я думал, что диалог позволит пользователю узнать, что он близок к некоторой цели, и даст ему возможность просмотреть информацию об этой цели. Для моей демонстрации я просто записываю, какая кнопка была нажата, и оставляю ее реализацию читателю. Вот код
.run(function($ionicPlatform, GeoAlert) { $ionicPlatform.ready(function() { // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard // for form inputs) if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) { cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); } if (window.StatusBar) { // org.apache.cordova.statusbar required StatusBar.styleLightContent(); } //Begin the service //hard coded 'target' var lat = 30.224090; var long = -92.019843; function onConfirm(idx) { console.log('button '+idx+' pressed'); } GeoAlert.begin(lat,long, function() { console.log('TARGET'); GeoAlert.end(); navigator.notification.confirm( 'You are near a target!', onConfirm, 'Target!', ['Cancel','View'] ); }); }); })
Довольно просто, я думаю. В качестве краткого примечания, индекс кнопки, передаваемый onConfirm, основан на 1, что хорошо, но не забывайте. Для тестирования я запустил свой iOS Simulator. То, что вы можете не знать, это то, что он позволяет вам изменить свое местоположение Это можно найти в меню «Отладка».
Обратите внимание, что у него есть пункт «Пользовательское местоположение». Вы можете выбрать это, ввести местоположение, и оно запомнит это. Я ввел свои значения (которые совпадают со статическими значениями в коде), и на следующей итерации сердечного ритма это совпадало:
Если вы хотите поиграть с этим, ознакомьтесь с полным исходным кодом здесь: https://github.com/cfjedimaster/Cordova-Examples/tree/master/geoalert .