Итак, это третья часть моей попытки переосмыслить привычный поиск по картам. Первая часть тут, а вторая тут — они более технические, но пробежать глазами для лучшего понимания можно. Вкратце это звучит так: мне надоело ковыряться в картах за рулем, пытаясь среди мелких значков и рекламы найти ближайшую заправку. Вместо этого хотелось бы просто ехать, посматривая на экран приложения. Чтобы оно сортировало ближайшие места по времени езды, показывало списком, объясняло, какие из них по пути и какой к ним трафик. Такая вот идея.
Собственно, к версии приложения 3.0 наконец получилось реализовать все основные функции, которые хотелось. После прошлой статьи из этой серии его скачало некоторое количество людей, и даже написали отзывы — спасибо, ко всем прислушался. Работал над новой версией месяца два интенсивно, все мелкие изменения не перечислить — по сути это на 80% новое приложение. С кардинально улучшенным интерфейсом, раза в 2 быстрее и значительно стабильнее. Опять же, приглашаю сочувствующих оценить и поругать. А под катом снова технические моменты.
https://www.dchabanenko.com/city-scope — вот тут ссылки на айфон и андроид
Пробки
Одной из ключевых претензий к предыдущей версии приложения был неправильный расчет времени — я реализовал его своими силами через Open Source Routing Machine, и он считал чистое время дороги. В периоды минимального трафика (например, ночью) мои цифры совпадали с тем, что выдавали те же гугловские карты, но в большинстве случаев оценка были как минимум крайне оптимистична. Это нивелировало собственно сам смысл приложения, и надо было что-то придумать.
Пути решения здесь два: обратиться к стороннему апи или пытаться выкачать веса трафика откуда-то и импортировать к себе. Очень не хотелось от кого-то зависеть, поэтому какое-то время я потратил на поиск второго решения. Итоги оказались неутешительными: я нигде не нашел базу весов трафика с глобальным покрытием, которая была бы совместима с OpenStreetMap. Есть какие-то открытые базы по кускам Европы и Америки, которые в теории можно сшить с OSM через прыжки с бубном — но в итоге, поразмыслив, я решил не связываться. Конечно, возможность хостить у себя навигацию с трафиком прельщала, но отпугнули неполное покрытие, сложности с интеграцией, погрешности и сам факт, что трафик кеширован, а не в реальном времени. Короче, опять потратить кучу времени и получить мизер в итоге.
Осознав, что в наше время без апи большого дяди никуда, я принялся искать адекватного и дешевого дядю. Потыкавшись, я остановил свой выбор на сервисах HERE — это бывшие карты нокии, потом их забрал к себе майкрософт, оставив в виде отдельного подразделения. Как я понял, они сейчас в основном работают с корпоративными клиентами (например по вопросам логистики) и имеют достаточно вменяемое и чистое апи. А главное, у них есть глобальный реалтаймовый трафик и достаточно щедрые квоты. Неочевидный выбор, но я решил попробовать.
Интеграция в итоге оказалась довольно простой. Немалую роль здесь сыграла общая гибкость архитектуры, которую я наваял. При желании сейчас в нее несложно встроить хоть гугл, хоть яндекс пробки. Трафик из HERE я сделал отключаемым с фоллбэком в свою старую навигацию. Наконец, сравнивая значения без трафика (мои) и с трафиком, можно вывести общую оценку — пустая дорога, легкий трафик, средний и так далее.
Карты — список
Карты были второй главной претензией к приложению. Я угрохал какое-то невероятное количество времени на рисование собственных превьюшек маршрута и общей карты, и оно даже работало — но генерация растровых тайлов с модным дизайном колоссально просаживала сервер. Если в режиме тестирования картинки появлялись в пределах половины секунды, то с реальной аудиторией (даже небольшой), люди могли ждать до пяти или даже десяти (!!!!) секунд. Особенно, если кусок карты был крупный — тогда мой код пережевывал и рендерил кучу векторных данных. К тому же этот процесс особо не распараллелишь: там были узкие места, и все очереди все равно быстро забивались. В общем, печаль.
Что делать, было очевидно — выкинуть превьюшки карт к чертовой матери. Я долго боролся с собой (весь я же так долго их делал), однако в итоге принял волевое решение и, в общем, правильно сделал. Когда огромные и зачастую не грузившиеся картинки ушли, стало спокойнее и освободилась куча места на экране. Даже заполнив его новыми данными и увеличив надписи, я смог втиснуть в экран в два раза больше результатов, чем ранее.
Карты — новый режим
А вот с общей картой я поступил иначе. Никто (включая меня самого) не мог понять, зачем, собственно, она нужна. Она была самопальной, корявой, грузилась вечность и вообще изначально создавалась для дебага. Однако визуальное изображение изохроны почему-то продолжало мне греть душу и в итоге осталось занимать место в интерфейсе. Тогда я решил: а давай попробуем сделать из этой бессмысленной картинки полноценный альтернативный режим просмотра. Если кому-то не нравится список и он легче читает карту — почему бы и нет? Тем более у меня оставалась парочка идей, которым больше нигде не нашлось места.
Скрепя сердце, я выкинул свою картинку (стонущий от нагрузки сервак был благодарен за это). Вместо нее я встроил полноценную карту через плагин flutter_map — фоновые тайлы взял у Mapbox — и стал показывать на ней свой положение и точки результатов вокруг. Почти сразу всплыла необходимость эти точки кластеризовать, и я быстро набросал основанный на расстоянии код кластеризации. Он довольно примитивный, но 90% случаев покрывает. Под все это я опять подложил свою любимую зеленую кляксу изохроны. Наконец, легенда карты тоже стала интерактивной: тап по количеству результатов фокусирует карту на точках, а тап по времени — на изохроне. Довольно удобно.
Одной из идей, в которой я чувствовал ценность, но никак не мог ее сформулировать, было отображение текущего маршрута и вектора движения автомобиля. Я пытался влепить это в карточки маршрутов разными способами, но нигде оно не выглядело органично и на своем месте. И вот наконец, почти отчаявшись, я понял: новый режим карты идеален для этой фишки. Потому что в режиме списка я пишу напрямую текстом, по пути какое-то место или нет — а вот на карте это всегда непонятно. Даже у гугла или эппла ты наблюдаешь постоянно вертящийся сектор компаса и долгое время не понимаешь, в каком направлении ты едешь.
Воодушевленный, я сел работать. Пришлось попутно отрефакторить кучу кода, но спустя пару дней логика была готова. Я решил обновлять позицию не каждый 200 метров, как результаты, а гораздо чаще — через метров 10. Каждое обновление я пересчитываю вектор движения, и так он получается очень точным, поскольку зависит не от акселерометра, а от предыдущей позиции. Маршрут (то есть массив истории наших координат) я рисую на карте линией, а направление движение — стрелочкой. Все это обновляется почти в реальном времени, и вы даже не представляете, насколько карта преобразилась и стала удобней.
Отдельным нюансом стал факт, что в первые секунды получения локации gps еще калибруется. Замечали, как точка ползает по карте какое-то время вначале? С моей логикой эти фантомные перемещения сразу давали бы ложные выводы о направлении движения. А с учетом того, что ближайшее обновление результатов аж через 200 метров, это бы здорово дезинформировало водителя. Решил я эту проблему очень просто: до первого обновления делать вид, что мы стоим. То есть не показывать на карте ни стрелочку (хотя точка все равно ползает), ни маршрут. А разблокировать эти данные уже после, когда мы переместились на какое-то существенное расстояние, прошло секунд 5 и шансы получить ложную информацию практически нулевые.
Еще я добавил карточку, которая вылетает при выборе результата на карте. По сути это те же данные, что и в списке, но к ним добавлена превьюшка маршрута (да-да, она все-таки вернулась) и кнопка, запускающая навигацию.
Интерфейс
В целом интерфейс был основательно перекроен. Я не буду описывать, как переписал менюшки, заново отстроил цветовую палитру и все в таком духе. Сфокусируюсь на самых интересных моментах. Все надписи были пропорционально увеличены (мое падающее зрение тут пригодилось — увеличивал, пока сам с водительского кресла не увидел). Поменял шрифт на SF Pro Rounded — это закругленная вариация эппловского San Francisco. Качать тут, толковый шрифт. Настоятельно рекомендую в случаях, где у вас не сплошной текст, а крупные плашки, которые должны быть читаемы издалека.
После некоторых раздумий принял неочевидное для себя решение убрать фильтр “По пути”. Как и фильтр по времени, поначалу он казался чуть ли не основной функцией аппа. Однако в какой-то момент я понял, что им не пользуюсь. В режиме списка и так прекрасно видно, какие места по пути, а в режиме карты он и вовсе сбивает с толку. Я какое-то время бессмысленно таскал этот переключатель по интерфейсам, после чего просто скрыл его и ничего не потерял. Плюс чисто технически он плодил нюансы, которые было муторно и совершенно необязательно решать.
Данные
Собственно, главная на данный момент проблема приложения — данные. Я по-прежнему беру их из OSM со всеми сопутствующими проблемами: неравномерное покрытие, куча устаревших данных, отсутствие часов работы, телефонов и так далее. Мой бекенд построен так, что интегрировать в него любое стороннее апи мест очень просто — только вот где его взять? Первый (и самый лучший) кандидат это Google Places, но после недавнего удорожания на 1400% (господи) я пока их не могу себе позволить. Все остальные — TripAdvisor, Foursquare и им подобные — тоже или дорогие, или с корявым апи. Некоторые сервисы (тот же Mapbox или HERE) под видом своих данных предоставляют пережеванные места из OSM, которые у меня самого есть.
Из всей этой братии я решил попробовать прикрутить Yelp — он вроде недорогой и апи выглядело прилично. Я понимал, что это американский портал, соответственно, данных по другим частям света будет минимум, но это хотя бы какой-то прогресс. И выглядело все поначалу неплохо: я за пару часов все интегрировал, и даже почитал, что они заявляют покрытие чуть ли не половины мира. Однако не успел я обрадоваться, как начался какой-то цирк. Огромное количество мест в их данных имело неправильные координаты. Очевидно, они вводили места не через точки, а через адреса — и их геокодер расставлял координаты произвольно. В результате у меня какая-нибудь заправка находится в правильном месте, но кроме адреса почти не имеет данных; а у них есть и отзывы, и рейтинг, и часы работы — только вот координаты вообще какие-то левые. Единственные свойства, по которым наши данные можно свести, это имя и адрес. Причем зачастую и то, и то написано произвольно, с ошибками, неправильно форматировано и тд. Я попробовал сличать их через свой геокодер + fuzzy matching, и, в принципе, это сработало — хотя какой-то процент мест мы таким образом все же теряем.
Но это оказалось лишь первой проблемой. Дальше посыпались остальные: у них очень нестабильный поиск по радиусу, куча багов (если почитать коменты людей в их гите, то там вообще ничего не работает) и так далее. В конце концов я проверил требования по брендингу — оказывается, ты обязан использовать их красные (!!!) звезды для рейтинга мест. Посмотрев, как это выглядит в моем интерфейсе, я плюнул и весь этот балаган отключил.
В итоге, с данными прогресса особо нет. Собственно, единственное, что можно (и очень легко) сделать — это прикрутить Google Places. Там все отлично и с покрытием, и с координатами. Только вот это стоит теперь очень больших денег. Поэтому прошу вашего мнения: как бы вы отнеслись к платной подписке? В бесплатной версии было бы как сейчас, а за какую-то символическую сумму в месяц были бы доступны данные гугла или например яндекса (надо почитать, сколько у них стоит). Так я, наверное, не разорюсь.
Итог
В общем, я недавно прикинул: почти полтора года у меня заняло это приложение. Конечно, не все мое свободное время я на него тратил, но большую часть точно. Пару раз бросал на месяц, иногда казалось, что никогда не доделаю — но вот все-таки доделал. И, в принципе, сейчас оно выглядит именно так, как я хотел с самого начала. По пути освоил не одну новую технологию и получил очень много опыта. В общем, было интересно. Теперь я был бы рад, если бы результат кому-то пригодился.
P.S.
На правах саморекламы: в промежутке сделал для себя другое приложение, совсем маленькое — помощник при парковке. Я не перевел его на русский, но там однокнопочный интерфейс. Может, будет кому-то интересно.
Автор: dchabanenko