CesiumJS работает поверх WebGL и формирует изображение сцены в
реальном времени, поэтому печать карты требует промежуточного этапа —
преобразования текущего состояния Scene в статическое
изображение. Основная задача сводится к корректному захвату canvas,
синхронизации слоёв тайлов и подготовки результата к выводу на печать
без потери качества.
В CesiumJS визуализация рендерится в элемент
<canvas>, доступный через
viewer.scene.canvas. Именно этот элемент используется как
источник итогового изображения.
Базовый способ получения снимка заключается в использовании метода:
const canvas = viewer.scene.canvas;
const image = canvas.toDataURL("image/png");
Однако прямой вызов toDataURL без предварительного
рендера может привести к захвату неполной сцены, поскольку Cesium
выполняет отложенную отрисовку кадров.
Корректный подход включает принудительный рендер:
viewer.render();
const canvas = viewer.scene.canvas;
const image = canvas.toDataURL("image/png");
Для сцен с динамическими слоями дополнительно учитывается готовность тайлов:
viewer.scene.globe.tileLoadProgressEvent.addEventListener(function (remaining) {
if (remaining === 0) {
viewer.render();
const image = viewer.scene.canvas.toDataURL("image/png");
}
});
Это гарантирует, что все подложки ImageryLayer полностью загружены.
Стандартный canvas ограничен размером экрана, что приводит к низкому качеству при печати. Для получения печатного качества используется увеличение виртуального разрешения через масштабирование canvas.
Подход основан на создании временного canvas:
const originalCanvas = viewer.scene.canvas;
const scale = 2;
const exportCanvas = document.createElement("canvas");
exportCanvas.width = originalCanvas.width * scale;
exportCanvas.height = originalCanvas.height * scale;
const ctx = exportCanvas.getContext("2d");
ctx.scale(scale, scale);
ctx.drawImage(originalCanvas, 0, 0);
const image = exportCanvas.toDataURL("image/png");
Для более качественного результата применяют коэффициент 3–4, однако нагрузка на память растёт экспоненциально.
После получения изображения формируется отдельное окно с минимальной HTML-разметкой:
const printWindow = window.open("", "_blank");
printWindow.document.write(`
<html>
<head>
<style>
body { margin: 0; }
img { width: 100%; }
</style>
</head>
<body>
<img src="${image}">
</body>
</html>
`);
printWindow.document.close();
printWindow.focus();
printWindow.print();
Такой подход обеспечивает контроль над масштабированием и предотвращает вмешательство стилей основной страницы.
CesiumJS продолжает обновлять кадры даже при отсутствии пользовательского взаимодействия. Для получения стабильного снимка необходимо зафиксировать камеру и отключить анимации:
viewer.clock.shouldAnimate = false;
viewer.scene.requestRenderMode = true;
viewer.scene.maximumRenderTimeChange = 0;
Дополнительно полезно отключать постобработку:
viewer.scene.postProcessStages.enabled = false;
Это снижает вероятность артефактов при печати.
Карты Cesium формируются из тайлов, загружаемых асинхронно. При печати важно дождаться завершения всех запросов.
Используется отслеживание состояния загрузки:
function waitForTiles(viewer) {
return new Promise((resolve) => {
const check = () => {
if (viewer.scene.globe.tilesLoaded) {
resolve();
} else {
requestAnimationFrame(check);
}
};
check();
});
}
После завершения загрузки можно безопасно формировать снимок сцены.
3D-объекты в CesiumJS рендерятся с учётом перспективы, что создаёт искажения при печати. Для плоского представления карты используется переход в ортографическую проекцию:
viewer.scene.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(0, 0, 20000000)
});
Либо временное переключение на режим, минимизирующий перспективные эффекты.
Также учитывается освещение:
viewer.scene.light = new Cesium.DirectionalLight({
direction: viewer.scene.camera.directionWC
});
Это обеспечивает стабильные тени при фиксированном положении камеры.
Перед созданием снимка часто требуется скрыть элементы интерфейса:
viewer.animation.container.style.display = "none";
viewer.timeline.container.style.display = "none";
viewer.geocoder.container.style.display = "none";
Также отключаются декоративные элементы сцены:
viewer.scene.skyAtmosphere.show = false;
viewer.scene.skyBox.show = false;
viewer.scene.fog.enabled = false;
Это обеспечивает чистый картографический вид без визуального шума.
Для повышения качества применяется рендеринг нескольких кадров подряд с последующим захватом:
for (let i = 0; i < 3; i++) {
viewer.render();
}
const canvas = viewer.scene.canvas;
const image = canvas.toDataURL("image/png");
Такая техника позволяет завершить догрузку шейдеров и текстур.
Для соответствия стандартам печати (A4, A3) вычисляется DPI:
const dpi = 300;
const inchesWidth = 8.27;
const pixels = dpi * inchesWidth;
После этого canvas масштабируется до целевого разрешения, сохраняя пропорции сцены.
WebGL-рендер может блокировать чтение пикселей при использовании cross-origin тайлов без CORS-заголовков. В CesiumJS это проявляется в виде «tainted canvas».
Решение заключается в настройке источников изображений:
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: new Cesium.OpenStreetMapImageryProvider({
url: "https://a.tile.openstreetmap.org/"
}),
requestRenderMode: true
});
При необходимости сервер тайлов должен возвращать заголовок:
Access-Control-Allow-Origin: *
После получения изображения возможно применение фильтров через дополнительный canvas:
const ctx = exportCanvas.getContext("2d");
const imageData = ctx.getImageData(0, 0, exportCanvas.width, exportCanvas.height);
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i] *= 1.05;
imageData.data[i + 1] *= 1.05;
imageData.data[i + 2] *= 1.05;
}
ctx.putImageData(imageData, 0, 0);
Такая коррекция повышает читаемость картографических слоёв при печати.
Помимо изображения, сцена может быть встроена в PDF через библиотеку
jsPDF:
const { jsPDF } = window.jspdf;
const pdf = new jsPDF("landscape", "px", [exportCanvas.width, exportCanvas.height]);
pdf.addImage(image, "PNG", 0, 0, exportCanvas.width, exportCanvas.height);
pdf.save("map.pdf");
Этот подход используется для стандартизированных отчётов с картографическим содержимым.
Для уменьшения артефактов при экспорте уровень детализации можно зафиксировать:
viewer.scene.globe.maximumScreenSpaceError = 1;
Уменьшение этого параметра увеличивает качество геометрии, что особенно важно при крупномасштабной печати.
При наличии движущихся объектов необходимо остановить симуляцию времени:
viewer.clock.multiplier = 0;
viewer.clock.currentTime = viewer.clock.currentTime;
viewer.clock.shouldAnimate = false;
Это исключает смазывание динамических сущностей на итоговом изображении.
CesiumJS использует линейное освещение и гамма-коррекцию, что может отличаться от печатного представления. Для корректировки применяются постпроцессинговые этапы:
viewer.scene.gamma = 1.2;
viewer.scene.highDynamicRange = false;
Балансировка этих параметров влияет на читаемость контуров и текстур в печати.