2ГИС вам на руку. Как мы добавили карту на Apple Watch


    Apple Watch быстро завоевали популярность и стали самыми популярными часами в мире, опередив Rolex и остальных производителей. Идея создания приложения для часов витала в офисе 2ГИС с 2015 года.


    До нас полноценное приложение с картой на часах выпустила только сама Apple. Приложение Яндекс.Карт отображает лишь виджеты пробок и время в пути до дома и работы. Яндекс.Навигатор, Google Maps, Waze и Maps.Me вообще недоступны на часах.


    По сути, из-за множества ограничений системы и сложности в разработке, компании либо вообще не делают приложения для часов, либо делают их очень простыми. Нельзя просто взять и сделать карту на часах. Но мы смогли.


    Загляните под кат, чтобы узнать, как пет-проджект вырос в полноценный продукт.


    UPD.: https://github.com/teanet/DemoWatch


    Мы решили делать карту. Что было на старте?


    1. Опыт разработки на часах — 2 дня работы над тестовым проектом.
    2. Опыт работы со SpriteKit — 0 дней.
    3. Опыт написания MapKit – 0 дней.
    4. Сомнения, что что-то может пойти не так — ∞.

    Итерация 1 — полет мысли


    Мы серьезные люди, поэтому для начала решили составить план работ. Учли, что мы работаем в жестко распланированном спринте, имеем пять сторипоинтов на «мелкопродуктовые задачи» и полное незнание, с чего начать.


    Карта — это очень большая картинка. Картинки на часах мы показывать умеем, значит и с показом карты справимся.


    У нас есть сервис, который умеет резать карту на кусочки:



    Если нарезать такую картинку и положить в WKImage, получим самый простой рабочий прототип за пять копеек.


    А если на эту картинку добавить PanGesture и на каждый свайп устанавливать новую картинку, то получим симуляцию взаимодействия с картой.


    /Радуемся/ Звучит ужасно, выглядит примерно так же, работает еще хуже, но по факту задача выполнена.


    Итерация 2 — минимальный прототип


    Непрерывная загрузка картинок дорого обходится батарее в часах. Да и само время загрузки страдает. Нам хотелось получить что-то более полноценное и отзывчивое. Краем уха мы слышали, что в часах есть поддержка SpriteKit — единственного фреймворка под WatchOS, с возможностью использовать координаты, зум и кастомизировать всё это великолепие под себя.


    После пары часов StackOverflow Driven Development (SDD) получаем вторую итерацию:
    Один SKSpriteNode, один WKPanGestureRecognizer.



    /Радуемся/ Да это же MapKit за 6 копеек, полностью рабочий. Срочно в релиз!


    Итерация 3 —добавляем тайлы и зум


    Когда эмоции спали, задумались, куда же идти дальше.


    Поняли, что важнее всего:


    • Заменить картинку на тайлы.
    • Подложить 4 тайла в бандл приложения и соединить их вместе.
    • Обеспечить зум картинки.
      Закинем 4 тайла в бандл приложения, потом положим их на некую:

    let rootNode = SKSpriteNode()

    с помощью нехитрой математики соединим их вместе.
    Зум делаем через WKCrownDelegate:


    internal func crownDidRotate(
      _ crownSequencer: WKCrownSequencer?, 
      rotationalDelta: Double
    ) {
      self.scale += CGFloat(rotationalDelta * 2)
      self.rootNode.setScale(self.scale)
    }


    /Радуемся/ Ну теперь то точно всё! Пару фиксов, и в мастер.


    Итерация 4 — оптимизируем взаимодействие с картой


    На следующий день оказалось, что для SpriteKit anchorPoint не влияет на зум. Зум полностью игнорирует anchorPoint и происходит относительно центра rootNode. Получается, что на каждый шаг зума нам нужно корректировать позицию.


    Также было бы неплохо грузить тайлы с сервера, а не хранить весь мир в памяти часов.
    Не забываем, что тайлы стоит привязать к координатам, чтобы они лежали не в центре SKScene, а на соответствующих местах на карте.


    Тайлы выглядят примерно так:



    Для каждого zoomLevel (далее «z») идет свой набор тайлов. Для z = 1 у нас 4 тайла составляют весь мир.



    для z = 2 — для того, чтобы покрыть весь мир, нужно уже 16 тайлов,
    для z = 3 — 64 тайла.
    для z = 18 ≈ 68 * 10^9 тайлов.
    Теперь их нужно положить в мир SpriteKit.


    Размер одного тайла 256 * 256 pt, значит
    для z = 1 размер «мира» будет равен 512 * 512 pt,
    для z = 2 размер «мира» будет равен 1024 * 1024 pt.
    Для простоты расчетов положим тайлы в мир следующим образом:



    Закодируем тайл:


    let kTileLength: CGFloat = 256
    
    struct TilePath {
      let x: Int
      let y: Int
      let z: Int
    }

    Определим координату тайла в таком мире:


    var position: CGPoint {
      let x = CGFloat(self.x)
      let y = CGFloat(self.y)
      let offset: CGFloat = pow(2, CGFloat(self.z - 1))
      return CGPoint(x: kTileLength * ( -offset + x ),
                            y: kTileLength * ( offset - y - 1 ))
    }
    
    var center: CGPoint {
      return self.position + CGPoint(x: kTileLength, y: kTileLength) * 0.5
    }

    Расположение удобно, так как позволяет привести всё в координаты реального мира: latitude/longitude = 0, что как раз в центре «мира».


    latitude/longitude реального мира преобразуются в наш мир следующим образом:


    extension CLLocationCoordinate2D {
    
      // относительное положение в мире ( -1 < TileLocation < 1 )
      func tileLocation() -> CGPoint {
        var siny = sin(self.latitude * .pi / 180)
        siny = min(max(siny, -1), 1)
        let y = CGFloat(log( ( 1 + siny ) / ( 1 - siny )))
        return CGPoint(
          x: kTileLength * ( 0.5 + CGFloat(self.longitude) / 360 ),
          y: kTileLength * ( 0.5 - y / ( 4 * .pi ) )
        )
      }
    
      // абсолютное положение в мире для нужного zoomLevel
      func location(for z: Int) -> CGPoint {
        let tile = self.tileLocation()
        let zoom: CGFloat = pow(2, CGFloat(z))
        let offset = kTileLength * 0.5
        return CGPoint(
          x: (tile.x - offset ) * zoom,
          y: (-tile.y + offset) * zoom
        )
      }
    
    }

    С зум левелами огребли проблем. Пришлось потратить пару выходных, чтобы собрать в кучу весь математический аппарат и обеспечить идеальное слияние тайлов. То есть тайл для z = 1 должен при зуме идеально переходить в четыре тайла для z = 2 и наоборот, четрые тайла для z = 2 должны переходить в один тайл для z = 1.



    Кроме того, понадобилось превратить линейный зум в экспотенциальный, так как зумы меняются от 1 <= z <= 18, а карта масштабируется, как 2^z.


    Плавный зум обеспечивается постоянной корректировкой положения тайлов. Важно, чтобы тайлы сшивались ровно посередине: то есть, чтобы тайл уровня 1 переходил в 4 тайла уровня 2 при зуме 1.5.


    SpriteKit под капотом использует float. Для z = 18 у нас получается разброс координат (-33 554 432/33 554 432), а точность float – 7 разрядов. На выходе имеем погрешность в районе 30 pt. Чтобы избежать возникновение «щелей» между таймами, размещаем видимый тайл максимально близко к центру SKScene.


    /Радуемся/ После всех этих телодвижений получили готовый к тестированию прототип.


    Релиз


    Так как приложение толком не имело ТЗ, мы нашли пару добровольцев, чтобы провести небольшое тестирование. Особых проблем не нашли, и решили выкатывать в стор.


    После релиза оказалось, что на часах первой серии процессор не успевает отрисовать первый кадр карты за 10 секунд и падает по таймауту. Пришлось изначально создавать карту полностью пустой, чтобы уложиться в 10 секунд, а потом постепенно грузить подложку. Сначала разрабатывать все уровни карты — а потом грузить для них тайлы.


    Очень много времени ушло на отлаживание сети, правильную настройку кеша и малый Memory Footprint, чтобы WatchOS максимально долго не пытался убить наше приложение.


    Попрофилировав приложение, выяснили, что вместо обычных тайлов можно использовать Retina-тайлы, практически без вреда для времени загрузки и потребления памяти, и в новом релизе уже перешли на них.


    Итоги и планы на будущее


    Мы уже можем отображать на карте маршрут с маневрами, построенными в основном приложении. Функция будет доступна в одном из ближайших релизов.


    Проект, который изначально казался невыполнимым, оказался крайне полезным лично для меня. Часто пользуюсь приложением, чтобы понять, скоро ли выходить на нужной остановке. Верю, что зимой оно окажется еще полезнее.


    Заодно в очередной раз убедился, что не так важна сложность проекта, вера окружающих в успех задачи или наличие свободного времени на работе. Главное — это желание сделать проект и нудное, постепенное движение к цели. В итоге у нас есть полноценный MapKit, который почти ничем не ограничен и работает с 3 WatchOS. Его можно дорабатывать как хочется, не ожидая, когда Apple выкатит подходящий API для разработки.


    P.S. Для интересующихся могу выложить готовый проект. Уровень кода там далек от production. Но, согласно военному принципу, — не важно, как это работает, главное, что работает!

    2ГИС

    148,91

    Карта города и справочник предприятий

    Поделиться публикацией

    Похожие публикации

    Комментарии 48
      +17

      Спасибо! 2гис — одно из лучших картографических приложений.

        +9
        Спасибо, стараемся
          –9
          Одно из самых тормозящих
            +14
            Не больше конкурентов. При этом уровень проработки — недостижимый для остальных.
              +3
              годика 2-3 назад так и было, но не сейчас. Выходите из спячки.
            +3
            Еще бы на samsung gear сделать аналогичное-)
              +1
              да, разработка под Tizen была бы кстати, это ведь почти все девайсы от Samsung (часы браслеты ТВ)
                0
                Поддерживаю предложение. Samsung Gear S3 довольно распространённые часы. Сам пользуюсь с удовольствием и много знакомых и друзей такие имеют.
              0
              да, ждем более тесной интеграции с навигацией на телефоне. А то установил и не понял прикола — кроме карты ничего нет
                +3
                Был вариант выпустить карту сейчас, или карту с навигацией, но потом.
                Выбрали что просто карта с пином последней открытой карточки в бесконечно раз лучше чем ничего.
                  0
                  Да, работает по крайней мере довольно шустро даже на самых первых часах (те что даже не S1).
                    0
                    это потому что пока рекламу не подвезли )
                      +6
                      Хорошо что хоть кто то знает как правильно оптимизировать огромный бесплатный проект чтобы он не тормозил во всех сценариях на всех устройствах, работал оффлайн и при этом зарабатывал.
                      Приходи к нам в команду, сделай все по красоте =)
                        0
                        А дубльгис разве бесплатен для организаций размещающих там свои контактные данные?
                          0
                          Если указаны только название, сайт, адрес и телефон — то бесплатен. Платные — вставка логотипа, цветная плашечка, дополнительное описание и так далее.
                +3
                Приятно каждый раз ездить около Сан Сити и знать, что там делают что-то полезное!
                  +1
                  Мы в основном в Академе работаем)
                  +1

                  Готовый проект было бы интересно посмотреть!

                  0
                  Вы были бы приложением №1 для начала навигации, если бы:
                  1) Ссылка share работала.
                  2) вы позволяли бы открыть найденное в навигаторе. Сами не ам, и другим не дам…
                    –1
                    А можно поподробнее что не так?
                      0
                      Пардон, share починили.

                      В чём проблема? В 2gis нет навигации, пробок и всего остального. Зато есть адреса. Нашёл адрес, нажал «open in», открыл в любимом навигаторе (тот же osmand), едешь.

                      Но нет, «открыть точку в картографическом приложении» отсутствует, надо использовать недонавигацию 2gis'а. Бу.
                        +1
                        Мы же тут все(или почти) взрослые люди и всем понятно, что такая фича ну никак не в наших интересах.
                          0
                          Ну я реально не понимаю. Вы — отличный каталог, жёлтые страницы. Навигации у вас нет. Если бы была возможность чужую навигацию с вашим каталогом, то было бы:

                          а) Актуально для вас (вы — начальная точка)
                          б) Удобно для пользователя (есть навигация)

                          Я просто не понимаю, в чём смысл запрещать открывать эту же точку в навигаторе. Ваш бизнес же не в навигации (которой у вас нет), а в работе в качестве yellow pages. И у вас остаётся эта самая ценная вещь — поиск объектов.

                          Т.е. я понимаю, если бы я предложил вам экспортировать данные в гугльмэпс. Но тут же речь всего лишь про шаринг пина.

                            +4
                            Навигация у нас есть. Возможно есть проблемы, но мы их решаем.

                            Мы не запрещаем, мы не реализовали такую возможность, как и многие другие.
                            Не было таких запросов на моей памяти и у нас много других приоритетов в разработке.

                            Про шаринг пина накину продактам, может что и выйдет.
                              0
                              … Она у вас такая, что просто ой. Сравните с OSMand. Если вы будете её делать — ну, ещё один навигатор. Но ведь это:
                              а) Голос.
                              б) Поддержка режимов навигации (машина, общественный транспорт, велосипед, пешком), с различением видов треков
                              в) Настройки навигации (возможность пометить какие-то дороги как нежелательные)
                              г) Предупреждения о знаках (настраиваемые)

                              Навигация — это ж целое отдельное искусство.

                              PS Я вспомнил что в share не работает.

                              Если я в гугльмапс скажу share, copy to clipboard, и вставлю в google maps (приложение), то оно покажет это самое место.

                              Если я в 2gis скажу share, copy to clipboard, вставлю в поле поиска, то 2gis скажет, «такого нет» (потому что там не location, а url).
                                +3
                                Но… Это же всё есть.
                                И пробки, и голос, и выбор приоритетного маршрута…
                                  0
                                  В тугис? На Кипре? No way. Или мы про разные приложения говорим. Моё глупое и умеет только показывать маршрут, а вести не умеет.
                                    +2
                                    Вы правы, навигацию в Кипр мы еще не завезли. Это связано с алгоритмами для левостороннего движения. Насколько я знаю, мы работаем над этим в данный момент.
                                      +1
                                      Да, всё почти готово и вот-вот будет. Возможно, через месяц уже.
                    +2
                    2ГИС молодцы!
                    Я помню вас когда именовались ДубльГисом и обновления раздавались на дискетах раз в месяц =)))
                      0
                      А для WearOS ожидается что-нибудь?
                        +1
                        Скорее всего нет, только если найдется какой-нибудь энтузиаст вроде меня.
                        +1
                        Как представил: Едешь такой в автобусе, часы слегка завибрировали:
                        -Приготовьтесь, выход на следующей остановке.
                          +3
                          Такие планы есть, теплыми осенними вечерами возможно допилю.
                            0
                            citymapper на телефоне такое делает, как минимум в метро.
                            0
                            А у вас не завалялось там пет-проджекта нативного десктопного приложения для Linux? :)
                              0
                              Лично у меня не завалялось, только на часы)
                              Но есть какой то симулятор под андроид который как то хитро запускается под линуксом, но там столько кода, и зависимостей, много из которых скорее всего под NDA, что вряд ли получится просто собрать это на домашней тачке.
                                0
                                То же самое про Mac OS хотелось бы спросить.
                                  0
                                  А зачем вам?
                                  Сам слегка парился по этому поводу, а сейча сне парюсь.
                                  В дороге — мобилки. На ПК — веб версия. Благо она умеет всё что нужно.
                                    0
                                    Просто мне привычнее и удобнее работать именно с классическим десктопным приложением (пользуюсь им года с 2005-го). Помимо этого, десктопная версия поддерживает модули и работу со слоями (в веб-версии слои есть, насколько я понял, не для всех городов), что тоже бывает полезно.
                                      0
                                      Мы делали кросс-платформенное десктоп-приложение, но закрыли его разработку из-за невостребованности. Почти никому на десктопе не нужен офлайн, когда есть браузер.
                                        0
                                        Выкатите уже онлайн на мобильники.
                                          0
                                          Работаем над этим. Но история там более сложная, конечно. Изначально мы офлайн-приложение, и чтобы стать гибридным, нужно переписать всё чуть менее чем полностью.
                                  0
                                  У меня проблема с навигатором 2ГИС Иркутск. Во время поездки приложение вылетает через какое-то время. Закономерностей я не могу понять. Просто ни с того ни с сего закрывается и все. Вылетает как на Android на разных смартфонах, так и на iPad'e. Проблема известна или она только у меня?
                                    +1
                                    Тут статья про часы, и хотелось бы вопросы/предложения по часам.

                                    Если проблема стабильно воспроизводится, то она нам не известна. Тут дальше унылая бюрократия…
                                    У вас последняя версия приложения?
                                    самые актуальные базы?
                                    Если да, то после вылета сразу!!! пожалуйста, зайдите в приложение, зайдите в боковое меню > помощь > сообщить об ошибке
                                    добавьте меня в копию e.tyutyuev@2gis.ru
                                    это сильно поможет.
                                    +1
                                    хранить весь мир в памяти часов

                                    Красиво сказано!

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое