# Формат данных о зданиях Документ описывает, что лежит в наборах данных, как из современного OpenStreetMap получается модель на 1897 год и где у этого метода границы. ## Общая схема ``` data/raw/*.json сырые ответы Overpass API (в публикацию не идут) ↓ tools/build-data.mjs data/build/*.geojson то, что грузит браузер ``` Плюс наборы, которые ведутся руками и в сборке участвуют как справочники (см. также раздел «Реестр объектов культурного наследия»): `landmarks.json`, `anachronisms.json`, `late-development.json`, `lost.json`, `street-names-1897.json`, и `scope.json` — граница охвата. И один набор, который **создаётся скриптом** и руками не правится, — как и `sources.json`: ``` data/building-dates.json ← tools/fetch-dates.mjs (даты построек из Викиданных) data/heritage-dates.json ← tools/fetch-egrkn.mjs (датировки реестра памятников) data/build/detail-meshes.json ← tools/fetch-detail3d.mjs + tools/mesh.mjs (объёмы) ``` ## Фильтр историчности `tools/build-data.mjs` пропускает каждый контур здания через такую цепочку. 1. **Граница охвата.** Центроид должен лежать внутри многоугольника `corePolygon` из `scope.json`. Это граница показа, а не историческая граница города: за ней остались Выборгская сторона, Охта, запад Васильевского острова и острова-парки, застроенные в основном после 1897 года. 2. **Список анахронизмов.** Если контур указан в `anachronisms.json`, он выбрасывается из модели и вместо него на карте появляется красная отметка с объяснением. 3. **Дата постройки.** Тег `start_date` разбирается терпимо: понимаются формы `1897`, `1897-05-01`, `1890..1895`, `early C18`. Берётся самый поздний уверенно читаемый год — именно он говорит, стояло ли здание в 1897-м. Если год больше 1897, контур выбрасывается. 4. **Запасные теги даты.** То же разбирается у `building:start_date`, `construction_date`, `building:year_built`, `year_of_construction`, `year_built`, `date_start` и `built` — их в выгрузке заметно меньше, но они ловят то, чего нет в `start_date` (например, `building:year_built` стоит у двух десятков домов 2010-х годов, а `year_of_construction` — у корпусов на Обводном канале и улице Шкапина). Для уровня достоверности `t=1` эти теги **не** используются: однозначно определённый смысл в OSM есть только у `start_date`, остальные ставят кто во что горазд, и годятся они ровно для одного вывода — «построено позже 1897 года». В список сознательно **не** входят похожие на дату теги, которые датой постройки не являются: `check_date` (когда данные в последний раз проверяли), `kgiop:inscription_date` (когда объект поставили на охрану), `note:building:year_built` (примечание) и `design:year` (год проекта, а не постройки; в охвате таких вообще нет). 5. **Дата из Викиданных** — `building-dates.json`, соединяется с контуром по тегу `wikidata` (он стоит у 469 контуров в охвате). Берётся свойство P571 «дата основания или создания». Если год больше 1897 — контур выбрасывается. Это единственный слой фильтра, который работает **по отдельному дому** и при этом опирается на внешний проверяемый источник: в карточке такого здания стоит прямая ссылка на элемент Викиданных. Что именно берётся и почему отброшены организации — см. `tools/fetch-dates.mjs` и раздел ниже. Если OSM и Викиданные спорят (в OSM дата не позже 1897, в Викиданных позже), здание **остаётся**, а спор пишется в `report.json` в поле `dateConflicts`. Два прямых источника разбираются руками, а не правилом. Сейчас поле пустое: единственный случай разобран — см. ниже. **Как разобран спор по дому С. С. Лентца** (13-я линия В. О., 16, `way/96598216`): в OSM стояло `start_date=1803`, в Викиданных — 1903 год. Решающим оказался не спор дат, а третий факт: автор дома, архитектор К. К. Шмидт, родился 21 декабря 1866 года, то есть здание 1803 года построить не мог. Авторство подтверждают и Викиданные (Q116723335), и статья русской Википедии «12—13-я линии Васильевского острова». Значит, в OSM опечатка. Дом занесён в `anachronisms.json` с этим объяснением — список анахронизмов проверяется раньше дат, поэтому правило на нём больше не спотыкается. 6. **Датировка из реестра объектов культурного наследия** — `heritage-dates.json`. Если реестр говорит, что дом построен позже 1897 года, контур выбрасывается. Связка двухступенчатая: сперва по номеру в реестре (тег `ref:egrokn`, его проставил человек), затем по адресу — и только когда адрес однозначен с обеих сторон. Подробнее — раздел ниже. Действует то же правило: если в OSM стоит `start_date` не позже 1897, а реестр говорит «позже», здание остаётся, а спор идёт в `dateConflicts`. 7. **Зоны поздней застройки** — `late-development.json`. Если центроид лежит внутри зоны, здание выбрасывается. Зона — это участок, который на планах Петербурга до 1900 года показан как поле, плац, пустырь, огород или вода; всё, что стоит там сегодня, появилось позже. Подробнее — раздел ниже. 8. **Стиль.** Тег `building:architecture` со значениями `constructivism`, `postconstructivism`, `stalinist_neoclassicism`, `soviet_modernism`, `brutalism`, `functionalism` сам по себе датирует здание XX веком. Значения `art_nouveau` (модерн) и `modern` в список **не** входят: петербургский модерн начался в самом конце 1890-х, граница проходит слишком близко к 1897 году, и, главное, в Петербурге очень распространена перелицовка старого дома в новом стиле — стены при этом остаются прежними. 9. **Высота.** По указу 1844 года карниз жилого дома в Петербурге не мог быть выше карниза Зимнего дворца — 11 саженей, около 23,5 м; предел сняли только в 1905 году. Поэтому `building:levels` ≥ 8 или `height` ≥ 30 м означает постройку XX века. Порог взят с запасом: семиэтажных зданий в выгрузке 209, и среди них могут быть дореволюционные дома, у которых в этажность попали мансарда и полуподвал, — семь этажей не режутся. Правило **не применяется** к храмам, колокольням, башням, воротам и памятникам (`building=church|cathedral|chapel|tower|triumphal_arch|…`, `man_made=tower`, `amenity=place_of_worship`, `tower:type`, `religion`). Иначе из модели вылетают Никольский морской собор 1762 года (46,5 м), Ростральные колонны 1810 года (32 м), Биржа (31,9 м) и колокольня Никольского собора (60 м). 10. **Тип постройки.** Выбрасываются `garage`, `garages`, `shed`, `roof`, `carport`, `kiosk`, `container`, `construction`, `service` и подобная современная дворовая мелочь, а также типы, появившиеся вместе с XX веком или заведомо временные: `garbage_shed`, `security_booth`, `storage_tank`, `swimming_pool`, `hangar`, `toilets`, `shelter`, `stadium`, `mall`, `supermarket`, `fuel`, `car_wash`, `ship`. Спорные типы в список **не** включены намеренно: `dormitory` (общежитие может занимать старый дом), `polyclinic`, `sports_centre`, `boathouse`, `gazebo` — беседкой в OSM размечен в том числе павильон Росси 1825 года, `greenhouse` — оранжерея Таврического сада. 11. **Размер.** Пятно меньше 30 м² считается сараем или ошибкой разметки. **Прямая дата бьёт косвенный признак.** Шаги 7–9 — это улики, а не документ. Если у здания есть дата постройки не позже 1897 года — в OSM (`start_date`), в Викиданных (P571) или в реестре памятников, — шаги 7–9 к нему не применяются: иначе из модели вылетели бы надстроенные в советское время старые дома. Противоречия «карта говорит пустырь, а тег говорит XVIII век» пишутся в `report.json` в поле `lateZoneConflicts` — их надо разбирать руками. Сейчас таких случаев нет. Ключевые здания из `landmarks.json` проходят фильтр **всегда**, шаги 3–11 к ним не применяются. Иначе их выбивают ошибки разметки: у Спаса на Крови в OSM стоит `start_date=1907` (это дата освящения, а не окончания строительства корпуса), а арка Новой Голландии размечена как `building=roof`. ## Зоны поздней застройки Самый доказуемый слой фильтра. Дата постройки в OSM есть у полутора сотен зданий из двенадцати тысяч, зато про землю под домом сказать что-то точное можно всегда: если на плане 1894 года участок пуст, значит всё, что стоит там сейчас, построено позже. Источник — **«Общий план С.-Петербурга, исправленный по 1894-ый год»**, приложение к справочнику «Весь Петербург», картографическое заведение А. Ильина, 250 саженей в дюйме (около 1:21 000). План издан в 1894 году и находится в общественном достоянии. В репозиторий скан не кладётся: он нужен только при проведении границ, а не при сборке. Как проведены границы: 1. Скан привязан к WGS84 преобразованием подобия по трём опорным точкам — Петропавловский собор, Дворцовая площадь, Смольный собор. Масштаб вышел 0,617 пикселя на метр (скан около 330 dpi), поворот 0,9°, невязка на опорных точках меньше 10 пикселей, то есть меньше 20 метров. 2. Поверх скана отрисованы современные контуры зданий из OSM и граница охвата. 3. Каждая зона обведена вручную по свободному от застройки полю, **с отступом внутрь** от ближайших нарисованных на плане строений. Различать застроенное и незастроенное на этом плане нужно осторожно: в центральных частях города план рисует отдельные дома, а на окраинах заливает квартал целиком цветом полицейского участка, не показывая строений. Светлая заливка квартала — это застройка, а не пустырь. Незастроенная земля остаётся белой с межевой штриховкой, вода — с волнистой. Автоматический поиск «светлых» участков на скане на этом и спотыкается, поэтому каждая зона проверена глазом, а отвергнутые кандидаты перечислены в `rejected` внутри самого набора. Формат записи: ```json { "id": "semenovsky-plac", "name": "Семёновский плац (Пионерская площадь и квартал по Звенигородской улице)", "in1894": "что показано на плане 1894 года", "built": "когда участок застроен на самом деле", "source": "ilyin-1894", "polygon": [[30.33391, 59.92154], "…"] } ``` `polygon` — замкнутый контур `[долгота, широта]` в WGS84, замыкающая точка не повторяется. `source` ссылается на запись в `sources` того же файла. В `report.json` для каждой зоны пишется, сколько зданий она сняла, — это единственный способ заметить, что зона перестала работать или, наоборот, захватила лишнее. **Проверка по плану 1897 года.** Граница зоны проводится по плану 1894 года, но целевой год у модели — 1897, и трёхлетний разрыв надо закрывать. Для этого каждая зона перепроверена по **«Общему плану С.-Петербурга, исправленному по 1897-ой год»** (то же заведение А. Ильина, 230 саженей в дюйме, около 1:19 320). У него в легенде прямо написано: «Закрашенныя пространства означаютъ застроенныя участки» — то есть цвет означает застройку, белое поле означает её отсутствие. Все зоны на 1897 год остаются белыми. Границы по плану 1897 года не проводятся: его скан сделан со сложенного листа, складки дают местные искажения, и привязка подобием держится около 40 метров против 20 на плане 1894 года. Этого хватает, чтобы подтвердить пустое поле размером в квартал, и не хватает, чтобы работать по отдельным домам. **Чего зонами сделать нельзя.** Каменные доходные дома Петроградской стороны почти целиком относятся к 1900—1914 годам, но участки под ними не пустовали: на плане 1894 года там сплошная мелкая деревянная застройка, а план 1897 года закрашивает эти кварталы почти целиком, что по его же легенде означает «застроенные участки». Значит, менялись дома, а земля застроенной была. Сказать «здесь ничего не было» нельзя ни по одному из двух планов, поэтому зоны там нет, и Петроградская сторона в модели остаётся заметно новее остального города. Поштучно её закрывают только прямые даты по конкретным домам — сейчас это Викиданные и список анахронизмов. То же с товарной станцией Николаевской железной дороги: пути на плане есть, но вместе с пакгаузами. ## Настоящие объёмы вместо плоских призм Весь город в модели — плоские призмы: контур из OSM, выдавленный на одну высоту. Для отдельных объектов внутри области подробной проработки (`data/detail-scope.json`) строится настоящая геометрия. Источник — схема **Simple 3D Buildings** в OpenStreetMap. В ней постройка описана набором частей `building:part`, у каждой своя подошва `min_height`, своя вершина `height` и своя форма кровли `roof:shape` с `roof:height`. Это измеренные данные, а не рисунок по мотивам. Конвейер: ``` data/detail-scope.json (ручной: граница области и список объектов) ↓ tools/fetch-detail3d.mjs → data/raw/detail3d.json ↓ tools/mesh.mjs (в составе build-data.mjs) data/build/detail-meshes.json → SimpleMeshLayer в браузере ``` **Чем рисуется.** `SimpleMeshLayer` из deck.gl принимает сырые типизированные массивы `POSITION`/`NORMAL`/`indices`, поэтому сетка отдаётся прямо в местных метрах относительно точки привязки. Загрузчик glTF при этом не нужен: в нашей сборке deck.gl его и нет (`registerLoaders` не экспортирован), и вендорный вес проекта не вырос ни на байт. В данных сетки разложены по цветам, а на сайте каждый объект сливается в одну сетку с цветом по вершинам (`COLOR_0`) и рисуется одним слоем — см. раздел «Реконструкция формы». **Что сборщик умеет и чего не делает.** По описанию строятся: | Форма | Как собирается | |---|---| | `flat` | плоская крышка | | `pyramidal` | веер к одной вершине | | `hipped` | вальмовая: конёк, укороченный с торцов на половину ширины пятна, по длинным сторонам трапеции, по торцам треугольники | | `gabled` | два ската от конька через центр | | `skillion` | наклонная плоскость плюс клин стены под ней | | `dome` | кольца, стянутые к центру по четверти окружности | | `half-dome` | те же кольца, но стянутые к середине прямой стороны пятна, плюс вертикальный срез | | `round` | цилиндрический свод: пятно сжимается только поперёк оси, вдоль оси остаётся во всю длину | Форма, которую построить не умеем вовсе, накрывается плоской крышкой и попадает в `detail.roofFallback` — молча подменять призмой нельзя. Сейчас это поле пустое. Части без высот не берутся и считаются в `detail.skippedNoHeight`. **Что осталось приближением.** Приближение — это не «упростили ради скорости», а «в данных нет того, что нужно построить». Каждый случай попадает в `detail.roofApprox` вместе с причиной и называется в карточке объекта. Сейчас таких два: - **Луковичная глава** (`onion`, Казанский собор): в разметке есть только высота и пятно, самого профиля — выноса и перехвата — нет. Собрана куполом. - **Вальмовая кровля на непрямоугольном пятне** (кровля павильона Росси): пятно ступенчатое, и конёк проведён по правилу — вдоль длинной оси, с укорочением на половину ширины, — а не снят с чертежа. На прямоугольном пятне то же построение точное, и остальные шесть вальмовых кровель в области собраны без оговорки. **Полукупол строится точно**, и направление среза берётся не из тега, а из самого пятна: у полукруглого контура есть ровно одна прямая сторона, заметно длиннее остальных, и весь контур лежит по одну её сторону. Эта сторона и есть плоскость среза. Если такой стороны нет, а `roof:direction` не задан, срез взять неоткуда — тогда собирается полный купол и пишется приближение. **Направление конька.** Порядок источников: 1. `roof:direction` — прямое измерение, берётся как есть; 2. `roof:orientation` — `along` вдоль длинной оси пятна, `across` поперёк; это значение описано в схеме S3DB, а не придумано нами; 3. ничего — конёк кладётся вдоль длинной оси и случай считается в `detail.roofAssumedDirection`. Поддержка `roof:orientation` — не мелочь: восемь двускатных кровель в области (в том числе четыре фронтона портиков Исаакиевского собора) размечены `across`, и до её появления конёк на них лежал поперёк правильного. Длинная ось пятна — длинная сторона наименьшего описанного прямоугольника, а не самое длинное ребро: в OSM длинная сторона контура часто разбита промежуточными точками, и самым длинным ребром оказывается короткий торец. **Сборка колец у мультиполигонов обязательна.** Overpass отдаёт границу мультиполигона кусками, и без склейки кусков в кольцо крупные здания — Адмиралтейство, Исаакиевский и Казанский соборы — просто не находятся, а их части остаются «ничьими». Первый прогон инвентаризации из-за этого показал один пригодный объект вместо семи. Досочинения формы здесь нет по определению: **чего нет в разметке, того нет и на карте**. У Александровской колонны, например, фигура ангела как геометрия не описана — части, занимающие её место, размечены как крест. Поэтому над полусферой стоит крест, а фигуры нет, и в карточке об этом сказано прямо. **Цвет — по частям и по материалу, текстур нет.** Цвета берутся из общей палитры `data/palette.json`. У объекта в `detail-scope.json` есть правила `paint`: первое подошедшее решает — по идентификатору части (`ids`), по материалу (`material`), по форме кровли (`shape`), по высоте (`minFrom`, `maxTo`); `wall` красит стены части, `roof` — её завершение (купол, шатёр, шпиль, скаты). Ни одно не подошло — цвет по `building:material` через `materialColours`. Геометрия обмера при раскраске не меняется. Тег `building:colour` из OSM не используется — это глазомерная прикидка картографа. ## Что в области вообще размечено Инвентаризация всей области подробной проработки (сентябрь 2026): | | | |---|---| | частей `building:part` всего | 623 | | у них нашлось здание-владелец | 599 | | зданий, имеющих части | 154 | | **пригодны полностью** (все части с высотами, частей ≥3) | **7** (197 частей) | | полны, но слишком мелки (1–2 части) | 1 | | размечены частично | 3 | | без единой высоты | 143 (383 части) | Семь пригодных: Павильон Росси (122 части), Александровская колонна (37), Исаакиевский собор (14), безымянное здание `way/233051448` (9), Здание Пробирной палаты (7), Главное Адмиралтейство (5), Казанский собор (3). Вывод, который из этого следует: метод дёшев ровно там, где кто-то уже обмерил здание и внёс обмер в OSM. Таких объектов в парадном ядре города — единицы процентов от числа зданий с частями, и почти все остальные (143 из 154) имеют части без единой высоты, то есть разметку начали и бросили. **Частично размеченные не собираются.** У Зимнего дворца высоты есть у одной части из четырёх, у гостиницы «Астория» — у одной из двух. Собрать такой объект значит показать половину здания объёмом, а половину коробкой, что хуже честной коробки целиком. Они ждут доразметки. **Плоская призма и объём не спорят.** Объект остаётся в `buildings.geojson` как был — числа фильтра от появления объёма не меняются, — но получает флаг `d`, и сайт не рисует призму там, где есть настоящая геометрия. Выбор кликом, карточка и раскраска по достоверности работают для объёма ровно так же, как для призмы. ## Зимний дворец: что нужно, чтобы взяться Разбор сентября 2026 года — почему главное здание области до сих пор коробка и при каких условиях перестанет ею быть. ### Что о нём есть в OSM Ничего, из чего строить объём. У дворца ровно одна часть с высотой — сам контур здания: `relation/69424` несёт `building:part=base` и `height=22`. Остальные три части внутри контура — служебные постройки во дворах площадью 44, 50 и 112 м², и высот у них нет. То есть в разметке нет ни карниза, ни кровли, ни балюстрады: всё здание — одна призма 34 586 м² высотой 22 м. Зато **план в OSM подробный**: мультиполигон из 17 внешних участков и 6 внутренних (дворы), 149 точек. Ризалиты, уступы и дворы в плане уже есть — не хватает только вертикального членения. ### Что опубликовано из обмеров | Источник | Что даёт | Доступ | |---|---|---| | Чертежи Ф.-Б. Растрелли (планы, фасады, разрезы), 1750-е | размеры: на листах того времени есть масштабная линейка в саженях (1 саж. = 2,1336 м) | оригиналы в РГИА (ф. 485), Эрмитаже, музейных собраниях; онлайн в открытом доступе нет | | Чертежи восстановления после пожара 1837 года (Стасов, Брюллов) | состояние, ближайшее к 1897 году: кровля и стропила после пожара сделаны заново | архивы, оцифровка идёт, открытого доступа нет | | Денисов Ю. М., Петров А. Н. «Зодчий Растрелли: материалы к изучению творчества». Л.: Госстройиздат, 1963 | первый полный иллюстрированный научный каталог работ Растрелли с воспроизведением чертежей | печатное издание, библиотека | | «Эрмитаж. История строительства и архитектура зданий». Л., 1989 | история строительства всех зданий ансамбля с чертежами | печатное издание, частично выложено библиотеками | | Лазерное сканирование и фотограмметрия южного фасада, 2008 | ортофотоплан фасада — самые точные размеры из существующих | коммерческая работа, в открытый доступ не выложена | | Викисклад | фотографии и виды | **чертежей нет**: у категории Winter Palace нет подкатегории планов и разрезов | Отсюда первое правило отбора: **лист без масштабной линейки — картинка, а не обмер**. Фасад, воспроизведённый в книге с обрезанной линейкой или с нарушенными пропорциями, размеров не даёт, даже если он красив и подлинен. ### Из чего дворец состоит как объём - Каре из четырёх корпусов вокруг Большого двора: 210 м по Неве, 175 м со стороны Адмиралтейства, высота 23,5 м, три основных этажа. - Ризалиты и уступы — **уже есть в плане OSM**, отдельных частей не требуют. - Карниз и кровля над каждым корпусом: форму даёт только поперечный разрез, из фасада её не вывести. - Балюстрада по периметру кровли с вазами и статуями. Тут важная тонкость для 1897 года: каменные фигуры меняли на металлические в 1892–1902 годах, то есть **в 1897 году на кровле стояли и те, и другие**. - Купол церкви и малые надстройки над кровлей. ### Как быть со скульптурой Отдельные фигуры мы не лепим: статуя — не архитектурная форма, её нет ни в одном размере, и «похожая» фигура запрещена тем же правилом, что и «похожий» ангел на Александровской колонне. Предлагаемый порядок: 1. балюстрада строится как сплошной парапет — это архитектурный элемент с размером на фасадном чертеже; 2. тумбы под фигурами строятся, если чертёж даёт их шаг и положение, — тумба тоже архитектура; 3. сами фигуры не строятся, а называются в карточке, с оговоркой про незавершённую к 1897 году замену камня на медь. ### Во что это выльется у нас Считать надо не «здание», а части. Оценка по уже собранным объектам (8 305 треугольников на 438,8 КБ — около 53 байт на треугольник без сжатия и 10,4 байта в gzip): | Элемент | Частей | Треугольников | |---|---|---| | основной объём до карниза по контуру из 149 точек | 1 | ≈ 450 | | кровли корпусов | 6–8 | ≈ 200 | | парапет балюстрады по всему периметру | 1 | ≈ 500 | | тумбы под фигурами (если чертёж даёт шаг) | ≈ 176 | ≈ 2 100 | | купол церкви и надстройки | 2–4 | ≈ 700 | | **итого** | **≈ 190** | **≈ 4 000** | Это примерно 210 КБ без сжатия и 45 КБ в gzip — то есть один дворец весит около половины всего, что собрано сейчас. По весу задача проходит. ### Где такие части держать и как их называть **Не в OSM.** OSM описывает сегодняшнее здание, а наши размеры описывают 1897 год — с балюстрадой в разгар замены фигур и с кровлей, какой она была до позднейших переделок. Внести это в OSM значило бы внести туда заведомо неверные данные. Поэтому собственный набор `data/detail-parts.json` в том же словаре, что и S3DB (`building:part`, `min_height`, `height`, `roof:shape`, `roof:direction`, `building:material`), — тогда его строит тот же `tools/mesh.mjs` без второй ветки кода. У каждой части обязательна ссылка на лист: издание, номер листа и масштаб. Часть без ссылки на чертёж не строится. И объём, собранный по чертежам, **нельзя смешивать с обмеренным**. Признак в данных: `origin` со значением `osm` или `drawings`, у каждой части своё. Заголовок в карточке меняется вместе с ним: «Объём построен по измеренным данным OpenStreetMap» против «Объём построен по историческим чертежам» — со списком листов. ### Вывод по чертежам По чертежам браться нельзя: без поперечного разреза кровля будет выдумана. Работа упирается не в моделирование, а в доступ к трём листам — фасад с масштабной линейкой, поперечный разрез с кровлей, план кровли, — то есть в поход в библиотеку и архив. **Планка менялась дважды.** Сначала владелец проекта разрешил современные фотографии как источник формы и текстуры (2026-09-25, утро); одинаковые текстурные плитки на всех зданиях результата не дали — город выглядел покрашенным под одну гребёнку. Решение того же дня: **от текстур отказаться полностью**, а каждому ключевому зданию дать узнаваемую форму и цвет по частям — так, как уже была собрана Александровская колонна. Разбор выше не отменяется: он объясняет, чего в реконструкции заведомо нет и что даст обращение к чертежам. ## Реконструкция формы Второй способ получить объём, отдельный от обмерного. Собирает его `tools/reconstruct.mjs` по ручному набору `data/reconstructions.json`, цвета берёт из общей палитры `data/palette.json`. **Чем отличается от обмера.** В обмерном способе (`tools/mesh.mjs`) форма целиком выведена из разметки OSM. В реконструкции из OSM берётся только план, а всё, что над ним, собирается из простых форм по описанию: высоты — из справочников, членения — по фотографиям и чертежам, источники перечислены у каждого объекта в поле `sources`. | | обмер | реконструкция | смесь | |---|---|---|---| | признак в данных | `origin.form: "osm"` | `origin.form: "reconstruction"` | `origin.form: "mixed"` | | заголовок в карточке | «Объём построен по измеренным данным» | «Объём реконструирован, а не обмерен» | «Объём: часть измерена, остальное реконструировано» | | откуда план | OSM | OSM | OSM | | откуда высоты | `building:part` | справочники, фотографии | обмер + справочники | | цвет | правила `paint` в `detail-scope.json` | роли `colours` в `reconstructions.json` | и то и другое, список цветов общий | **Смесь** получается, когда у одного здания есть и обмер, и описание в `reconstructions.json`: у Казанского собора обмерены только барабан и купол, у Исаакиевского собора звонницы обрываются плоским верхом, у Адмиралтейства пригоден только шпиль. Сборка складывает сетки в один объект; обмер не переписывается, реконструкция лишь достраивает. **Исключение из правила «обмер не трогаем» — одно, и оно записано.** У Адмиралтейства корпуса размечены высотой 10 м, куб башни — 15 м, а по справочным данным здание высотой 16,5 м. С такими высотами Адмиралтейство оказывалось ниже домов в собственном дворе. Четыре части отброшены полем `excludeParts` в `detail-scope.json` с причиной; причина уходит в `report.detail.excludedParts` и в карточку. Из обмера взят только шпиль. ### Словарь частей Координаты — местные метры от точки привязки (центр внешнего кольца OSM), x на восток, y на север; направления компасные. | Тип | Что строит | |---|---| | `facade` | стандартная «этажерка» петербургского фасада по пятну OSM: гранитный цоколь с выносом, стены, ритм пилястр/полуколонн, карниз с выносом, аттик, парапет-балюстрада с фигурами, кровля скатами к коньковой площадке | | `portico` | стилобат, ряд (или два ряда) колонн, антаблемент, фронтон, аттик, статуи; точка `at` притягивается к ближайшему ребру контура, фронт смотрит по нормали ребра; `recess` — лоджия внутри пятна, стена в этом месте вынимается | | `columns` | ряд колонн по отрезку или по дуге | | `beam` | брус по ломаной или дуге — антаблемент колоннады | | `mass` | призма над частью пятна: `within` — выпуклая область, `without` — вычитаемые выпуклые области (прямоугольники, дуговые полосы) | | `box`, `prism`, `gable` | брус, призма по точкам (верх может быть наклонным), двускатное завершение | | `lathe` | тело вращения: барабан, купол, луковица, шлем, шпиль, сфера или свой профиль | | `band`, `roof`, `pilasters` | карниз, кровля и пилястры отдельно — для зданий, чьи стены уже есть в обмере | | `cross`, `figure`, `horse`, `quadriga` | крест, условная статуя, конная статуя (стоит, идёт шагом, на дыбах), колесница | **Вычитание областей.** План триангулируется, треугольники обрезаются по выпуклым областям (отсечение полуплоскостями), стены ставятся по рёбрам, которые не делятся с соседним куском. Кусок, не пересекающийся с областью, не режется вовсе — иначе двадцать четыре дуговые полосы колоннады Казанского собора крошили бы весь план на осколки. Цена — лишние стенки внутри массива у соседей разрезанных кусков; их не видно. **Фигуры — силуэты, а не скульптура.** Статуи, кони, колесницы собраны из десятка граней: нужны рост, положение, направление взгляда и поза (конь на дыбах у Медного всадника и Николая I, шагом — у Петра перед Михайловским замком). Портретного сходства нет, и карточка каждого памятника говорит об этом первой строкой блока. ### Цвет: палитра и роли `data/palette.json` — единый словарь цветов для обмера и реконструкции. Цвет там либо **материал** (гранит, мрамор, пудостский камень, кирпич, бронза, золочение) — его вид задан самим материалом, — либо **окраска** (штукатурка, кровельное железо). У объекта в `reconstructions.json` роли `wall`, `trim`, `roof`, `plinth`, `statue`… указывают на ключи палитры; часть ссылается на роль или прямо на ключ. Подтверждена ли окраска фасада именно на 1897 год, решает не палитра, а объект: `colour.documented`. Документированы Зимний дворец (тёмная охра 1860–1901 годов со светлыми колоннами и красной кровлей), Казанский собор, Исаакиевский собор, Мраморный дворец, арка Новой Голландии (там цвет — это материал) и памятники. У остальных взят сегодняшний или типичный для эпохи тон, приглушённый; такие объекты перечислены в `report.reconstruction.noColourSource`, а карточка несёт красную оговорку. Здания Дворцовой площади (Главный штаб, штаб Гвардейского корпуса) и корпуса Эрмитажа окрашены в дворцовую палитру 1897 года **по ансамблю** — в 1901 году Главный штаб перекрасили вместе с дворцом, — и это названо допущением, а не сведением. **Цвет по вершинам.** На сайте каждый объект — одна сетка и один слой `SimpleMeshLayer`: цвет части уходит в атрибут `COLOR_0`, слой умножает его на `getColor`, поэтому в обычном режиме `getColor` белый, а в режиме достоверности подставляется сетка без `COLOR_0`, и объект красится целиком. Нормалей у реконструкций в данных нет — сайт считает их по порядку обхода вершин; сборщик выправляет порядок каждой грани по вектору «наружу». Координаты реконструкций лежат целыми дециметрами (`scale: 0.1`). ### Здания, чей сегодняшний вид не годится на 1897 год | Здание | Что не так | Как поступили | |---|---|---| | Спас на Крови | строился 1883–1907; к 1897 году своды сомкнуты и главы поставлены, отделка шла до 1907 года | **по решению владельца проекта** — законченная форма в строительных лесах (часть `scaffold`); стадия описана в карточке, расположение лесов условное | | Конногвардейский манеж | мраморных Диоскуров убрали в 1840 году, вернули в 1954-м | портик без статуй | | Пассаж | сегодняшний фасад — перестройка 1900 года | объём показывает место и размер, колер нейтральный | | Большой Гостиный двор | отделка Бенуа 1886–1887 годов снята в 1947–1948 | в карточке сказано, что в 1897 году здание было наряднее | | Михайловский дворец | в 1895–1898 годах перестраивался внутри под Русский музей | снаружи облик Росси, оговорка в карточке | | Публичная библиотека | корпус по Садовой строился в 1896–1901 | оговорка в карточке | | Городская дума | оптический телеграф на башне стоял в 1839–1850-х | в 1897 году его нет и в модели | Памятники — только открытые до 1897 года: Медный всадник (1782), Пётр I у Михайловского замка (1800), Суворов (1801, на нынешнем месте с 1818), Кутузов и Барклай-де-Толли (1837), Крылов (1855), Николай I (1859), Екатерина II (1873), бюсты Жуковского (1887), Пржевальского (1892), Лермонтова и Гоголя (1896). Бюст Глинки в Александровском саду (1899) и Александр III на Знаменской площади (1909) — позже, их нет. ## Рельеф Конвейер: `node tools/fetch-dem.mjs` → `node tools/build-terrain.mjs` → `node tools/build-data.mjs`. - **Источник** — Copernicus DEM GLO-30 (тайл N59 E030 с открытого зеркала AWS, лицензия GLO-30 Public — свободно с указанием авторства; указано в подвале сайта и в «Источниках»). Это DSM: крыши и кроны в отметках. - **Земля (DTM)** — `tools/build-terrain.mjs`: пиксели под контурами зданий OSM выбрасываются, берётся 5-й процентиль отметок в круге ~240 м, затем гаусс ~120 м. Контроль: Дворцовая 1,7 м, Исаакиевская 2,1, Сенная 3,3, Знаменская 7,6. Погрешность — метр-два. - **Поверхность** — `tools/terrain.mjs`: вода по OSM (`water.geojson` + полосы по `waterways`) на сетке 4 м → уровень воды 0 м; суша — билинейно по DTM, не ниже 1 м. Отсюда уступ у набережных. - **Тайлы** — raster-dem в кодировке Mapbox, 256 px, z10–13, охват `scope.json`: 30 PNG, ~232 КБ (PNG уже сжат). Свой кодировщик PNG — `tools/png.mjs`. - **Земля под объектами** — поле `g` у зданий, подписей, отметок и настилов мостов, поле `ground` у объёмов: наименьшая отметка суши по точкам контура (вода в расчёт не входит). Сайт ставит объект на `g × преувеличение`, MapLibre умножает тайлы на то же число — объект стоит ровно на земле при ×1, ×3, ×5. - **Сайт:** MapLibre `terrain` + слабая отмывка; deck.gl в режиме `interleaved: true`, чтобы рельеф честно перекрывал подножия домов; подписи и отметки рисуются поверх всего (`depthCompare: always`). - **Мосты** — `bridges.geojson`: короткие (до 120 м) мосты OSM в охвате, плоская лента на уровне высшей из набережных. Без неё улица у канала проваливалась бы к воде. - **1897 год:** рельеф отдельно не восстанавливался — документированных изменений отметок центра в наших данных нет; береговая линия — современная из OSM. ## Рядовая застройка: прикидка на область Поштучно, как дворец, рядовые дома не сделать: в области подробной проработки их 1 102, средний контур — 16,7 точек. **Сколько геометрии.** На дом: стены 34 треугольника, карниз-парапет 34, кровля около 50 — итого ≈ 120. На 1 102 дома ≈ **132 000 треугольников**. **Возить готовые сетки нельзя**: по измеренной ставке это около 1,4 МБ в gzip — больше, чем весь нынешний вес объёмов. **Значит, строить в браузере.** Контуры и высоты уже лежат в `buildings.geojson`; выдавить из них цоколь, стены, карниз и кровлю — та же арифметика, что в `tools/reconstruct.mjs`, только на клиенте. Добавочный вес данных — ноль, цвет — из той же палитры по ролям. Рисовать одним слоем на всю область, а не по слою на дом. ## Подписи улиц на карте Кегль подписи задан **в метрах на местности** (`sizeUnits: 'meters'`), а не в пикселях. С пикселями подпись имела постоянный экранный размер, и на общем плане казалась крупнее, чем вблизи, — ровно наоборот тому, чего ждёшь от карты. В метрах подпись живёт вместе с городом: приближаешь — растёт, отдаляешь — мельчает. `sizeMinPixels`/`sizeMaxPixels` не дают ей ни расплыться, ни стать нечитаемой. Раз подписи при отдалении мельчают, их помещается на экран слишком много, и город скрывается под ними. Поэтому набор подписей меняется ступенями по масштабу (`visibleLabels()` в `assets/js/app.js`): до 12,6 подписей нет вовсе, до 13,8 показываются только магистрали, до 14,9 — магистрали и улицы второго порядка, дальше все. Ступень берётся из поля `p` в `street-labels.geojson`. Текст белый и полужирный. Полужирное начертание — потому что тонкие засечки PT Serif Regular в мелком кегле рассыпаются в атласе SDF; начертание 700 уже лежит в `assets/fonts`, так что читаемость выросла без единого лишнего байта и без смены гарнитуры. Оба начертания ждём через `document.fonts.load` до создания слоя: атлас глифов строится синхронно, и неподгруженный шрифт молча подменяется системным. Под текстом — тёмная плашка (`background: true`), а не обводка: кровли в модели почти белые, и белый текст на них без плашки исчезает. Обводкой это не лечится — `outlineWidth` в TextLayer задаётся долей кегля, а не пикселями, и заметное значение заливает глиф целиком. ## Раскраска по достоверности Уровень `t` виден не только в карточке: в панели настроек есть переключатель «Раскрасить по достоверности», который разводит объёмы по тону — выверено вручную, есть дата из источника, даты нет. Рядом легенда с долями, они считаются по самой модели, а не по отчёту, поэтому число на экране всегда совпадает с тем, что нарисовано. По умолчанию город остаётся монохромным: это карта, а не дашборд, и обычный вид — то, ради чего проект делался. Но доли уровней видны в панели всегда, даже при выключенной раскраске, так что слабое место модели не спрятано. Палитра — сине-оранжевая, а не красно-зелёная: она различима при самых частых видах цветовой слепоты. Тона светлые и приглушённые намеренно: объёмы затеняются материалом слоя, и насыщенный цвет на теневых гранях уходит в грязь, а светлая база читается и в тени. Значения — в константе `TRUST_COLORS` в `assets/js/app.js`, те же цвета продублированы в CSS для плашек легенды. ## Уровни достоверности У каждого здания в `buildings.geojson` есть свойство `t`: | `t` | что значит | как показано в карточке | |----|------------|--------------------------| | 2 | ключевое здание, сведения выверены вручную | полная справка, источники, изображение | | 1 | есть дата постройки не позже 1897 года — в OSM (`start_date`) либо в Викиданных (P571) | «Дата постройки из OSM» или «из Викиданных» (со ссылкой на элемент), без справки | | 0 | даты нет вовсе | «Сведения уточняются» плюс прямое предупреждение, что здание могло быть построено и позже | Уровень 0 — это большинство домов. Так и должно быть: фильтр по дате работает только там, где дата в OSM проставлена, а проставлена она у сотни с небольшим зданий из двенадцати тысяч. Зоны поздней застройки эту дыру закрывают лишь частично — там, где есть карта и участок на ней пуст. Исторический центр застраивался преимущественно до 1917 года, но это статистика, а не доказательство по каждому дому, и карточка об этом говорит прямо, а не делает вид, что дом точно дореволюционный. ## Свойства объектов ### `buildings.geojson` | поле | смысл | |------|-------| | `i` | идентификатор OSM, например `relation/3229838` | | `h` | высота объёма в метрах | | `hs` | откуда высота: `manual`, `levels`, `height-tag`, `default` | | `t` | уровень достоверности (см. выше) | | `l` | идентификатор ключевого здания из `landmarks.json`, если есть | | `y` | год постройки, если он не позже 1897 | | `ys` | откуда взят год: поля нет — из OSM, `wikidata` — из Викиданных, `egrkn` — из реестра памятников | | `q` | идентификатор элемента Викиданных, если год взят оттуда | | `r`, `rn`, `rd`, `rb` | сведения реестра памятников: номер, наименование, датировка как она записана, и как сопоставлено (`ref` или `address`). Ставятся всегда, когда запись реестра нашлась, — в том числе когда датировку прочитать не удалось и уровень достоверности остался `0` | | `n` | название из OSM, если есть | | `a` | современный адрес из OSM, если есть | **Как считается высота.** Сначала ручное значение из `landmarks.json` (только для восьми зданий, чья высота общеизвестна). Затем `building:levels` × 3,5 м плюс 2 м на кровлю: 3,5 м — обычная высота этажа доходного дома конца XIX века. Затем тег `height`. Если ничего нет — два этажа для пятна меньше 150 м², четыре для остальных. **Чего высота не отражает.** Объём строится выдавливанием пятна застройки на одну высоту. Купола, шпили, скаты кровель, дворовые флигели разной этажности не моделируются. Поэтому Исаакиевский собор выглядит параллелепипедом высотой 101,5 м, а не собором: 101,5 — это документированная высота с крестом, и она честнее, чем выдуманная «высота карниза», но форма при этом условна. ### `street-labels.geojson` | поле | смысл | |------|-------| | `n` | название на 1897 год (или современное, если оно не менялось) | | `m` | современное название, если оно отличается | | `c` | уверенность в переименовании: `high` или `medium` | | `o` | то же название в дореформенной орфографии | | `r` | угол поворота подписи в градусах | | `p` | приоритет при отсеве пересекающихся подписей | ### `markers.geojson` | поле | смысл | |------|-------| | `kind` | `landmark`, `anachronism` или `lost` | | `l` | идентификатор записи в соответствующем наборе | | `n` | название для подсказки | ## Ручные наборы ### `landmarks.json` Ключевое здание описывается так: ```json { "id": "st-isaacs", "osm": "relation/3229838", "wikidata": "Q215423", "name1897": "Исаакиевский собор", "nameModern": "Исаакиевский собор", "category": "Храм", "built": "1818—1858", "architect": "О. Монферран", "addressModern": "Исаакиевская площадь, 4", "addressNote": "…", "in1897": "…", "note": "…", "height": 101.5, "heightNote": "…" } ``` Правила ведения: - **`osm` обязателен** и указывает на реальный контур. Координаты в набор не пишутся вообще: их даёт OSM. Так исключается целый класс ошибок, когда объект оказывается на соседнем участке. - **`address1897` заполняется только при уверенности.** Нумерация домов на Невском и соседних улицах менялась в 1880-х годах. Если исторический номер не подтверждён источником, поле не заполняется, а показывается современный адрес с пометкой. Выдуманный исторический адрес хуже отсутствующего. - **`wikidata`** нужен `tools/fetch-sources.mjs`: по нему находятся статья русской Википедии и категория Викисклада. - **`height`** ставится только там, где высота общеизвестна и опубликована. - **`status: "under-construction"`** — для того, что в 1897 году ещё строилось (Спас на Крови, Русский музей в Михайловском дворце). ### `anachronisms.json` Объекты, которые есть в современном OSM, но которых в 1897 году не было. Поле `osm` указывает исключаемый контур, `why` объясняет, почему. Раздел `outOfScope` — то, чего нет и в охвате модели (дацан, электрический трамвай), он нужен как справка, в сборке не участвует. Набор рассчитан на объекты, о которых есть что рассказать: от анахронизма остаётся красная отметка на карте и карточка с датой постройки, архитектором и объяснением, что было на этом месте раньше. Всё, что просто «построено позже» и ничем не примечательно, отсеивается общими правилами фильтра и в этот набор не попадает. ### `late-development.json` Зоны поздней застройки — см. отдельный раздел выше. Кроме `zones` в файле есть `sources` (список использованных карт с годом, издателем и правовым статусом) и `rejected` — участки, которые рассматривались как зона и были отвергнуты, с объяснением почему. Второе не менее важно, чем первое: без него следующий человек начнёт проверять те же места заново. ### `lost.json` Обратная задача: здание в 1897 году стояло, но снесено, и в OSM его нет. Контура нет, поэтому объём не достраивается — только точка. Поле `accuracy` говорит, насколько точно известно место: `exact` или `approximate`. ### `street-names-1897.json` Словарь «современное название из OSM → название на 1897 год». Улицы, названия которых не менялись, в словарь не попадают и показываются как есть. Поле `c`: - `high` — переименование общеизвестно и однозначно (Грибоедова → Екатерининский канал, Марата → Николаевская); - `medium` — требует сверки по адресной книге «Весь Петербург» за 1897 год. Часть записей несёт поле `note` — чем подтверждено или почему не подтверждено. **Сверка по планам Ильина.** Записи `medium` проверялись так: ось улицы из OpenStreetMap накладывалась на скан плана 1894 года, и подпись читалась прямо под ней. Подтвердились две — Менделеевская линия («УНИВЕРСИТЕТСКАЯ») и площадь Тургенева («Покровская площадь», рядом «Ц. Покрова»). Остальные так проверить не удалось: подписи на плане набраны вразрядку вдоль квартала, и на узких параллельных улицах Петроградской стороны подпись с равным правом относится к соседней улице. Там же вскрылось расхождение, которое важнее самой сверки: **у рот Измайловского полка подписи плана ложатся со сдвигом на одну улицу**. Над осью 9-й Красноармейской стоит «ДЕСЯТАЯ РОТА», над 10-й — «ОДИНАДЦАТАЯ РОТА», над 11-й — «ДВѢНАДЦАТАЯ», над 12-й — «ПРІЮТСКАЯ». Либо подписи относятся к кварталу южнее улицы, либо весь ряд рот в наборе сдвинут на единицу — а это затрагивает и пять записей с меткой `high`. По карте того же масштаба это не разрешается; нужен адресный справочник. До тех пор ничего не переписано. ## Дореформенная орфография Переключатель «Орфография 1897 года» показывает названия в написании того времени. Преобразование механическое и сознательно неполное: - `ъ` на конце слова после согласной: `проспект` → `проспектъ`; - `і` перед гласной: `линия` → `линія`; - окончания `-ий` → `-ій`, `-ого` → `-аго`. **Ять не угадывается.** Она ставится только по короткому выверенному списку корней в `YAT_WORDS` (`Сенная` → `Сѣнная`, `Песочная` → `Пѣсочная` и ещё несколько). Правильная расстановка ятя требует словаря, а ошибка в ней заметна любому, кто разбирается в предмете. Поэтому там, где корня в списке нет, слово остаётся в современном написании. ## Отчёт сборки `data/build/report.json` пишется при каждой сборке и содержит счётчики: сколько контуров отброшено по каждой причине, сколько зданий какого уровня достоверности, какие ключевые здания и анахронизмы не удалось привязать к контуру. Этот файл — первое, куда стоит смотреть после изменения данных. Счётчики в `buildings`: | поле | что считает | |------|-------------| | `total` | контуров в сырой выгрузке | | `outOfScope` | центроид вне границы охвата | | `anachronism` | совпало с записью в `anachronisms.json` | | `tooNew` | `start_date` позже 1897 года | | `tooNewAltTag` | позже 1897 по запасным тегам даты | | `tooNewWikidata` | позже 1897 по дате из Викиданных | | `tooNewHeritage` | позже 1897 по датировке реестра памятников | | `lateZone` | стоит в зоне поздней застройки | | `sovietStyle` | стиль XX века в `building:architecture` | | `tooTall` | выше предела высоты, снятого в 1905 году | | `dropType` | современный или временный тип постройки | | `tooSmall` | пятно меньше 30 м² | | `kept` | осталось в модели | | `dated` / `landmark` | из них с уровнем достоверности 1 и 2 | | `datedByWikidata` | сколько из `dated` датировано по Викиданным | | `datedByHeritage` | сколько из `dated` датировано по реестру памятников | | `namedByHeritage` | скольким контурам реестр дал наименование (на `dated` и на отсев не влияет) | Порядок проверок важен: здание считается один раз, по первой сработавшей причине. Поэтому, например, `dropType` уменьшился после введения зон — часть гаражей теперь отсеивается раньше, как стоящая на бывшем поле. В `unmatchedLandmarks` и `unmatchedAnachronisms` попадают записи, не получившие объёма, — и у каждой стоит поле `why`. Не всякое «не привязано» дефект: мосты в OpenStreetMap это линии дорог, а не контуры зданий, объёма у них быть не может в принципе, и они показываются точечной отметкой. Смотреть в этих списках надо на причины «вне границы охвата» и «в наборе не указан контур OSM». Кроме счётчиков в отчёт пишутся `lateZones` (сколько зданий сняла каждая зона), `lateZoneConflicts` (здания, у которых дата в OSM спорит с картой) и `dateConflicts` (здания, у которых дата в OSM спорит с Викиданными). ## Даты построек из Викиданных `tools/fetch-dates.mjs` делает два запроса к SPARQL Викиданных и складывает результат в `data/building-dates.json` — словарь «идентификатор элемента → год постройки». Со зданием он соединяется по тегу `wikidata`, который в охвате стоит у 469 контуров. Почему это ценно: `start_date` в OpenStreetMap проставлен у полутора сотен зданий из двенадцати тысяч, и отсеивать по нему почти нечего. Викиданные добавляют ещё несколько десятков **поимённо датированных домов** — с прямой ссылкой, которую видно в карточке. Это единственный слой, который работает по отдельному зданию, а не по участку земли. Что отбрасывается и почему: - **Организации.** Первый запрос отбирает элементы-сооружения (P31 ведёт по цепочке подклассов к «зданию», «архитектурному сооружению» или «объекту недвижимости»). Но этого мало: Большой драматический театр и Филармония тоже размечены как здания, а P571 у них — год основания труппы (1919 и 1921) при зданиях 1878 и 1839 годов. Поэтому вторым запросом отбираются те же элементы, которые заодно являются организациями (подклассами Q43229), и их даты выбрасываются. Без этой проверки фильтр сносил подлинные здания. - **P580 и P1619** («дата начала», «дата открытия») не используются: они ещё чаще относятся к учреждению, а не к дому. Почему два запроса, а не один с `FILTER NOT EXISTS`: отрицание с обходом иерархии подклассов по целому прямоугольнику уводит сервер в таймаут (504). Второй запрос идёт порциями по 300 идентификаторов, уже найденных первым. Файл создаётся скриптом, в git попадает готовым, и при сборке он необязателен: если его нет, сборка выведет предупреждение и обойдётся без этого слоя. ### Почему связка только по тегу `wikidata`, а не по координате или адресу Тег `wikidata` стоит у 469 контуров из 11 700, то есть слой датирования покрывает лишь несколько процентов модели. Соблазнительно расширить его, сопоставляя элементы Викиданных с контурами по координате (у каждого элемента есть P625) или по адресу (адрес есть у 89,7 % контуров). Это проверено и **отвергнуто по результатам измерения**. Проверка сделана так: эталоном взяты контуры, у которых тег `wikidata` уже стоит, — для них известно, какой элемент правильный. Затем к ним применена связка по координате и посчитано, как часто она приводит **другой** элемент. | ограничение на элемент | контрольных случаев | ошибок | |---|---|---| | без ограничений | 155 | 24 — **15,5 %** | | не является частью другого объекта (нет P361) | 124 | 14 — 11,3 % | | есть архитектор (P84) | 33 | 5 — 15,2 % | | есть охранный статус (P1435) | 150 | 21 — 14,0 % | | архитектор и охранный статус, и не часть | 25 | 4 — 16,0 % | Ни одно ограничение не опускает долю ошибок даже близко к нулю. Причина не в способе сопоставления, а в том, **что именно лежит в Викиданных внутри пятна здания**. Кроме самого дома там оказываются: органы («organ of the Mariinsky Theater», 2009 год — внутри контура концертного зала), сады («Никольский сад», 1874 — внутри контура Никольского собора), ограды, часовни, флигели, отдельные корпуса ансамблей и театральные труппы («Комедианты», 1989 — внутри контура жилого дома на Лиговском). Датировать здание по его органу или по саду во дворе нельзя, а отличить такие элементы от самого дома формальным признаком не получается. Связка по адресу упирается в то же самое: она меняет способ соединения, но не качество сущностей, и унаследовала бы ту же долю ошибок. Плюс к этому у элементов Викиданных адрес (P6375) заполнен редко, а единственный реестр, где адрес и датировка есть у каждого объекта, — ЕГРОКН — недоступен (см. ниже). Поэтому используется только тег `wikidata`: его проставил человек, привязав конкретный элемент к конкретному контуру, и это единственная связка, которая проходит проверку. ## Реестр объектов культурного наследия (ЕГРОКН) Самый крупный источник поимённых датировок. Скрипт `tools/fetch-egrkn.mjs` берёт официальную выгрузку Минкультуры, отбирает из неё Петербург и пишет `data/heritage-dates.json`. Файл создаётся скриптом и руками не правится. **Откуда выгрузка.** Порталы открытых данных (`opendata.mkrf.ru`, `kgiop.gov.spb.ru`, `data.gov.spb.ru`) не отвечают на сетевом уровне: соединение отваливается по таймауту, не доходя до HTTP, при том что Викиданные и прочие внешние источники с той же машины работают. Поэтому используется снимок официальной выгрузки в Архиве интернета (13.07.2023, около 100 МБ). В адресе Wayback обязателен суффикс `id_`: без него вместо файла приходит HTML-обёртка архива. Возраст выгрузки роли не играет — год постройки дома в реестре не меняется. **Что берётся из реестра.** В выгрузке 152 369 строк, по Петербургу 6 023. Из них в дело идут только объекты с общей видовой принадлежностью «памятник градостроительства и архитектуры» (4 231 запись). **Памятники истории отброшены целиком**, и это принципиально: у них в поле «дата создания» стоит дата СОБЫТИЯ, а не постройки. У дома, где жил Блок, там написано «1912-1921 гг.» — это годы жизни поэта в доме, а сам дом 1876 года. У дома, где жил актёр Н. К. Симонов, — «1946-1973 гг.». Без этого отсева фильтр сносил бы подлинные здания пачками; в первом прогоне таких записей набралось полтора десятка. Отсеиваются также памятники искусства и археологии. ### Разбор датировки «Дата создания» в реестре — свободный текст: `1896-1898 гг.`, `кон. XIX в.`, `1820-е гг., перестроен в 1901`, `1855 г., ХХ в.`. Разбор консервативный в обе стороны: - берутся все однозначно читаемые четырёхзначные годы; если их нет (`кон. XIX в.`), запись не используется вовсе; - десятилетие раскрывается до последнего года: `1890-е` — это до 1899; - **на снос** годится только `min > 1897`, то есть вся датировка позже 1897 года. Поэтому `1820-е гг., перестроен в 1901` здание не сносит: оно стояло в 1897-м, пусть и в другом виде; - **на датирование** годится только `max ≤ 1897` и полное отсутствие веков; - любое упоминание века ниже XX снимает запись со сноса, любое упоминание века вообще — с датирования. `1896-1898 гг.` не годится ни туда, ни сюда: датировка перекрывает 1897 год. Отдельная ловушка: римские цифры в реестре сплошь и рядом набраны кириллицей — `ХХ в.` с кириллическим Х выглядит как латинское `XX`. Перед разбором строка приводится к латинице, иначе проверка на век не срабатывает. И наоборот: окончания десятилетий (`1830-х гг.`) надо вырезать ДО поиска веков, иначе `х` читается как римская десятка. ### Связка с контурами Двухступенчатая. 1. **По номеру в реестре.** Тег `ref:egrokn` стоит у 60 контуров в охвате, и проставил его человек. Проверка: все 60 номеров нашлись в выгрузке, то есть связка работает как есть. 2. **По адресу.** Ключ — «улица|дом|литера|корпус» (см. `tools/address.mjs`). Дом «16» и дом «16 литА» дают разные ключи и совпадением не считаются. Адрес идёт в дело, только если он однозначен **с обеих сторон**: - в реестре на этом адресе ровно одна запись. Иначе выбрать не из чего: на адресе часто висят и дом, и его ограда, и сад (345 адресов отброшено); - в OSM на этом адресе ровно один контур. Иначе непонятно, какой из них датировать: лицевой дом и дворовые флигели носят один адрес. **Контрольный замер адресной связки.** Эталоном взяты те же 60 контуров с проставленным `ref:egrokn` — для них известна правильная запись реестра. К ним применена связка по адресу: | | | |---|---| | проверено контуров | 60 | | адрес не нашёлся в реестре | 34 | | на адресе несколько записей (отброшено правилом) | 6 | | однозначное совпадение | 20 | | из них привели **не ту** запись | **0 — 0,0 %** | Второй, независимый замер: среди всех контуров, которым адресная связка дала датировку, у 41 есть ещё и собственная дата в OSM или в Викиданных. Расхождений по признаку «до/после 1897» — **ноль**. Итого 59 контрольных случаев и ни одной ошибки — в отличие от связки по координате, где ошибок 15,5 % (см. выше). Разница в том, что адрес — это удостоверение конкретного дома, а не пространственное попадание: орган внутри концертного зала своего почтового адреса не имеет, а ограда с тем же адресом отсекается правилом «ровно одна запись». **Чего связка не ловит.** Реестр может хранить прежнее название улицы, а OSM — современное; такие адреса просто не совпадут и дадут не ошибку, а пропуск. ### Наименование отдельно от датировки Памятников архитектуры по Петербургу в выгрузке 4 231, а читаемая датировка есть лишь у 2 489. Оставшиеся полторы тысячи записей для фильтра бесполезны — но **официальное наименование дома в них есть**, и для карточки это ровно то, что нужно: вместо «Доходный дом. Рядовая застройка квартала» посетитель читает «Дом Фокина» с номером в реестре. Поэтому записи с неточной датировкой из набора больше не выбрасываются: у них просто `v: null`. Отсюда правило, которое легко нарушить: > **Имя и дата — разные вещи.** Наличие имени из реестра НЕ повышает уровень > достоверности и НЕ даёт отсева. Уровень `t=1` ставится только по читаемой > датировке. Ради этого в наборе **два раздельных адресных индекса**: | индекс | что внутри | кого питает | |---|---|---| | `byAddress` | только записи с читаемой датировкой | фильтр и уровень `t` | | `namesByAddress` | все записи, включая неточно датированные | только карточка | Индекс один сделать нельзя: добавление полутора тысяч записей сделало бы часть адресов неоднозначными (на адресе стало бы две записи вместо одной), и уже работающие датировки молча выпали бы из фильтра. Проверка после этой правки — числа отсева и уровней достоверности не должны измениться ни на единицу. Свойства `rn`, `rd`, `r`, `rb` ставятся у контура всегда, когда запись реестра нашлась, — независимо от того, дала ли она датировку. В карточке при этом прямо сказано, что датировку прочитать не удалось.