[{"data":1,"prerenderedAt":554},["ShallowReactive",2],{"page:\u002Fru\u002Fposts\u002Fcode_n_shit\u002Fcuando_vector":3,"translations:\u002Fru\u002Fposts\u002Fcode_n_shit\u002Fcuando_vector":530,"series:ru:cuando":535},{"id":4,"title":5,"author":6,"body":7,"date":515,"description":516,"draft":517,"extension":518,"image":519,"listed":520,"meta":521,"navigation":520,"path":522,"seo":523,"series":524,"stem":525,"tags":526,"theme":524,"toc":520,"__hash__":529},"content_ru\u002Fru\u002Fposts\u002Fcode_n_shit\u002Fcuando_vector.md","Пятьдесят восемь мегабайт","slnt_opp",{"type":8,"value":9,"toc":503},"minimark",[10,14,22,27,30,33,48,51,54,57,79,83,87,90,113,116,119,123,135,138,145,162,169,172,175,198,201,205,208,216,219,222,225,232,236,239,242,253,256,259,263,280,291,294,305,311,314,317,343,347,350,449,455,458,462,465,476,479,483],[11,12,13],"p",{},"Вторая часть закончилась шуткой. Сервер, который рисует нашу карту картинками, получает на вход совершенно нормальную векторную карту региона, а существует он только потому, что наши телефоны не успевали рисовать её сами.",[11,15,16,17,21],{},"Прошло полтора года, и от шутки почти ничего не осталось. С этой недели по одному стилю на тему, которые отдаёт наш API, и один архив на ",[18,19,20],"strong",{},"58,67 МБ"," рисуют карту в нашей админке и — на Android — в новом партнёрском приложении. Flutter-приложение, которое сейчас лежит в сторах, по-прежнему показывает картинки и будет показывать их, пока его не заменят. Это история о том, как мы к этому пришли и почему вектор побеждает.",[23,24,26],"h2",{"id":25},"во-что-обходятся-картинки","Во что обходятся картинки",[11,28,29],{},"Картинки работали. Это стоит сказать сразу: с февраля 2025 года все карты в приложении, кроме спутника, рисовали мы сами, и никто ничего не заметил.",[11,31,32],{},"Платили мы за них временем. Во второй части мы обещали рисовать тайлы заранее, чтобы никто не ждал по три секунды, пока появится улица, которую до него ещё никто не видел. Сделали мы это в два захода. На следующий день после переезда появился маленький скрипт: он обходил центр города на 17-м и 18-м зумах и запрашивал каждый тайл у нашего же сервера, чтобы всё было нарисовано и сохранено раньше, чем туда кто-нибудь посмотрит. В апреле 2026 года наш Flutter-разработчик превратил его в настоящий пререндер с пулом параллельных рендереров, а через пару дней пререндер начал рисовать прямо из векторных тайлов в файлы.",[11,34,35,36,39,40,43,44,47],{},"А потом мы запустили его по-настоящему. Зумы с 13-го по 19-й в прямоугольнике, который сетка зума покрывала до мая, — это ",[18,37,38],{},"944 382 тайла на тему",", и 94% из них приходятся на 18-й и 19-й зумы. Наша виртуалка для рендера, та самая из второй части, тянет около четырёх рендеров одновременно, примерно по три секунды на каждый. Выходит около восьми дней на тему. Поэтому пререндер крутится на моём Mac mini: там одна тема занимает ",[18,41,42],{},"от 10 до 12 часов"," и на выходе весит около ",[18,45,46],{},"15 ГБ",". Виртуалка теперь дорисовывает только то, чего не хватает.",[11,49,50],{},"По оценке самого скрипта выходило около семи часов. Тайлы, которые он пропускал, потому что они уже лежали на диске, он засчитывал в прогресс, а все эти пропуски пришлись на самое начало. Так что чем дольше он работал, тем хуже становилась оценка.",[11,52,53],{},"Эти 15 ГБ — только счёт за хранение. Настоящая цена вылезает, когда хочется что-то поменять. В апреле мы сделали названия дорог темнее и добавили им обводку, потому что они плохо читались. Для векторной карты это несколько строк в файле стиля. Для картинок это значит заново нарисовать всю пирамиду, причём для обеих тем: день работы Mac mini и около 30 ГБ, которые потом надо залить.",[11,55,56],{},"А кое-что картинки не умеют в принципе:",[58,59,60,67,73],"ul",{},[61,62,63,66],"li",{},[18,64,65],{},"Подписи запечены в картинку."," Каждое название улицы — часть изображения, на одном языке, так что тот, у кого приложение на русском, видит ровно те же подписи, что и тот, у кого оно на английском.",[61,68,69,72],{},[18,70,71],{},"Подписи не умеют держаться прямо."," Повернёшь карту — и названия улиц повернутся вместе с ней, а если крутить дальше, то и вверх ногами.",[61,74,75,78],{},[18,76,77],{},"Новый регион — это ещё одна пирамида."," Приложение выходит за пределы Торревьехи, и для каждого нового города придётся рисовать и хранить его картинки — в каждой теме.",[80,81],"gallery",{":images":82},"[{\"src\":\"\u002Fimages\u002Fcuando_vector\u002Flabels-raster.jpg\",\"alt\":\"Картинки: названия улиц — часть изображения, заглавными буквами, на одном языке\"},{\"src\":\"\u002Fimages\u002Fcuando_vector\u002Flabels-vector.jpg\",\"alt\":\"Вектор: телефон рисует названия вдоль дорог, и они читаются, как карту ни поверни\"}]",[23,84,86],{"id":85},"векторные-данные-раньше-векторного-рендерера","Векторные данные раньше векторного рендерера",[11,88,89],{},"Первый архив в нашем собственном векторном Worker вообще не был картой. Это были наши точки интереса.",[11,91,92,93,100,101,106,107,112],{},"В апреле 2025 года мы подняли второй Cloudflare Worker рядом с сервером тайлов. Это скопированный Worker от ",[94,95,99],"a",{"href":96,"rel":97},"https:\u002F\u002Fprotomaps.com",[98],"nofollow","Protomaps"," для ",[94,102,105],{"href":103,"rel":104},"https:\u002F\u002Fdocs.protomaps.com\u002Fpmtiles\u002F",[98],"PMTiles"," — формата, который упаковывает целую пирамиду тайлов карты, векторных или растровых, в один файл. Свой домен он получил через 27 минут после первого коммита. Первым архивом, который мы туда положили, стали наши POI, нарезанные на векторные тайлы через ",[94,108,111],{"href":109,"rel":110},"https:\u002F\u002Fgithub.com\u002Ffelt\u002Ftippecanoe",[98],"tippecanoe",".",[11,114,115],{},"Рисовать векторные тайлы Flutter-приложение к тому времени уже перестало, а вот читать — вполне. С середины 2025 года, когда приходит тайл-картинка, приложение подтягивает тайл с POI для того же места, декодирует его на Dart и рисует каждую точку маркером поверх. Векторные данные без векторного рендерера.",[11,117,118],{},"Точечные объекты обходятся дёшево, поэтому тайлы с POI идут до самого 22-го зума. А на каком зуме показывать точку, теперь записано в ней самой: одно число в архиве, и никаких картинок перерисовывать не надо. В мае 2026 года tippecanoe переехал в Cloudflare Container, который каждую ночь пересобирает архив. Жить в самом Worker он не может: tippecanoe — нативный бинарник, а запустить такой Worker не может.",[23,120,122],{"id":121},"как-устроена-векторная-часть","Как устроена векторная часть",[11,124,125,126,131,132,134],{},"Подложка — тоже один файл PMTiles. Это данные OpenStreetMap по региону, которые tilemaker нарезал по схеме ",[94,127,130],{"href":128,"rel":129},"https:\u002F\u002Fopenmaptiles.org",[98],"OpenMapTiles",". Те же самые данные, которые наш сервер картинок читает с самого первого тайла. Весит файл ",[18,133,20],{}," и живёт в R2 за тем же Worker, что и POI.",[11,136,137],{},"Целиком этот файл Worker не загружает никогда. Заголовок и индекс верхнего уровня он читает одним range-запросом на 16 КБ, а потом так же достаёт каждый тайл: один диапазон, только байты этого тайла. Единственное, что он держит в памяти, — до 25 распарсенных блоков индекса. Тайлы сутки лежат в edge-кэше Cloudflare. Так что память Worker никак не зависит от размера архива — именно так Protomaps и раздаёт всю планету из одного файла. Для нас регион побольше — это файл побольше в том же бакете, а не новая архитектура. Чтобы заменить файл, достаточно залить новый: каждое чтение проверяет ETag файла, так что Worker сам замечает новую версию.",[11,139,140,141,144],{},"Поверх данных лежит ",[18,142,143],{},"стиль",": JSON-файл, в котором сказано, что рисовать, каким цветом и на каком зуме. Мы переделали под телефоны Positron от OpenMapTiles для светлой темы и Dark Matter для тёмной:",[58,146,147,150,153,156,159],{},[61,148,149],{},"названия районов остаются на экране, когда зумишь, а не пропадают ровно тогда, когда они нужны;",[61,151,152],{},"размер любого текста плавно растёт вместе с зумом;",[61,154,155],{},"когда подписи налезают друг на друга, города берут верх над районами, районы — над улицами, а города покрупнее — над городами поменьше;",[61,157,158],{},"названия улиц идут вдоль дорог;",[61,160,161],{},"пешеходные дорожки — тонкий пунктир.",[11,163,164,165,168],{},"Каждый стиль весит около ",[18,166,167],{},"22 КБ",". Вот и вся цена темы.",[11,170,171],{},"На этой неделе стили переехали в наш API, а вместе с переездом вышли и правки под телефоны из списка выше. Раньше админка, партнёрское приложение и новая версия пользовательского приложения таскали в себе одну и ту же старую копию, так что любую правку приходилось вносить в каждую руками. Теперь есть одна поддерживаемая версия: она встроена в API, и API её отдаёт. Приложения держат у себя копию только как затравку, чтобы карта могла нарисоваться раньше, чем ответит сеть. Если изменение ломает совместимость, стиль получает новый путь, так что старые сборки приложения никогда не получат стиль, с которым не справятся.",[11,173,174],{},"Две вещи, которые мы при этом выяснили:",[58,176,177,183],{},[61,178,179,182],{},[18,180,181],{},"Шрифты — строго по одному."," Стиль может перечислять запасные шрифты, и MapLibre запрашивает их все одним запросом, склеив названия через запятую. Наши глифы лежат в R2 файлами, и файла с таким склеенным именем нет. Поэтому каждая подпись в наших стилях использует ровно одно начертание одного шрифта — Metropolis.",[61,184,185,188,189,193,194,197],{},[18,186,187],{},"Кесарю кесарево."," В метаданных файла PMTiles не было атрибуции, поэтому MapLibre указал единственного, о ком знал, — себя. На карте было написано ",[190,191,192],"em",{},"«MapLibre Android»"," там, где должно было быть ",[190,195,196],{},"«© OpenStreetMap contributors»",", а лицензия OpenStreetMap требует именно второго. Исправление, тоже на этой неделе, — маленькая табличка в Worker: для подложки указывается OpenStreetMap, а для наших собственных POI — никто. PR открыли и смёржили за восемь минут, и он починил подпись на всех картах, которые читают наши векторные тайлы, — в админке и на Android, — а перезаливать не пришлось ни одного файла.",[11,199,200],{},"В июле за Worker с тайлами переехала и подложка. Старый адрес теперь редиректит туда — для всего, у чего он ещё зашит в стиль. Самый преданный пользователь старого адреса — наш же рендерер картинок: стили, по которым он рисует, до сих пор смотрят туда.",[23,202,204],{"id":203},"первой-была-админка","Первой была админка",[11,206,207],{},"Первые карты появились в нашей админке в мае 2026 года, и с первой же строчки это был MapLibre. Почему — объясняет комментарий в шапке самого первого компонента с картой:",[209,210,211],"blockquote",{},[11,212,213,112],{},[190,214,215],{},"«Ушли с mapbox-gl сознательно: maplibre совместим по API во всём, что мы используем, не требует токена доступа, не стучится в events.mapbox.com \u002F map-sessions с телеметрией и не рисует логотип Mapbox»",[11,217,218],{},"А ещё в ней появилось то, чего нет в нашей Flutter-карте. На каждом маршруте в админке нарисованы маленькие шевроны, которые показывают, куда едет автобус: MapLibre, пока рисует линию, повторяет вдоль неё небольшое изображение и поворачивает его на каждом изгибе, так что шевроны всегда смотрят вперёд. Версия для Flutter расписана и ждёт своего часа.",[11,220,221],{},"Через две недели мы удалили старый редактор маршрутов — последнее, что в репозитории ещё использовало веб-библиотеку Mapbox.",[11,223,224],{},"Одна ирония всё же осталась. В админке есть вкладка, которая показывает сетку зума — ту самую тепловую карту из второй части, которая не даёт Flutter-приложению запрашивать никому не нужные картинки, — и даёт посмотреть на новую сетку до публикации. Рисует она всё это на векторной карте.",[11,226,227],{},[228,229],"img",{"alt":230,"src":231},"Админка на нашей векторной подложке: пин тестовой заявки и зоны покрытия двух выдуманных исполнителей над Аликанте, а в углу указан OpenStreetMap","\u002Fimages\u002Fcuando_vector\u002Fconsole-map.jpg",[23,233,235],{"id":234},"flutter-ещё-разок","Flutter, ещё разок",[11,237,238],{},"Вектор во Flutter мы пробовали дважды. Первый раз, в ноябре 2024 года, описан во второй части: всё работало, а на Android карта лагала и дорисовывалась тайл за тайлом.",[11,240,241],{},"Вторая попытка закончилась в марте 2026 года. Для векторного пакета flutter_map в бете вышла новая мажорная версия, которая рендерит не через обычный canvas, а через GPU API Flutter, — ровно то, что нужно карте, где всё сплошь из векторных контуров. Наш Flutter-разработчик её попробовал, и на Redmi-середнячке приложение упало:",[243,244,250],"pre",{"className":245,"code":247,"language":248,"meta":249},[246],"language-text","MSAA is not currently supported on this backend.\n[ERROR:flutter\u002Fimpeller\u002Frenderer\u002Fbackend\u002Fgles\u002Fbuffer_bindings_gles.cc(409)] … Float uniform should have a float type.\n[FATAL:flutter\u002Fimpeller\u002Frenderer\u002Fbackend\u002Fgles\u002Frender_pass_gles.cc(726)] Check failed: result. Must be able to encode GL commands without error.\nFatal signal 6 (SIGABRT) … (1.raster)\n","text","",[251,252,247],"code",{"__ignoreMap":249},[11,254,255],{},"Если по-человечески: у рендерера Flutter на Android два бэкенда, современный на Vulkan и старый на OpenGL ES, и на некоторых телефонах он выбирает старый. Этот телефон оказался как раз из таких. Бэкенд на OpenGL ES наткнулся в шейдерах карты на значение, которое не знал, как передать в GPU. За мелкий глюк он такое не считает: если GL-команду не удалось закодировать, это фатальная ошибка, так задумано. Поэтому движок роняет всё приложение целиком. И на Vulkan он при этом не переключается.",[11,257,258],{},"Без GPU API, на предыдущей бете, всё работало. Медленно. Эта версия живёт в ветке, которую так и не смёржили, а версия, которая падала, в git вообще не попала: ветка — это и есть запасной вариант. В апреле приложение получило более свежий flutter_map, всё так же на картинках.",[23,260,262],{"id":261},"новые-приложения-три-картографических-движка-за-одной-вьюхой","Новые приложения: три картографических движка за одной вьюхой",[11,264,265,266,271,272,275,276,279],{},"В июле мы начали делать партнёрское приложение для бизнеса: оно написано на Swift и собирается под Android через ",[94,267,270],{"href":268,"rel":269},"https:\u002F\u002Fskip.tools",[98],"Skip",". Первая карта в нём была растровой, и не от хорошей жизни: в SwiftUI от Skip ещё не было ни ",[251,273,274],{},"Map",", ни ",[251,277,278],{},"Canvas",", так что карта — это наши собственные тайлы-картинки, разложенные как изображения. Комментарий к ней гласит:",[209,281,282],{},[11,283,284,112],{},[190,285,286,287,290],{},"«Растр, а не вектор, потому что ему вообще не нужен движок рендеринга: тайл — это PNG, ",[251,288,289],{},"AsyncImage"," умеет его загрузить, и это одинаково работает на обеих платформах без единой нативной зависимости»",[11,292,293],{},"В тот же вечер приложение получило MapKit на iOS, а на следующее утро — MapLibre на Android, который рисует векторный стиль самой админки. Для Android-части нужно немного Kotlin: хук Skip для нативных Android-вьюх принимает Compose-функцию, а их на Swift не запишешь. Всё, что решает, что показывать на карте, остаётся на Swift. Все три сидят за одной вьюхой:",[209,295,296],{},[11,297,298,112],{},[190,299,300,301,304],{},"«MapKit на iOS, MapLibre на Android, растровая slippy map как подстраховка под обоими. Все три сидят за ",[251,302,303],{},"CuandoMapView",", так что выше этой линии выбор не виден, а если бэкенд откажет, мы потеряем карту покрасивее — и ничего больше»",[11,306,307,308,112],{},"Почему на iOS MapKit, а не тот же MapLibre? Один рендерер означал бы один набор стилей. Но это означало бы и отказ от собственной карты Apple и всего, что iOS с ней делает. Наше правило для пользовательского приложения, которое мы сейчас переписываем: ",[190,309,310],{},"«Никакие стилистические изыски не важнее самой карты»",[80,312],{":images":313},"[{\"src\":\"\u002Fimages\u002Fcuando_vector\u002Fpartner-android.jpg\",\"alt\":\"Cuando Partner на Android: MapLibre рисует наш векторный стиль\"},{\"src\":\"\u002Fimages\u002Fcuando_vector\u002Fpartner-ios.jpg\",\"alt\":\"Тот же экран на iOS — на MapKit и Apple Maps\"}]",[11,315,316],{},"У карты, под которой лежит запасной вариант, есть свой особый способ сломаться: молча. За две недели мы собрали три таких поломки.",[58,318,319,328,337],{},[61,320,321,324,325,112],{},[18,322,323],{},"Пустая карта, которая на самом деле перезагружалась."," Swift пересоздаёт объект-мост к Kotlin каждый раз, когда вьюха пересчитывается, так что флаг «а я уже применил этот стиль?» на нём всегда оказывался сброшенным. Карта применяла стиль примерно дважды в секунду, а применение стиля выбрасывает всё, что успело загрузиться. Поэтому ни один тайл так и не доехал. Выглядело это ровно как сломанный сервер тайлов. В новой версии пользовательского приложения та же пустая карта потом вернулась ещё дважды, по другим причинам, и заметка, которую мы себе там оставили, гласит: ",[190,326,327],{},"«Карта, которая рисует цвет фона и ни одного тайла, — это карта, которая перезагружает стиль»",[61,329,330,333,334,112],{},[18,331,332],{},"Векторная карта, которая втайне была растровой."," Когда новая версия пользовательского приложения позаимствовала карту у партнёрского, она продолжала искать Kotlin-класс в пакете партнёрского приложения. Поиск падал, ошибка проглатывалась, и Android тихо откатывался на растровую подстраховку. Чтобы это заметить, понадобились день и человек, который по-настоящему пользовался приложением: при перетаскивании карта казалась «слишком уж чувствительной», не поворачивалась, а зум шёл ступеньками. Это и есть карта из картинок. В приложении есть экран для разработчиков, где видно, каким движком нарисована карта, и всё это время там значилось ",[190,335,336],{},"«Raster tiles — fallback»",[61,338,339,342],{},[18,340,341],{},"Иконки, которые не рисовались."," Стили берут иконки из спрайта OpenMapTiles, который хостим не мы, а иконка, которой в нём нет, не рисуется вообще никак. Теперь наши пины рисуются прямо на устройстве из SVG-путей Maki.",[23,344,346],{"id":345},"почему-вектор-побеждает","Почему вектор побеждает",[11,348,349],{},"Вот сравнение для нашего региона, картинки против вектора:",[351,352,353,368],"table",{},[354,355,356],"thead",{},[357,358,359,362,365],"tr",{},[360,361],"th",{},[360,363,364],{},"Картинки (растр)",[360,366,367],{},"Вектор",[369,370,371,383,394,405,416,427,438],"tbody",{},[357,372,373,377,380],{},[374,375,376],"td",{},"Что на выходе",[374,378,379],{},"944 382 PNG-тайла на тему, зумы с 13-го по 19-й",[374,381,382],{},"один архив на все темы",[357,384,385,388,391],{},[374,386,387],{},"Хранение",[374,389,390],{},"около 15 ГБ на тему",[374,392,393],{},"58,67 МБ на всё плюс около 22 КБ на тему",[357,395,396,399,402],{},[374,397,398],{},"Смена внешнего вида",[374,400,401],{},"10–12 часов на тему на Mac mini, потом заливка",[374,403,404],{},"правка JSON-файла",[357,406,407,410,413],{},[374,408,409],{},"Глубже максимального зума данных",[374,411,412],{},"вчетверо больше тайлов на каждый уровень зума",[374,414,415],{},"телефон растягивает то, что есть",[357,417,418,421,424],{},[374,419,420],{},"Отдача тайла",[374,422,423],{},"Worker, хранилище, а при промахе — GL-рендер на виртуалке",[374,425,426],{},"Worker, edge-кэш, диапазон байтов из хранилища",[357,428,429,432,435],{},[374,430,431],{},"Подписи",[374,433,434],{},"запечены, на одном языке, поворачиваются вместе с картой",[374,436,437],{},"рисуются на телефоне, идут вдоль дорог, не переворачиваются",[357,439,440,443,446],{},[374,441,442],{},"Ещё один город",[374,444,445],{},"ещё одна пирамида, в каждой теме",[374,447,448],{},"файл побольше в том же бакете",[11,450,451],{},[228,452],{"alt":453,"src":454},"Одна растровая тема рядом со всем векторным регионом, площади в одном масштабе","\u002Fimages\u002Fcuando_vector\u002Fsize-ru.png",[11,456,457],{},"И цифры тут даже не главный аргумент. С картинками за каждое решение о том, как выглядит карта, платишь часами рендера, а за каждый новый регион — гигабайтами. С вектором и то и другое превращается в правки. А на сервере больше вообще ничего не рендерится.",[23,459,461],{"id":460},"остатки-mapbox","Остатки Mapbox",[11,463,464],{},"Два маленьких финала, оба за последние две недели.",[11,466,467,468,471,472,475],{},"4 августа мы наконец выпилили из Flutter-приложения CocoaPod MapboxMaps. Он приехал вместе с нативным SDK 20 августа 2024 года, а сам SDK исчез через пять дней. А вот под провисел почти два года. Его нигде не импортировали, но CocoaPods скачивает его у Mapbox, а для этого нужен секретный токен в ",[251,469,470],{},"~\u002F.netrc",". Так что ",[251,473,474],{},"pod install"," падал на каждой машине, которой такой токен не выдали.",[11,477,478],{},"11 августа спутник ушёл из выбора карт. Это был последний стиль, который ещё приходил от Mapbox. Если ты его уже выбрал, он пока останется.",[23,480,482],{"id":481},"что-дальше","Что дальше",[58,484,485,491,497],{},[61,486,487,490],{},[18,488,489],{},"Пользовательское приложение на Skip."," Когда оно выйдет, Android будет рисовать нашу векторную карту через MapLibre, а iOS — карту Apple. Пирамида картинок, пререндер и виртуалка для рендера смогут уйти на пенсию вместе с Flutter-приложением.",[61,492,493,496],{},[18,494,495],{},"Подписи на твоём языке."," С вектором язык подписей — это настройка в стиле, одно выражение на каждый слой подписей, а не перерендер. Наши подписи пока что только на латинице, и это следующее, что мы поменяем.",[61,498,499,502],{},[18,500,501],{},"Регион побольше."," Файл побольше в том же бакете.",{"title":249,"searchDepth":504,"depth":504,"links":505},2,[506,507,508,509,510,511,512,513,514],{"id":25,"depth":504,"text":26},{"id":85,"depth":504,"text":86},{"id":121,"depth":504,"text":122},{"id":203,"depth":504,"text":204},{"id":234,"depth":504,"text":235},{"id":261,"depth":504,"text":262},{"id":345,"depth":504,"text":346},{"id":460,"depth":504,"text":461},{"id":481,"depth":504,"text":482},"2026-08-15","Полтора года мы рисовали карту картинками. Почему Cuando переезжает на вектор: один файл на 58,67 МБ вместо 15 ГБ на тему, упавший рендерер во Flutter и три картографических движка за одной вьюхой в новых приложениях.",false,"md","\u002Fimages\u002Fcuando_vector\u002Fcover.jpg",true,{},"\u002Fru\u002Fposts\u002Fcode_n_shit\u002Fcuando_vector",{"title":5,"description":516},"cuando","ru\u002Fposts\u002Fcode_n_shit\u002Fcuando_vector",[251,524,527,528],"maps","cloudflare","UNMmxIfAVZC3yVsRZr5btpfEVBrxcaaPY4Tp4fuK4dA",[531],{"locale":532,"path":533,"title":534},"en","\u002Fposts\u002Fcode_n_shit\u002Fcuando_vector","Fifty-eight megabytes",{"slug":524,"title":536,"path":537,"chapters":538},"Как мы делали Cuando","\u002Fru\u002Fseries\u002Fcuando",[539,546,552],{"path":540,"title":541,"description":542,"date":543,"image":544,"draft":517,"series":524,"n":545},"\u002Fru\u002Fposts\u002Fcode_n_shit\u002Fcuando_bus_app","Мы сделали приложение про автобусы Торревьехи. Оно задаёт один вопрос","Cuando — приложение про автобусы Торревьехи, теперь в Google Play и App Store. Данных для старта не было, так что сеть мы нарисовали руками: 163 остановки, 16 маршрутов, приложение-секундомер и куча YAML. Рассказываю, как оно работает и почему графовая база продержалась восемь недель.","2025-01-24","\u002Fimages\u002Fcuando_bus_app\u002Fcover.jpg",1,{"path":547,"title":548,"description":549,"date":550,"image":551,"draft":517,"series":524,"n":504},"\u002Fru\u002Fposts\u002Fcode_n_shit\u002Fcuando_tiles","Платим за каждый тайл","Все решения про карту в Cuando до сих пор вытекали из того, как за эту карту брали деньги. Полгода Mapbox, одна неделя векторных тайлов, тепловая карта, которая решает, насколько глубоко можно зумить, и — с этой недели — карта, которую мы рисуем сами.","2025-02-22","\u002Fimages\u002Fcuando_tiles\u002Fcover.jpg",{"path":522,"title":5,"description":516,"date":515,"image":519,"draft":517,"series":524,"n":553},3,1790723721532]