Перелистывая хаб «Разработка под Java ME» наткнулся на тему Spb Transport J2ME, где автор использует картографические сервисы, и одним из TODO является поддержка GPS (для улучшения юзабилити). Проблема в том что телефонов с встроенным GPS-приемником относительно небольшое количество. Надеюсь данным постом помогу не только автору той темы, но и кому то еще, сам в свое время набил немало шишек. Итак, приступим.
Чтобы определить местоположение пользователя (��елефона, как вам угодно), можно использовать несколько способов:
— по GPS. Способ наиболее точный. Из недостатков: относительно долгий старт, потребляет много энергии, не так уж много аппаратов с встроенным приемником.
— по вышкам оператора. Средний по точности. Энергии кушает немного. Из минусов: не на всех телефонах доступны данные.
— по IP. Наименее точный. Собственно это самый большой минус.
— по CB-сообщениям оператора
Итак, нам нужна более-менее приемлемая точность при определении местоположения (для города это примерно 150-300 метров (это где то 1,5-2 минуты пешего хода), за городом соответственно 2-5 км и более, как повезет), так же нам необходимо охватить как можно большее количество аппаратов, и неплохо было бы оперативно обновлять координаты.
Наиболее подходящим будет определение местоположения через данные сотового оператора.
У каждого из сервисов есть неплохо документированное API (кроме GoogleMaps). Параметры, принимаемые API: MCC (код страны), MNC (код оператора сети), LAC (код соты), CellID (идентификатор вышки).
API возвратит координаты для данного набора данных.
Существует так же ряд других неприятных моментов. К примеру на Siemens'ах данные сети без патчинга прошивки получить не получиться. SonyEricsson возвращает данные в HEX-представлении. Nokia отказывается выдавать LAC несертифицированным (то-есть почти всем) мидлетам.
В итоге есть возможность определить местоположение на телефонах Siemens, SonyEricsson, Samsung (к примеру s5230), Huawei и прочих. Время загрузки координат и адресов примерно секунд 10-15.
Ну и исходники с демо
goo.gl/lPkON
Чтобы определить местоположение пользователя (��елефона, как вам угодно), можно использовать несколько способов:
— по GPS. Способ наиболее точный. Из недостатков: относительно долгий старт, потребляет много энергии, не так уж много аппаратов с встроенным приемником.
— по вышкам оператора. Средний по точности. Энергии кушает немного. Из минусов: не на всех телефонах доступны данные.
— по IP. Наименее точный. Собственно это самый большой минус.
— по CB-сообщениям оператора
Итак, нам нужна более-менее приемлемая точность при определении местоположения (для города это примерно 150-300 метров (это где то 1,5-2 минуты пешего хода), за городом соответственно 2-5 км и более, как повезет), так же нам необходимо охватить как можно большее количество аппаратов, и неплохо было бы оперативно обновлять координаты.
Наиболее подходящим будет определение местоположения через данные сотового оператора.
Сервисы:
Чтобы сконвертировать данные сети и получить координаты, нам понадобятся базы данных вышек сотовых операторов. В сети существует немало ресурсов, я использовал проверенные временем GoogleMaps, Yandex.Locator, location-api.com и opencellid.org (для большей точности и надежности используем все сразу). плюс как бонус у Яндекса в API есть метод определения местоположения с учетом силы сигнала, но о этом позже.У каждого из сервисов есть неплохо документированное API (кроме GoogleMaps). Параметры, принимаемые API: MCC (код страны), MNC (код оператора сети), LAC (код соты), CellID (идентификатор вышки).
API возвратит координаты для данного набора данных.
Данные:
Получить вышеназванные данные задача нетривиальная, ведь каждый из производителей посчитал делом чести изобрести свой велосипед. В результате для каждой марки существует свой набор ключей для вызова System.getProperty(key), отыскать которые не так то легко.Существует так же ряд других неприятных моментов. К примеру на Siemens'ах данные сети без патчинга прошивки получить не получиться. SonyEricsson возвращает данные в HEX-представлении. Nokia отказывается выдавать LAC несертифицированным (то-есть почти всем) мидлетам.
Решение:
Я написал класс, перебирающий известные ключи и получающий по этим ключам данные о сети. Потом ключи отправлялись в API сервисов, получались координаты и выводилось среднее значение, которое я считаю наиболее правдоподобным (за год использования не припомню случаев очень больших ошибок). Если телефон позволяет получить силу сигнала, мы используем бонус Яндекса: получаем координаты С учетом силы сигнала и БЕЗ, получаем дельту этих значений, применяем ее ко всем результатам от API, выводим средний результат. Как ни странно, последнее решение оказалось палкой о двух концах. При равномерном затухании сигнала точность по сравнению с обычным способом увеличивается раза в два, но если это плотная городская застройка или холмистая местность, где сигнал распространяется неравномерно, в этом случае точность падает достаточно сильно.В итоге есть возможность определить местоположение на телефонах Siemens, SonyEricsson, Samsung (к примеру s5230), Huawei и прочих. Время загрузки координат и адресов примерно секунд 10-15.
Пример из демо (используется надстройка класс Location, в котором происходит определение координат и загрузка для них адреса)
import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Form; import javax.microedition.midlet.MIDlet; import loc.*; public class HelloWorld extends MIDlet implements Runnable { Display display; Form form; public void startApp() { display = Display.getDisplay(this); form = new Form("NetMonitor"); display.setCurrent(form); new Thread(this).start(); } public void run() { //проверяем не Нокиа ли if (!Location.reallyNull(Location.lac)) { //обновляем данные сети Location.getData(); //получаем координаты Location.getCoordinates(); //если есть доступ к силе сигнала if (!Location.reallyNull(SystemUtil.signal())) { form.append(("Нетмонитор: ") + "\nКод страны: " + String.valueOf(Location.mcc) + " \nКод сети: " + String.valueOf(Location.mnc) + " \nКод соты: " + String.valueOf(Location.lac) + " \nКод БC: " + String.valueOf(Location.cid) + " \nСила сигнала: " + SystemUtil.signal() + " \n"); } //иначе else { form.append("Нетмонитор: " + "\nКод страны: " + String.valueOf(Location.mcc) + " \nКод сети: " + String.valueOf(Location.mnc) + " \nКод соты: " + String.valueOf(Location.lac) + " \nКод БС: " + String.valueOf(Location.cid) + " \n"); } } //прочие данные от телефона String txt = SystemUtil.nativeDigitSupport(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.operatorName(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.serviceProvider(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.traffic(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.gid1(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } txt = SystemUtil.gid2(); if (!Location.reallyNull(txt)) { form.append(txt + "\n"); } //геоданные form.append("Улица: ".concat(String.valueOf(Location.getStreet().concat(" \n")))); form.append("Город: ".concat(String.valueOf(Location.getCity().trim().concat(" \n")))); form.append("Область: ".concat(String.valueOf(Location.getArea().concat(" \n")))); form.append("Страна: ".concat(String.valueOf(Location.getCountry().concat(" \n")))); form.append("Долгота: ".concat(String.valueOf(Location.getLongitude().concat(" \n")))); form.append("Широта: ".concat(String.valueOf(Location.getLatitude().concat(" \n")))); form.append("Высота над у.м.: ".concat(String.valueOf(Location.getElevation().concat(" м \n")))); //для спотсменов бонус String sens = System.getProperty("microedition.sensor.version"); if (sens != null && sens.length() != 0 && !sens.equals("null")) { sens = SensorApi.getSensor(3); if (sens != null && sens.length() != 0 && !sens.equals("null") && !sens.equals("0")) { form.append("Сегодня пеших шагов: " + String.valueOf(sens) + " \n"); } } } public void pauseApp() { } public void destroyApp(boolean flag) { } }
Ну и исходники с демо
goo.gl/lPkON