Sharing и embedding

CesiumJS предоставляет несколько уровней взаимодействия для распространения 3D-контента: от простого сохранения состояния камеры до полноценного встраивания готовых сцен в сторонние веб-приложения. Основные подходы к sharing и embedding базируются на сериализации состояния Viewer, использовании URL-параметров, iframe-встраивании и публикации ассетов через Cesium ion.


Сериализация состояния сцены через URL

Одним из базовых механизмов шаринга является сохранение состояния камеры и параметров сцены в адресной строке. В CesiumJS нет единого обязательного стандарта для этого, однако распространённой практикой является ручная сериализация ключевых параметров:

  • положение камеры (Camera.position)
  • ориентация (heading, pitch, roll)
  • целевая точка наблюдения
  • выбранные слои и тайлы
  • активные сущности (Entity)

Пример формирования URL с параметрами:

const camera = viewer.camera;

const cartographic = Cesium.Cartographic.fromCartesian(camera.position);

const state = {
  lon: Cesium.Math.toDegrees(cartographic.longitude),
  lat: Cesium.Math.toDegrees(cartographic.latitude),
  height: cartographic.height,
  heading: camera.heading,
  pitch: camera.pitch,
  roll: camera.roll
};

const url = new URL(window.location.href);

url.searchParams.set("lon", state.lon);
url.searchParams.set("lat", state.lat);
url.searchParams.set("h", state.height);
url.searchParams.set("heading", state.heading);
url.searchParams.set("pitch", state.pitch);
url.searchParams.set("roll", state.roll);

history.replaceState({}, "", url);

Восстановление состояния при загрузке:

const params = new URLSearchParams(window.location.search);

const lon = parseFloat(params.get("lon"));
const lat = parseFloat(params.get("lat"));
const height = parseFloat(params.get("h"));

viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
  orientation: {
    heading: parseFloat(params.get("heading")),
    pitch: parseFloat(params.get("pitch")),
    roll: parseFloat(params.get("roll"))
  }
});

Такой подход делает сцену воспроизводимой и позволяет передавать ссылку на конкретный ракурс.


Использование Sandcastle share-сценариев

Cesium Sandcastle — инструмент для демонстрации примеров кода CesiumJS — поддерживает встроенный механизм генерации share-ссылок. Каждая сцена может быть сериализована в URL, содержащий код JavaScript.

Механизм основан на:

  • кодировании скрипта в URL (обычно base64 или аналогичная схема)
  • восстановлении редактора Sandcastle из ссылки
  • автоматическом запуске сцены

Пример логики:

Sandcastle.addDefaultToolbarButton("Share", function () {
  const code = editor.getValue();
  const encoded = btoa(unescape(encodeURIComponent(code)));

  const url = `${location.origin}/Sandcastle/index.html#c=${encoded}`;

  navigator.clipboard.writeText(url);
});

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


Сохранение визуального состояния сцены

Для более продвинутого sharing используется сохранение состояния визуальных слоёв:

  • включённые imagery layers
  • terrain provider
  • 3D Tilesets
  • entities и primitives
  • styling (например, ColorBlendMode)

Пример сохранения активных слоёв:

const layers = viewer.imageryLayers._layers.map(layer => ({
  url: layer.imageryProvider?.url,
  alpha: layer.alpha,
  show: layer.show
}));

Восстановление:

layers.forEach(cfg => {
  viewer.imageryLayers.addImageryProvider(
    new Cesium.UrlTemplateImageryProvider({ url: cfg.url })
  );
});

Для 3D Tiles:

const tileset = viewer.scene.primitives.add(
  new Cesium.Cesium3DTileset({
    url: tilesetUrl
  })
);

Сериализация таких данных обычно выводится в JSON, который затем помещается в URL или хранится на сервере.


Embedding через iframe

Наиболее распространённый способ встраивания CesiumJS-приложений — использование <iframe>. Такой подход применяется при публикации готовых сцен или приложений, размещённых на отдельном домене.

Базовый пример:

<iframe
  src="https://example.com/cesium-app"
  width="100%"
  height="600"
  style="border: none;"
></iframe>

Особенности embedding:

  • изоляция контекста выполнения
  • отсутствие прямого доступа к Viewer извне
  • необходимость postMessage для взаимодействия
  • отдельная загрузка ресурсов (terrain, imagery, tiles)

Встраивание с управлением через postMessage

Для динамического взаимодействия между родительской страницей и Cesium-приложением используется window.postMessage.

Внутри Cesium-приложения:

window.addEventListener("message", (event) => {
  if (event.data.type === "FLY_TO") {
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(
        event.data.lon,
        event.data.lat,
        event.data.height
      )
    });
  }
});

На стороне embedding-страницы:

const iframe = document.querySelector("iframe");

iframe.contentWindow.postMessage({
  type: "FLY_TO",
  lon: 71.4304,
  lat: 51.1283,
  height: 10000
}, "*");

Такой подход позволяет управлять сценой без прямого доступа к внутреннему API.


Embedding Cesium ion сцен

Cesium ion предоставляет готовые механизмы публикации сцен и ассетов. После загрузки данных (3D Tiles, terrain, imagery) формируется уникальный endpoint, который может быть встроен в приложение.

Типичный сценарий:

  • загрузка модели в Cesium ion
  • получение asset ID
  • использование публичного URL или token-аутентификации

Пример подключения tileset:

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrainProvider: Cesium.createWorldTerrain()
});

const tileset = new Cesium.Cesium3DTileset({
  url: Cesium.IonResource.fromAssetId(123456)
});

viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);

Для embedding в сторонний сайт используется тот же код, но с ограничением доступа через access token.


Контроль доступа и безопасность embedding

При встраивании Cesium-сцен критически важна настройка токенов и CORS:

  • Cesium ion access token ограничивает доступ к ассетам
  • серверные тайлы требуют корректных CORS-заголовков
  • публичные сцены должны исключать приватные данные

Пример установки токена:

Cesium.Ion.defaultAccessToken = "YOUR_ACCESS_TOKEN";

Для production-сцен используется:

  • ограничение доменов
  • регенерация токенов
  • разделение публичных и приватных ассетов

Responsive embedding и адаптация контейнера

CesiumJS зависит от размеров canvas, поэтому embedding требует корректной адаптации контейнера:

#cesiumContainer {
  width: 100%;
  height: 100vh;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

При изменении размеров окна важно уведомлять Cesium:

window.addEventListener("resize", () => {
  viewer.resize();
});

В современных приложениях часто используется ResizeObserver:

const observer = new ResizeObserver(() => {
  viewer.resize();
});

observer.observe(document.getElementById("cesiumContainer"));

Embedding через CDN и self-hosted сборки

CesiumJS может быть встроен как через CDN, так и через локальную сборку.

CDN-вариант:

<script src="https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Widgets/widgets.css" rel="stylesheet">

Self-hosted вариант:

  • сборка через npm
  • использование bundler (Vite/Webpack)
  • локальная поставка assets
import * as Cesium from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";

Embedding в этом случае становится частью обычного frontend-приложения.


Передача состояния между страницами embedding

Для сложных систем используется гибридный подход:

  • URL хранит минимальное состояние (камера, режим)
  • сервер хранит полный JSON сцены
  • iframe загружает сцену по ID

Пример URL:

https://app.com/viewer?sceneId=42&mode=3d

Загрузка сцены:

fetch(`/api/scenes/${sceneId}`)
  .then(res => res.json())
  .then(scene => {
    loadScene(viewer, scene);
  });

Такой подход масштабируется лучше, чем длинные URL с сериализацией всего состояния.


Embedding 3D Tiles сцен и потоковая загрузка

3D Tiles являются основным форматом CesiumJS для потокового отображения больших данных. При embedding важно учитывать:

  • прогрессивную загрузку тайлов
  • LOD (Level of Detail)
  • сетевые ограничения

Пример:

const tileset = new Cesium.Cesium3DTileset({
  url: Cesium.IonResource.fromAssetId(98765),
  maximumScreenSpaceError: 2
});

viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);

При встраивании таких сцен часто используется lazy-loading, чтобы минимизировать стартовую нагрузку iframe.


Интеграция embedding с UI-слоями приложения

Встраивание CesiumJS в интерфейсы часто сопровождается дополнительными слоями управления:

  • React/Vue панели
  • контролы фильтрации данных
  • переключатели слоёв
  • временные шкалы

Связь с viewer:

function toggleLayer(layer) {
  layer.show = !layer.show;
}

Такая архитектура разделяет embedding сцены и бизнес-логику интерфейса, позволяя масштабировать приложения с геопространственными данными.