О карте в iPhone
Для продвинутых
Немного теории о том, как работает приложение Maps на iPhone
До недавнего времени пользователи интернет имели два способа просмотреть информацию на картах Google – сайт Карты Google и приложение Google Планета Земля. Не смотря на то, что оба этих способа черпают данные из одного источника, они слегка различаются. Приложение Maps на iPhone пользуется своим, третьим, способом получать карты. При этом загруженная информация сохраняется (кешируется) в памяти телефона, при последующем просмотре она не будет скачиваться из сети. Именно это обстоятельство позволяет нам загрузить заранее необходимую часть мировой карты , телефону будет казаться, что эту информацию загрузил он сам.
Для изготовления офлайн карт как правило используется механизм maps.google.com, поэтому такие карты имеют несколько иной цветовой оттенок и окантовку, чем те, которые загружаются самим телефоном. Используемый механизм был выбран в силу наличия его подробного описания, например здесь.
Кроме того, не следует забывать о том, что Google периодически обновляет карты, увеличивая при этом номер их версии. Приложение Maps в такой ситуации полностью удаляет загруженную информацию и начинает с чистого листа. Если вы перенесете в телефон ранее загруженную карту, она не будет удалена до очередного обновления на серверах Google, однако следует помнить, что версия загруженной карты может быть более древней, чем текущая, и поэтому возможны искажения на границах загруженной карты. После обновления версии карт на сервере вы можете при помощи iMapsManager просто перезалить в телефон имеющиеся у вас карты, нет необходимости скачивать еще раз данные из сети.
MapTiles.sqlitedb
Телефон загружает карту фрагментами 64х64 точки (в прошивке 2.2 - 128х128 точек, iPad/iPhone4 - 256x256 точек), загруженные фрагменты сохраняются в едином файле. В разных версиях прошивок он располагается в разных местах файловой системы телефона:
/private/var/root/Library/Caches/MapTiles/MapTiles.sqlitedb
(прошивки 1.0.0-1.1.2)
/private/var/mobile/Library/Caches/MapTiles/MapTiles.sqlitedb
(прошивки 1.1.2-3.0.1)
/private/var/mobile/Library/Caches/Maps/MapTiles/MapTiles.sqlitedbd
(для прошивок 3.1 и выше).
Этот файл является базой данных в
формате SQLite,
и может быть просмотрен/модифицирован на настольном компьютере. Стоит
отметить, что начиная с прошивки 3.1 кеш приложения "Карты" на телефоне
хранится отдельно от кеша карт, используемых другими приложениями,
устанавливаемыми из AppStore. Другие приложения, например
MyBank
при работе с картой кешируют ее в папку, что использовалась на
1.1.2-3.0.1 прошивках.
База данных содержит 2 таблицы – images и version, в последней хранится номер версии карты, использованной при ее создании. По нему телефон определяет, не нужно ли ему удалять закешированную информацию. Если в таблице содержится информация об устаревшей версии, телефон удалит базу при первом же обращении к серверу. В случае, если таблица пуста, телефон считает карту актуальной и заносит туда текущий номер версии с сервера Google. iMapsManager позволяет очищать эту таблицу перед загрузкой карты в телефон. Кроме того, начиная с прошивки 3.2/4.0 в таблице version хранится язык интерфейса, который использовался при загрузке карты. При изменении языка телефона имеющаяся карта будет удалена, новая будет содержать названия топонимов на выбранном языке.
Приложение "Карты" ведет себя следующим образом: пока общее количество фрагментов в базе (суммарно по слоям и типам карты) не превышает 40 960, скачиваемые из сети фрагменты просто добавляются в кеш. Если количество фрагментов превысило этот порог, то на каждом следующем 1024м новом фрагменте (41 984м, 43008м и т.д.) происходит удаление самых старых фрагментов так, чтобы в базе их осталось 40 960. Т.о. предположим, вы загрузили в телефон базу из 100 000 фрагментов. В дальнейшем вы просматриваете незакешированный участок. Телефон подгрузит еще 352 фрагмента (100 352 = 1024*98) и в этот момент удалит половину вашего кеша, в нем останутся фрагменты начиная с номера 59 392 (59 392 = 100 352 - 40 960). Нумерация идет последовательно в порядке помещений фрагментов в базу. В дальнейшем, загрузив еще 1024 новых фрагмента, вы снова потеряете 1024 самых старых. Процесс удаления на больших картах занимает продолжительное время и может показаться, что приложение зависло. Это не так – дайте ему подумать десяток-другой минут, и оно снова начнет отвечать. Но части фрагментов уже не будет. Характерным признаком идущего процесса удаления является наличие файла MapTiles.sqlitedb-journal в папке карты на диске телефона. Не удаляйте его! Это приведет к разрушению информации в кеше. Для справки - карта Москвы для прошивок ниже 2.2 с увеличением до предпоследнего уровня состоит из 191 766 фрагментов, слой с максимальным увеличением содержит еще около 700 000 штук.
Размер файла кеша в этом процессе не уменьшается, оставаясь равным максимально достигнутому: в случае со стандартным поведением – примерно на 42 000 фрагментов, при загрузке внешнего кеша – размеру загруженного кеша.
Существует 2 пути борьбы с таким поведением:
- не давать приложению прав на запись в файл карты путем
правильного выставления разрешений на файл. При этом подгруженные
фрагменты будут выводиться на экран, но не будут сохраняться в кеш. Для
корректной работы на прошивке 3.2/4.х необходимо установить права
Read+Execute (Чтение+Исполнение) на сам файл карты и на
папку, в которой он лежит (для Owner,Group и All). При этом
файл будет закрыт на запись и на удаление - новые данные туда
телефоном добавляться не будут, но и старые не удалятся.
- Пропатчить приложение /System/Library/PrivateFrameworks/GMM.framework/GMM (именно оно обеспечивает такое поведение карт). Начиная с прошивки 3.1 данный файл объединен с другими фреймворками в т.н. shared cache, расположенный в /System/Library/Caches/com.apple.dyld/dyld_shared_cache_armv6 или 7. Перед его модификацией его нужно извлечь при помощи программы dyldcache, либо патчить на лету, используя MobileSubstrate.
В случае, если приложение Maps было завершено в тот момент, когда оно осуществляло запись в базу, рядом с файлом MapTiles.sqlitedb создается файл MapTiles.sqlitedb-journal, который будет использован при следующем запуске приложения для восстановления структуры данных. Не удаляйте его вручную, в противном случае база будет разрушена.
Bookmarks.plist
В файле /private/var/mobile/Library/Maps/Bookmarks.plist хранится информация о пользовательских закладках. Данный файл может быть использован для обеспечения офф-лайн поиска на карте – в него заносятся координаты улиц необходимого вам района.
Как правило, для изготовления этого файла используется информация с сайта openstreetmap.com. Инструкцию по его изготовлению можно прочитать (и просмотреть) здесь (английский). Кроме того, его можно создать при помощи API Яндекс.Карт.
com.apple.Maps.plist
В файле /private/var/mobile/Library/Preferences/com.apple.Maps.plist содержится информация о последнем просмотренном фрагменте карты. Файл является стандартным plist'ом, преобразовать его из бинарного формата в xml можно при помощи, например, этой страницы.
Другие программы
На сегодняшний день существует множество программ, позволяющих загрузить данные с серверов Google и других источников картографической информации. В их число входят GMDL, GoogleMV, MapBuilder, GoogleV, SAS.Планета, GPSMapEdit, MGMaps, скрипт для OpenStreetMap и т.п. Из них только GMDL и SAS.Планета имеют возможность выводить результаты в формате, понимаемом iPhone.
Кроме того, существуют утилиты для iPhone, позволяющие переключаться между кешами не используя компьютер. К их числу относятся JasonKit (бывшая iLM), MapsOffline, OfflineMaps. Данные программы позволяют загрузить на телефон несколько отдельных файлов баз, а затем переключаться между ними, используя интерфейс телефона. При этом они копируют карту в папку /private/var/mobile/Library/Caches/MapTiles из своей локальной директории или создают символьную ссылку на этот файл.Сейчас вы знаете достаточно для того, чтобы самостоятельно управиться с любыми задачами по загрузке карт в телефон. Если же вы собираетесь разработать свое приложение, взаимодействующее с картами - переходите к разделу для разработчиков.