Встраиваемая карта
Карта сети для встраивания на сайт: только слои, разрешённые ключом встраивания, и только на указанных сайтах.
Возможности
- Показать карту тепловой сети на чужом сайте (портал акимата, ЕДДС, кабинет потребителя) без входа — по ключу встраивания.
- Посетитель включает и выключает слои ключа флажками в плашке, приближает карту и щёлкает объект — открывается короткая карточка без личных данных.
- Если ключ это разрешает и на сервере включена публичная лента, поверх сети видны зоны отключений.
- Адрес страницы задаёт начальный вид и часть слоёв; можно убрать плашку и карточку.
- Сайт может управлять картой из своего JavaScript через SDK
tgid-embed.js: менять вид и слои, подсвечивать объекты, получать щелчки по объектам.
Как это работает
Ключ встраивания выдаёт администратор ТГИД в «Администрирование» → «Встраиваемые карты». Ключ начинается
с tge_ и показывается один раз; в базе хранится только его хеш SHA-256. В ключе записаны:
- сайты, где карту можно показывать (1–20 адресов вида
https://сайт[:порт]; путь отбрасывается); - слои — из пяти: «Участки теплосети», «Узлы сети», «Задвижки», «Потребители», «Источники тепла»;
- фрагменты (до 200; не заданы — все);
- показывать ли отключения, начальный вид (центр и зум) и срок действия (1–3650 дней или бессрочно).
Ключ публичный по природе: он виден в коде страницы сайта. Поэтому он даёт только чтение выбранных слоёв.
Проверка сайта. Страница /embed узнаёт сайт-родитель по location.ancestorOrigins, а где его нет (Firefox) —
по заголовку Referer. Карта показывается, если:
- страница открыта сама по себе, не во фрейме, — например, по ссылке;
- она во фрейме на самом сайте ТГИД (предпросмотр);
- сайт-родитель есть в списке сайтов ключа.
Иначе вместо карты — сообщение о запрете. Проверку делает сама страница; сервер тайлов сайт не проверяет, ему достаточно действующего ключа. Сообщения от карты сайту уходят только по проверенному адресу родителя, команды принимаются только от него.
Слои и зумы. Тайлы — векторные (MVT), строит их сервер из базы города. Слой виден с зума:
| Слой | С зума | Поля в карточке |
|---|---|---|
| Источники тепла | 9 | название |
| Участки теплосети | 10 | название, трубопровод (подача, обратка), Ду, длина, состояние подачи и обратки |
| Потребители | 14 | вид, нагрузка, Гкал/ч |
| Узлы сети | 15 | название (вид узла есть в тайле, но в карточку не выводится) |
| Задвижки | 15 | название, Ду, состояние (открыта, закрыта) |
Участки на обзорных зумах прорежены: до зума 12 — только Ду от 500 мм, на зуме 12 — от 300 мм, с 13 — все. Участки внутренних схем узлов (камер, ЦТП) не показываются. Пока слой не виден, внизу плашки подсказка «Приблизьте карту, чтобы увидеть: …». Имён и адресов потребителей в тайлах нет.
Начальный вид: center и zoom из адреса страницы (без zoom — 15), иначе вид из ключа, иначе вся сеть
города.
Параметры адреса: key — ключ (обязателен), layers=pipes,valves — часть слоёв ключа, center=долгота,широта,
zoom, ui=0 — без плашки и карточки. Идентификаторы слоёв: pipes, nodes, valves, consumers,
heat_sources.
Отключения. Если в ключе отмечены отключения и на сервере включена публичная лента, карта рисует те же зоны, что публичная карта отключений, и обновляет их раз в 5 минут.
Отзыв и срок. Сервер помнит проверку ключа 60 секунд, тайлы браузер держит до 5 минут. Поэтому после отзыва
карта на сайте перестаёт работать не мгновенно. Когда тайлы начинают отвечать отказом, сайт получает событие
error «Ключ встраивания больше не действует» (один раз). Время последнего использования ключа видно администратору.
SDK. Файл <сайт ТГИД>/tgid-embed.js создаёт iframe и обменивается с ним сообщениями (протокол v1):
TGIDEmbed.create(элемент, { url, key, layers, center, zoom, ui, height, title });- команды:
setView({ center, zoom }),fitBounds([запад, юг, восток, север], отступ),setLayers([...]),highlight(слой, [id…])(до 500 объектов),getView()— обещание с видом и границами (ждёт ответа 5 секунд),destroy(); - события:
ready,select(слой, id, поля, точка щелчка),moveend,error.
Команды, отправленные до ready, SDK копит и отдаёт после загрузки карты; дождаться загрузки можно и через
обещание map.ready.
Что нужно для работы
- Ключ встраивания: создаёт пользователь с ролью «Администратор». В ключ нужно внести сайт, где будет карта.
- Для отключений: отметка «отключения» в ключе и
PUBLIC_OUTAGES_ENABLED=trueна сервере. - Сайт-родитель не должен запрещать передачу адреса: у iframe нельзя ставить
referrerpolicy="no-referrer"(SDK ставитstrict-origin-when-cross-origin). - Посетителю — только браузер, вход не нужен.
Шаги
Откройте адрес с ключом — видна карта сети, только слои ключа. Плашка в углу — название города и список слоёв: флажками слои включаются и выключаются, подсказка внизу говорит, какие слои появятся при приближении.

Нажатие на объект открывает короткую карточку: только технические поля, без личных данных.
Ключ создаётся в «Администрирование → Встраиваемые карты»: название, сайты, на которых карту можно показывать, слои и срок действия. Без ключа страница показывает только подсказку.
На чужом сайте карта вставляется через iframe с адресом …/itwin-map/embed?key=…; сайт-родитель должен быть в списке сайтов ключа, иначе вместо карты — сообщение о запрете.
Примеры
Прореживание на копии Алматы. В слое «Участки теплосети» по всем фрагментам 48 982 участка основной схемы. До зума 12 карта показывает 6709 из них (Ду от 500 мм), на зуме 12 — 10 217 (Ду от 300 мм), с зума 13 — все. Узлы и задвижки появляются с зума 15.
Ключ не тот. Стенд инструкции на запрос настроек с выдуманным ключом отвечает «Ключ встраивания не принят: отозван, истёк или из другого города», без ключа — «Нужен ключ встраивания (параметр key)». Страница в обоих случаях показывает «Ключ встраивания не принят: он отозван, истёк или выдан для другого города.» или «В адресе карты нет ключа встраивания (key).».
Код для сайта с SDK (адреса условные):
<div id="tgid-map" style="height:480px"></div>
<script src="https://tgid.example/itwin-map/tgid-embed.js"></script>
<script>
var map = TGIDEmbed.create(document.getElementById('tgid-map'), {
url: 'https://tgid.example/itwin-map/embed', key: 'tge_…', layers: ['pipes', 'heat_sources'],
});
map.on('select', function (e) { console.log(e.layer, e.id, e.properties); });
</script>
Если не получается
- «Эта карта не разрешена для сайта …» — сайта нет в ключе. Адрес сравнивается целиком:
https://site.kzиhttps://www.site.kz— разные сайты, порт тоже важен. - «Не удалось определить сайт, на котором открыта карта. Уберите у iframe referrerpolicy="no-referrer".» — браузер не сообщил адрес родителя.
- «Карта сейчас недоступна — попробуйте обновить страницу позже.» — сервер не ответил на запрос настроек.
- Сеть не видна на обзорном зуме — слой ещё не виден на этом зуме или мелкие участки прорежены; приблизьте карту.
- Нет зон отключений — в ключе не отмечены отключения, на сервере выключена публичная лента или сейчас нет объявленных и идущих отключений с отметкой для публикации.
- Карта без улиц — у веба не задан ключ MapTiler (
NUXT_PUBLIC_MAPTILER_KEY); сеть при этом видна.
Для администратора
- Ключи — таблица
tgid_embed_key(сайты, слои, фрагменты, отключения, вид, срок, отзыв, последнее использование). Создание и отзыв пишутся в журнал действий (EMBED_KEY_CREATE,EMBED_KEY_REVOKE). - Настройки и тайлы открыты без входа даже при
AUTH_REQUIRED_GET=true. Ключ проверяется под ролью пула API, тайлы читаются под ролью анонима. - Тайлы обзорных зумов (до 12) сервер держит в памяти 10 минут, до 256 штук; отдаёт с
Cache-Control: public, max-age=300. - Веб отдаёт для
/embedзаголовокContent-Security-Policy: frame-ancestors *— фрейм разрешён любому сайту, отбор сайтов делает страница. Остальные страницы приложения во фрейме на чужом сайте не открываются. - Отзыв ключа сразу сбрасывает кеш проверки в том процессе API, который его отозвал; другие процессы перестанут принимать ключ в течение 60 секунд.
Для разработчика
- API:
GET /api/v1/embed/config?key=,GET /api/v1/embed/tiles/{layer}/{z}/{x}/{y}?key=(без входа);GET|POST /api/v1/embed/keys,DELETE /api/v1/embed/keys/{id}(роль «Администратор»). Код —routers/embed.py,database/embed.py(LAYERS,PIPES_OVERVIEW_DU,KEY_TTL_S,CACHE_BELOW_ZOOM), источники слоёв —database/ogc_features.py(COLLECTIONS). - Веб:
pages/embed.vue(без общего макета), протокол и правила —utils/embedProtocol.ts, SDK —public/tgid-embed.js, заголовки фрейма —server/middleware/frameAncestors.ts,utils/frameAncestors.ts; окно ключей —components/EmbedKeysDialog.vue. - Тесты: vitest —
tests/utils/embedProtocol.spec.ts,tests/utils/embedSdk.spec.ts,tests/utils/frameAncestors.spec.ts; API —tests/test_embed.py. Отдельного e2e у страницы нет; кадр инструкции снимает сценарийscripts/manual/scenarios/09-field-public.mjs.
