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


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


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


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


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


  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. Но, согласно военному принципу, — не важно, как это работает, главное, что работает!

Комментарии (46)


  1. betony
    12.09.2018 08:55
    +2

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


    1. teanet Автор
      12.09.2018 09:29
      +1

      Спасибо, стараемся


    1. plus
      12.09.2018 10:09
      -3

      Одно из самых тормозящих


      1. AllexIn
        12.09.2018 10:24
        +4

        Не больше конкурентов. При этом уровень проработки — недостижимый для остальных.


      1. mikes
        12.09.2018 16:14
        +2

        годика 2-3 назад так и было, но не сейчас. Выходите из спячки.


  1. Wit71
    12.09.2018 09:51

    Еще бы на samsung gear сделать аналогичное-)


    1. mikes
      12.09.2018 16:15
      +1

      да, разработка под Tizen была бы кстати, это ведь почти все девайсы от Samsung (часы браслеты ТВ)


      1. academy21
        13.09.2018 11:28

        Поддерживаю предложение. Samsung Gear S3 довольно распространённые часы. Сам пользуюсь с удовольствием и много знакомых и друзей такие имеют.


  1. advan20092
    12.09.2018 10:31

    да, ждем более тесной интеграции с навигацией на телефоне. А то установил и не понял прикола — кроме карты ничего нет


    1. teanet Автор
      12.09.2018 10:46
      +2

      Был вариант выпустить карту сейчас, или карту с навигацией, но потом.
      Выбрали что просто карта с пином последней открытой карточки в бесконечно раз лучше чем ничего.


      1. advan20092
        12.09.2018 10:51

        Да, работает по крайней мере довольно шустро даже на самых первых часах (те что даже не S1).


        1. cup_of_coffee
          12.09.2018 16:03
          -1

          это потому что пока рекламу не подвезли )


          1. teanet Автор
            12.09.2018 16:09
            +4

            Хорошо что хоть кто то знает как правильно оптимизировать огромный бесплатный проект чтобы он не тормозил во всех сценариях на всех устройствах, работал оффлайн и при этом зарабатывал.
            Приходи к нам в команду, сделай все по красоте =)


            1. DIHALT
              12.09.2018 20:55

              А дубльгис разве бесплатен для организаций размещающих там свои контактные данные?


              1. cyberly
                12.09.2018 21:51

                Если указаны только название, сайт, адрес и телефон — то бесплатен. Платные — вставка логотипа, цветная плашечка, дополнительное описание и так далее.


  1. nerlihmax
    12.09.2018 15:00
    +2

    Приятно каждый раз ездить около Сан Сити и знать, что там делают что-то полезное!


    1. teanet Автор
      12.09.2018 16:14
      +1

      Мы в основном в Академе работаем)


  1. drinkius
    12.09.2018 15:23

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


    1. teanet Автор
      12.09.2018 16:10
      +2

      Выложу в течении месяца отдельным Pod'ом


  1. amarao
    12.09.2018 15:42

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


    1. teanet Автор
      12.09.2018 16:06

      А можно поподробнее что не так?


      1. amarao
        12.09.2018 16:30

        Пардон, share починили.

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

        Но нет, «открыть точку в картографическом приложении» отсутствует, надо использовать недонавигацию 2gis'а. Бу.


        1. teanet Автор
          12.09.2018 17:28

          Мы же тут все(или почти) взрослые люди и всем понятно, что такая фича ну никак не в наших интересах.


          1. amarao
            12.09.2018 17:36

            Ну я реально не понимаю. Вы — отличный каталог, жёлтые страницы. Навигации у вас нет. Если бы была возможность чужую навигацию с вашим каталогом, то было бы:

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

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

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


            1. teanet Автор
              12.09.2018 17:43
              +1

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

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

              Про шаринг пина накину продактам, может что и выйдет.


              1. amarao
                12.09.2018 17:54
                +1

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

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

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

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

                Если я в 2gis скажу share, copy to clipboard, вставлю в поле поиска, то 2gis скажет, «такого нет» (потому что там не location, а url).


                1. AllexIn
                  12.09.2018 19:53

                  Но… Это же всё есть.
                  И пробки, и голос, и выбор приоритетного маршрута…


                  1. amarao
                    12.09.2018 23:33

                    В тугис? На Кипре? No way. Или мы про разные приложения говорим. Моё глупое и умеет только показывать маршрут, а вести не умеет.


                    1. teanet Автор
                      13.09.2018 06:10

                      Вы правы, навигацию в Кипр мы еще не завезли. Это связано с алгоритмами для левостороннего движения. Насколько я знаю, мы работаем над этим в данный момент.


                      1. docomo
                        13.09.2018 08:29

                        Да, всё почти готово и вот-вот будет. Возможно, через месяц уже.


  1. xrays72
    12.09.2018 15:46

    2ГИС молодцы!
    Я помню вас когда именовались ДубльГисом и обновления раздавались на дискетах раз в месяц =)))


  1. RingoAl
    12.09.2018 16:04

    А для WearOS ожидается что-нибудь?


    1. teanet Автор
      12.09.2018 16:05

      Скорее всего нет, только если найдется какой-нибудь энтузиаст вроде меня.


  1. Nice-L
    12.09.2018 16:05
    +1

    Как представил: Едешь такой в автобусе, часы слегка завибрировали:
    -Приготовьтесь, выход на следующей остановке.


    1. teanet Автор
      12.09.2018 16:06

      Такие планы есть, теплыми осенними вечерами возможно допилю.


    1. chersanya
      12.09.2018 20:15

      citymapper на телефоне такое делает, как минимум в метро.


  1. StackZero
    12.09.2018 17:37

    А у вас не завалялось там пет-проджекта нативного десктопного приложения для Linux? :)


    1. teanet Автор
      12.09.2018 17:49

      Лично у меня не завалялось, только на часы)
      Но есть какой то симулятор под андроид который как то хитро запускается под линуксом, но там столько кода, и зависимостей, много из которых скорее всего под NDA, что вряд ли получится просто собрать это на домашней тачке.


    1. Ravebinovich
      12.09.2018 19:21

      То же самое про Mac OS хотелось бы спросить.


    1. AllexIn
      12.09.2018 19:54

      А зачем вам?
      Сам слегка парился по этому поводу, а сейча сне парюсь.
      В дороге — мобилки. На ПК — веб версия. Благо она умеет всё что нужно.


      1. StackZero
        12.09.2018 21:08

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


        1. docomo
          13.09.2018 08:31

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


          1. TRIMER
            13.09.2018 09:20

            Выкатите уже онлайн на мобильники.


            1. docomo
              13.09.2018 10:06

              Работаем над этим. Но история там более сложная, конечно. Изначально мы офлайн-приложение, и чтобы стать гибридным, нужно переписать всё чуть менее чем полностью.


  1. bulavin
    13.09.2018 04:52

    У меня проблема с навигатором 2ГИС Иркутск. Во время поездки приложение вылетает через какое-то время. Закономерностей я не могу понять. Просто ни с того ни с сего закрывается и все. Вылетает как на Android на разных смартфонах, так и на iPad'e. Проблема известна или она только у меня?


    1. teanet Автор
      13.09.2018 06:07

      Тут статья про часы, и хотелось бы вопросы/предложения по часам.

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