Несколько дней назад я как раз вспомнил о поездках, которые совершил в прошлом году. Я никогда раньше не бывал в действительно хороших городах. Так мне в голову пришла идея немного поиграть с библиотеками карт.
Я знал несколько классных библиотек карт для React, но никогда не использовал их вместе с инструментами визуализации данных. С этими целями я решил начать небольшой проект, в основном используя react-map-gl
и deck.gl
от Uber. На данный момент у меня есть инструменты, но… какие данные я могу использовать и откуда их взять? 🤔
Я давно использую историю местоположений Google, поэтому я знал, что у меня есть данные о моих поездках на их платформе. Что было новым для меня, так это способ извлечения этих данных, поскольку у них нет для этого API. Единственный способ, который я нашел, - это загрузка всех данных о моем местоположении с помощью их проекта по освобождению данных под названием Google Takeout. С его помощью вы можете загружать собственные данные о некоторых продуктах Google. В моем случае мне просто нужна история местоположений для этого проекта. В результате получился JSON-файл размером 32 МБ, который невозможно использовать для простого проекта без его размещения в базе данных.
Я решил упростить данные о местоположении и загрузить их непосредственно в проект, избегая использования каких-либо внешних сервисов для их запроса. Внутри файла JSON у меня есть массив объектов со свойствами, такими как долгота, широта и временные метки, как показано в следующем фрагменте, поэтому я получил все, что мне нужно, чтобы начать кодирование.
{ "timestampMs" : "1514242208538", "latitudeE7" : 378673894, "longitudeE7" : -47359558, "accuracy" : 19, "altitude" : 177, "verticalAccuracy" : 2, "activity" : [ { "timestampMs" : "1514242476975", "activity" : [ { "type" : "STILL", "confidence" : 100 } ] } ] }
Давайте углубимся в технологии
Я начал простой проект реагирования, используя create-react-app
инструмент. Для сложных проектов у меня есть собственный шаблон с моими настраиваемыми конфигурациями, касающимися Redux или маршрутизации. В подобных случаях я предпочитаю использовать create-react-app
, так как не хочу беспокоиться о конфигурациях, подобных тем, которые используются для Webpack или Babel, а проект настолько прост, что я m не собираюсь использовать какие-либо другие распространенные библиотеки.
Имея свое базовое приложение в качестве отправной точки проекта, я удалил некоторые элементы шаблона, такие как примеры компонентов и стилей, которые я получил как результат create-react-app
. Затем я установил react-map-gl
библиотеку, чтобы начать рендеринг некоторых карт. В процессе настройки мне пришлось создать бесплатный проект на веб-сайте Mapbox, чтобы получить токен доступа, поскольку от него зависит библиотека Uber.
react-map-gl
имеет простую документацию, поэтому я легко смог протестировать большинство компонентов и стили. Но в конце я использовал несколько базовых стилей и только дополнительный компонент для управления навигацией. Итак, функция render
возвращает следующее:
return ( <div> <ReactMapGL {...viewport} mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN} mapStyle="mapbox://styles/mapbox/dark-v9" onViewportChange={vp => this.onViewportChange(vp)} > <Controller onViewportChange={vp => this.onViewportChange(vp)} /> </ReactMapGL> </div> )
Последним и самым интересным шагом была визуализация данных с помощью библиотеки Deck.gl. У меня были все данные и карта, поэтому я с нетерпением ждал, когда мои данные будут отображены поверх них. Документы Deck.gl сложнее, чем использовавшаяся ранее библиотека карт, поэтому я просто просмотрел вкладку "Начало работы", поскольку мне не нужны были какие-либо настраиваемые слои или интерактивность.
Для всех точек расположения я использовал класс GridLayer
, так как он был лучшим в соответствии с моими потребностями. Производительность оказалась лучше, чем я ожидал, поскольку я рендерил более 60 тыс. Точек местоположения, прочитанных непосредственно из файла JSON. Чтобы реализовать это, нужно добавить только один компонент к ReactMapGL
, и он выглядит так:
<DeckGL {...viewport} layers={[ new GridLayer({ id: "grid-layer", data: showLocations ? gridData : [] }), ]} />
И карта отрисована…
На карте просматривается множество точек, поэтому поездки было легко идентифицировать, но… как насчет полетов? ✈️
Для этого потребовались дополнительные вычисления. Изучив данные, я увидел, что Google сохраняет большинство точек местоположения с помощью свойства активности, которое представляет собой массив с различными типами активности и точками уверенности для каждого из них. Таким образом вы сможете рассчитать, как вы двигаетесь во время путешествия. Это действительно полезно, если я хочу отображать свои автомобильные или железнодорожные поездки на другом слое, но не о рейсах. Я думаю, что в моем случае это было проще, так как я выключаю свой мобильный телефон или включаю режим полета во время полета, поэтому точки местоположения не должны отображаться.
Чтобы отобразить мой слой с рейсами, я предположил, что две последовательные точки местоположения в моей истории местоположений с разрывом более 300 км будут считаться полетом.
Давай попробуем! 💪 Чтобы сравнить две последовательные точки с точки зрения расстояния, мне нужно было преобразовать две пары широты и долготы в расстояние в км. Это был момент для использования формулы Хаверсина, которая определяет расстояние между двумя точками на сфере с учетом их долготы и широты. Реализация javascript:
function calcDistance(latitude1, longitude1, latitude2, longitude2){ const R = 6371 // km const dLat = toRad(latitude2 - latitude1) const dLon = toRad(longitude2 - longitude1) const lat1 = toRad(latitude1) const lat2 = toRad(latitude2) const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2) const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) return R * c } // Converts numeric degrees to radians function toRad(Value) { return Value * Math.PI / 180 }
Как видите, я также создал функцию toRad
, которая просто преобразует градусы в радианы. Функция calcDistance
использовалась функцией getFlightsTrips
, которая имеет в качестве параметра данные locationHistory и возвращает массив объектов с информацией о рейсах.
После этих расчетов данные, которые я получил, казались правильными, так что настал момент их показать. С этой целью я решил добавить новый слой типа ArcLayer
. Идеально подходит для рендеринга полетов, так как соединяет пару точек дугами.
В совокупности результат был ожидаемым. Полеты отображались правильно, поэтому предположения соответствовали моим данным. Вот как это выглядит после добавления последнего слоя:
С таким результатом я считал проект завершенным. Библиотеки, которые я использовал, действительно хороши и для более сложных проектов, поэтому я обязательно сохраню их в своем списке на будущее. Я разместил демо-версию этого проекта в своем профиле на github, так что не стесняйтесь посещать его и оставлять комментарии!