В этой статье мы покажем вам, как вы можете использовать API геолокации W3C для измерения скорости и курса вашего автомобиля во время вождения. Эта статья далее использует SVG для рендеринга датчика скорости и курса.
В этой статье мы создадим следующее:
Здесь вы можете увидеть два датчика. Один покажет курс, по которому вы едете, а другой — скорость в километрах. Вы можете проверить это самостоятельно, используя следующую ссылку: Откройте это в устройстве включения GPS . После открытия браузер, вероятно, попросит вас разрешить доступ к вашему местоположению. Если вы включите это и начнете двигаться, вы увидите, что оба датчика движутся соответствующим образом.
Чтобы заставить все это работать на самом деле очень легко и состоит из следующих шагов:
- Измените изображения SVG, чтобы мы могли вращать иглу и добавлять на страницу.
- Используйте API геолокации, чтобы определить текущую скорость и курс
- Обновить иглу на основе текущего и предыдущего значения
Начнем с части SVG.
Измените изображения SVG, чтобы мы могли вращать иглу и добавлять на страницу
Для изображений я решил использовать SVG. Преимущество SVG заключается в том, что он может масштабироваться без потери деталей, и вы можете легко манипулировать и анимировать различные части изображения SVG. Оба изображения SVG были скопированы с openclipart.org :
Это векторная графика, обе созданы с помощью иллюстратора. Прежде чем мы сможем повернуть иглы на этих изображениях, нам нужно внести пару небольших изменений в код SVG. С SVG вы можете применять матричные преобразования к каждому элементу SVG, с этим вы можете легко вращать, наклонять, масштабировать или переводить компонент. Помимо преобразования матрицы вы также можете применять вращение и перевод напрямую, используя ключевые слова translate и rotate. В этом примере я использовал функции translaten и rotate напрямую.
При работе с этими функциями необходимо учитывать, что функция вращения не вращается вокруг центра компонента, она вращается вокруг точки 0,0. Поэтому мы должны убедиться, что для наших игл точка, которую мы хотим вращать, установлена на 0,0. Не вдаваясь в подробности, я удалил две иглы с изображения и добавил их как отдельную группу в изображение svg. Затем я убедился, что иглы, которые мы нарисовали, относительно точки 0,0, которую я хотел вращать. Для спидометра стрелка теперь определяется как это:
<g transform="translate(171,157) rotate(45)" id="speed"> <rect y="0" x="-2.5" height="100" width="5" id="rect5532" style="fill:url(#linearGradient5547);fill-opacity:1;stroke:none" /> </g>
А для компаса стрелка определяется так:
<path transform="translate(225,231) rotate(135)" sodipodi:nodetypes="ccccc" id="compass" d="M -4.21,88 L -4.21,-88 C -2.77,-72 -1.93,-35 4.21,-20 L -4.21,-21 L -4.21,88 z" style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round; stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
Если вы знаете, как читать SVG, вы можете видеть, что эти фигуры теперь нарисованы вокруг их точки вращения (нижний центр спидометра и центр компаса). Как видите, мы также добавили конкретный идентификатор для обоих этих элементов. Таким образом, мы можем ссылаться на них непосредственно из нашего javascript позже и обновлять свойство transform из анимации jquery.
Далее нам просто нужно добавить их на страницу. Для этого я использовал d3.js , который имеет все виды вспомогательных функций для SVG и который вы можете использовать для загрузки этих элементов следующим образом:
function loadGraphics() { d3.xml("assets/compass.svg", "image/svg+xml", function(xml) { document.body.appendChild(xml.documentElement); }); d3.xml("assets/speed.svg", "image/svg+xml", function(xml) { document.body.appendChild(xml.documentElement); }); }
И с этим у нас есть готовые компоненты визуализации.
Используйте API геолокации, чтобы определить текущую скорость и курс
Следующим шагом является использование API геолокации для доступа к свойствам скорости и курса. Вы можете получить эту информацию из объекта позиции, предоставленного вам этим API:
interface Position { readonly attribute Coordinates coords; readonly attribute DOMTimeStamp timestamp; };
Этот объект имеет объект Coordinate, который содержит информацию, которую мы ищем:
interface Coordinates { readonly attribute double latitude; readonly attribute double longitude; readonly attribute double? altitude; readonly attribute double accuracy; readonly attribute double? altitudeAccuracy; readonly attribute double? heading; readonly attribute double? speed; };
Много полезных атрибутов, но нас интересуют только последние два. Направление (от 0 до 360) показывает направление, в котором мы движемся, а скорость в метрах в секунду, как вы, наверное, догадались, равна скорости, с которой мы движемся.
Есть два разных варианта получения этих значений. Мы можем опросить сами эти значения (например, setInterval) или подождать, пока наша позиция. Во втором случае вы автоматически получаете обновление. В этом примере мы используем второй подход:
function initGeo() { navigator.geolocation.watchPosition( geosuccess, geofailure, { enableHighAccuracy:true, maximumAge:30000, timeout:20000 } ); //moveSpeed(30); //moveCompassNeedle(56); } var count = 0; function geosuccess(event) { $("#debugoutput").text("geosuccess: " + count++ + " : " + event.coords.heading + ":" + event.coords.speed); var heading = event.coords.heading; var speed = event.coords.speed; if (heading != null && speed !=null && speed > 0) { moveCompassNeedle(heading); } if (speed != null) { // update the speed moveSpeed(speed); } }
С этим фрагментом кода мы регистрируем функцию обратного вызова в watchPosition. Мы также добавим пару свойств в функцию watchPosition. С помощью этих свойств мы сообщаем API использовать GPS (enableHighAccuracy) и устанавливаем некоторые значения времени ожидания и кэширования. Всякий раз, когда мы получаем обновление от API, вызывается функция geosuccess. Эта функция получает объект позиции (показанный ранее), который мы используем для доступа к скорости и курсу. На основании значения курса и скорости мы обновляем компас и спидометр.
Обновить иглу на основе текущего и предыдущего значения
Чтобы обновить иглы, мы используем анимации jquery для облегчения. Обычно вы используете анимацию jquery для анимации свойств объекта css, но вы также можете использовать это для анимации произвольных свойств. Для анимации спидомера мы используем следующее:
var currentSpeed = {property: 0}; function moveSpeed(speed) { // we use a svg transform to move to correct orientation and location var translateValue = "translate(171,157)"; // to is in the range of 45 to 315, which is 0 to 260 km var to = {property: Math.round((speed*3.6/250) *270) + 45}; // stop the current animation and run to the new one $(currentSpeed).stop().animate(to, { duration: 2000, step: function() { $("#speed").attr("transform", translateValue + " rotate(" + this.property + ")") } }); }
Мы создаем пользовательский объект currentSpeed с одним свойством. Это свойство устанавливается на коэффициент поворота, который отражает текущую скорость. Далее это свойство используется в анимации jquery. Обратите внимание, что мы прекращаем любые существующие анимации, если мы получим обновление, когда текущая анимация все еще работает. В свойстве шага анимации мы устанавливаем значение transfrom элемента SVG. Это приведет к повороту стрелки в течение двух секунд от старого значения к новому.
И чтобы оживить компас, мы делаем то же самое:
var currentCompassPosition = {property: 0}; function moveCompassNeedle(heading) { // we use a svg transform to move to correct orientation and location var translateValue = "translate(225,231)"; var to = {property: heading}; // stop the current animation and run to the new one $(currentCompassPosition).stop().animate(to, { duration: 2000, step: function() { $("#compass").attr("transform", translateValue + " rotate(" + this.property + ")") } }); }
Есть небольшая ошибка, с которой я столкнулся при этой настройке. Иногда мой телефон терял свой сигнал GPS (работает под управлением Firefox Mobile), и это останавливало движение дисков Обновления веб-страницы было достаточно, чтобы все началось снова. Я мог бы изменить это, чтобы активно извлекать информацию, используя вызов API getCurrentLocation, чтобы увидеть, работает ли это лучше.
Другая проблема заключается в том, что, по крайней мере, я нашел, что вы не можете отключить телефон, переходящий в спящий режим из браузера. Поэтому, если вы не настроите свой телефон так, чтобы он не ложился спать, экран станет черным.