Мой навигатор решает эту проблему. Строит маршруты, которые длиннее процентов на 20, чем кратчайшие, зато в разы интереснее.
Долгое время сервис жил в виде веб-версии, но пользователи настойчиво просили Android-приложение. Так что пришлось им наконец заняться.
Алгоритм поиска путей
Сперва пара слов о базовой идее. Алгоритм работы у сервиса такой:
1. Парсим Open Street Map
2. Выдираем оттуда объекты, представляющие потенциальный интерес у туристов, используя теги для фильтрации. Например, здания с тегом historic или tourism, объекты с тегом park или garden и т.п.
3. Считаем зоны видимости объектов. Используется простой трассировщик лучей, нужно это так как в городе часто здания загораживают друг друга, и с улицы может быть плохо видно стоящее в глубине двора историческое здание
4. Берем популярную библиотечку для поиска путей GraphHopper
5. В областях видимости меняем веса ребер навигационного графа, чтобы маршрутам было выгодно заходить в эти области, а не вести напрямик.
Более подробное описание алгоритма можно найти на Хабре.
После того, как базовая веб-версия была готова, встал вопрос о создании мобильного приложения. Заниматься им, честно говоря, не очень хотелось - опыта в Android у меня не так много. Поначалу я пытался отделаться экспортом построенных маршрутов в KML файл - такие файлы умеют открывать многие популярные приложения с картами. Однако потом все-таки начал пилить свое.
Osmdroid - тормоза и фризы
Сперва встал вопрос выбора картографического движка. Поскольку данные я беру из Open Street Map, логично использовать и их же карты.
По запросу "OSM Android SDK" первым делом выдает библиотечку osmdroid, которая обещает быть полной заменой стандартного гуглового MapView. Поначалу все было хорошо, встроить по туториалам его удалось достаточно быстро.
Но почти сразу проявились проблемы с производительностью. Сама по себе карта работала довольно шустро, однако стоило добавить на нее хотя бы пару маркеров или линий - и все начинало ощутимо лагать при скролле. В трекере мы нашли несколько багов, такая проблема воспроизводилась много у кого, но разработчики по-видимому не могли или не хотели уделить ей время.
Поскольку для нашего приложения отображение маркеров очень важно - пришлось искать альтернативу.
Mapbox
Вторым вариантом стал SDK MapBox. У них там есть куча платных фич, однако само использование MapView в своем приложении бесплатно (платно - всякие API типа роутинга, но он у нас свой собственный).
Пришлось немного подхачить отображение маршрутов. Так как их карта ожидает объект в том формате, который возвращает их API, а мы его не используем. Пришлось поковырять структуру их JSON, опытным путем выкинуть все лишнее и завернуть наш собственный маршрут в эту структуру.
В итоге приложение стало выглядеть вот так. Пример показывает, как прямому маршруту по Суворовскому проспекту (он идет под надписью "Пески") приложение предпочло крюк через Таврический сад, где парк и больше всяких памятников и красот:
Немного про UI
На самом деле, работа с картой в таком приложении - самое простое. До меня уже несколько человек пытались на энтузиазме запилить приложение под наше открытое API. Как правило, энтузиазма хватало как раз сделать карту и отображение маршрутов, все ломались на UI. Потому что даже такое вроде бы простое приложение содержит довольно много экранов, форм, поп-апов и прочей скучнейшей и нуднейшей в разработке мутотени.
Вот так выглядит экран прокладки маршрута. Тут надо собрать начальную и конечную точки, сделать возможным добавление и удаление промежуточных точек, причем все это должно работать в двух вариантах - выбором на карте и вводом адреса. Попутно на сервер должны улетать запросы с геокодированием (преобразованием координат в адрес и обратно), с проверкой точек (не выходят ли они за пределы зоны, в которой поддерживается поиск пути) и т.п
И да, кроме прямых маршрутов (между несколькими точками) есть еще и круговые - с возвратом в стартовую точку за указанное время. Такие маршруты хорошо подходят, если не хочется уходить далеко от какого-то места (отеля, вокзала или просто места встречи с друзьями), есть пара свободных часов и хочется немного погулять.
Но для них - свой набор входных данных и своя форма ввода. Там пользователи хотят указывать область, внутри которой нужно искать достопримечательности, и время.
Пример кругового маршрута по двум паркам на час с небольшим вокруг станции метро "Парк Победы" в Питере:
В Android есть типичная проблема - Activity классы быстро превращаются в этакий God-object, с тысячами строк кода и кучей логики вперемешку с отображением. Бороться с этим призваны всякие хитрые варианты архитектуры. В своем приложении я использовал MVP с помощью библиотечки Moxy. Вкратце - приложение делится на Model (данные), View (отображение, собственно Activity) и Presenter (логика, вынесенная в отдельный класс и работающая с View через интерфейс). На самом деле довольно очевидная вещь, из тех что ты сперва начинаешь сам изобретать, а потом узнаешь что оказывается у нее уже есть какое-то умное название.
Про Moxy есть хорошая статья на Хабре. Главный профит этой библиотечки - что она берет на себя заботу о сохранении состояния приложения и обработку всяких смен ориентации и прочих причин пересоздания Activity. Presenter по сути заполняет очередь команд (на основе бизнес-логики нарисовать то-то там-то, отобразить такой-то поп-ап), которая при необходимости проигрывается заново если Activity была пересоздана.
Заключение
Приложение мы пилили без малого год и все еще продолжаем его допиливать. Работает оно бесплатно и без рекламы, взять можно тут: https://play.google.com/store/apps/details?id=city.sightsafari.android
Из крупного - осталось суметь сделать оффлайн-режим. Это важная фича для путешественников, так как интернет в дороге есть не везде. Но это весьма нетривиально, так как поиск пути у нас сделан довольно сложным и ресурсоемким, не всякий телефон его потянет. Да и кода придется много переносить.
Надеюсь кому-нибудь такое приложение пригодится в путешествиях. Впрочем, даже в родном городе и вроде бы знакомом районе можно с его помощью найти что-то интересное.
Работает пока в 100+ городах России и мира. Оставить заявки на добавление новых городов можно тут в комментариях или в нашей группе ВК.