В Твиттере Снехал обратился ко мне с интересным вопросом. Учитывая местоположение 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 .

