Структура проекта определяет организацию исходного кода, ресурсов, конфигурационных файлов и внешних зависимостей. В приложениях на CesiumJS этот вопрос особенно важен, поскольку проект обычно содержит большое количество геопространственных данных, 3D-моделей, изображений, шейдеров, пользовательских компонентов интерфейса и модулей визуализации.
Грамотно организованная структура позволяет:
Типичный проект на CesiumJS может иметь следующую организацию каталогов:
project/
│
├── public/
│ ├── assets/
│ │ ├── images/
│ │ ├── models/
│ │ ├── terrain/
│ │ └── textures/
│ │
│ └── cesium/
│
├── src/
│ ├── core/
│ ├── viewer/
│ ├── layers/
│ ├── entities/
│ ├── primitives/
│ ├── widgets/
│ ├── services/
│ ├── utils/
│ ├── styles/
│ └── main.js
│
├── package.json
├── vite.config.js
└── README.md
Каждый каталог решает отдельную задачу и не должен смешивать различные уровни логики приложения.
Папка public содержит статические ресурсы, которые
веб-сервер отдает без обработки сборщиком.
Пример:
public/
├── assets/
├── favicon.ico
└── robots.txt
В контексте CesiumJS здесь обычно размещаются:
Например:
public/assets/models/
├── aircraft.glb
├── satellite.glb
└── vehicle.glb
Доступ к таким ресурсам осуществляется по URL:
const modelUrl = "/assets/models/aircraft.glb";
Cesium использует собственный набор служебных файлов:
Build/
├── Assets/
├── ThirdParty/
├── Widgets/
└── Workers/
При использовании современных сборщиков данные каталоги копируются в отдельную директорию:
public/cesium/
├── Assets/
├── Widgets/
├── Workers/
└── ThirdParty/
После этого необходимо определить базовый путь:
window.CESIUM_BASE_URL = "/cesium";
Без корректного размещения этих ресурсов не будут работать:
В папке src располагается основной исходный код
приложения.
Пример:
src/
├── core/
├── viewer/
├── layers/
├── entities/
├── services/
└── main.js
Все JavaScript-модули должны находиться именно здесь.
Обычно используется файл:
src/main.js
Именно отсюда начинается инициализация приложения.
Пример:
import { createViewer } from "./viewer/createViewer.js";
const viewer = createViewer();
Точка входа должна содержать минимальное количество логики.
Нежелательно создавать объекты сцены непосредственно в этом файле.
Плохой пример:
const viewer = new Cesium.Viewer("cesiumContainer");
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
30,
50
)
});
Подобный код быстро становится трудно поддерживать.
Создание экземпляра Viewer обычно выносится в отдельный
каталог.
Структура:
viewer/
├── createViewer.js
├── configureCamera.js
├── configureScene.js
└── configureWidgets.js
Создание Viewer:
import * as Cesium from "cesium";
export function createViewer() {
return new Cesium.Viewer(
"cesiumContainer",
{
animation: false,
timeline: false
}
);
}
Такой подход позволяет централизованно управлять настройками визуализатора.
Папка core содержит фундаментальные механизмы
приложения.
Пример:
core/
├── App.js
├── EventBus.js
├── Config.js
└── Constants.js
Здесь размещаются:
Пример конфигурации:
export const Config = {
terrain: true,
shadows: true,
fps: false
};
Большинство проектов работают со множеством слоев данных.
Для них выделяется отдельный каталог.
layers/
├── imagery/
├── terrain/
├── vector/
└── tiles/
imagery/
├── BingLayer.js
├── OSMProvider.js
└── ArcGISLayer.js
Пример:
export function addOSMLayer(viewer) {
viewer.imageryLayers.addImageryProvider(
new Cesium.OpenStreetMapImageryProvider()
);
}
terrain/
├── WorldTerrain.js
└── LocalTerrain.js
Пример:
export async function loadTerrain() {
return await Cesium.createWorldTerrainAsync();
}
tiles/
├── BuildingTiles.js
├── CityTiles.js
└── IndustryTiles.js
Пример:
export async function loadBuildings(viewer) {
const tileset =
await Cesium.Cesium3DTileset.fromUrl(
"/assets/tiles/city/tileset.json"
);
viewer.scene.primitives.add(
tileset
);
}
В проектах на Cesium часто используется API Entity.
Для него удобно создать отдельный раздел.
entities/
├── Aircraft.js
├── Vehicle.js
├── Satellite.js
└── Sensor.js
Каждый файл отвечает за один тип объектов.
Пример:
export function createAircraft(viewer) {
return viewer.entities.add({
position:
Cesium.Cartesian3.fromDegrees(
37.6176,
55.7558,
10000
),
model: {
uri: "/assets/models/aircraft.glb"
}
});
}
Такое разделение предотвращает образование огромных файлов с сотнями объектов.
При работе с большими объемами данных часто используются Primitive.
Структура:
primitives/
├── PointCloud.js
├── PolylineCollection.js
├── GroundPrimitive.js
└── CustomPrimitive.js
Пример:
export function createPolyline(
viewer,
positions
) {
const primitive =
new Cesium.PolylineCollection();
primitive.add({
positions
});
viewer.scene.primitives.add(
primitive
);
}
Сервисы отвечают за работу с внешними источниками данных.
services/
├── ApiService.js
├── GeoJsonService.js
├── TileService.js
└── TrackingService.js
Пример:
export async function loadGeoJson(url) {
const response = await fetch(url);
return await response.json();
}
Сервис не должен содержать код отображения данных.
Его задача — только получение и обработка информации.
В сложных проектах появляются собственные элементы интерфейса.
widgets/
├── LayerPanel.js
├── SearchPanel.js
├── MeasurementTool.js
└── CoordinatesWidget.js
Пример:
export class CoordinatesWidget {
constructor(viewer) {
this.viewer = viewer;
}
initialize() {
// логика виджета
}
}
Подобная организация напоминает архитектуру современных SPA-приложений.
Утилиты содержат вспомогательные функции общего назначения.
utils/
├── coordinates.js
├── colors.js
├── geometry.js
└── time.js
Пример:
export function toDegrees(
longitude,
latitude
) {
return Cesium.Cartographic.fromDegrees(
longitude,
latitude
);
}
Утилиты должны быть независимыми от конкретной бизнес-логики.
Даже если большая часть визуализации создается через WebGL, интерфейс приложения требует CSS.
Структура:
styles/
├── main.css
├── viewer.css
├── panels.css
└── widgets.css
Подключение:
import "./styles/main.css";
Разделение стилей по назначению значительно упрощает сопровождение интерфейса.
Содержит:
Пример:
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
При использовании Vite обычно создается отдельная конфигурация.
Пример:
import { defineConfig } from "vite";
export default defineConfig({
server: {
port: 3000
}
});
Эффективная структура CesiumJS-проекта обычно строится по следующему принципу:
UI
│
├── Widgets
│
Application
│
├── Services
├── Layers
├── Entities
│
Infrastructure
│
├── Viewer
├── Core
└── Utils
Каждый уровень знает только о нижележащих слоях.
Например:
Widgets
↓
Services
↓
Viewer
Но не наоборот.
Это уменьшает связанность модулей и облегчает тестирование.
src/
│
├── core/
│ ├── App.js
│ ├── Config.js
│ └── EventBus.js
│
├── viewer/
│ ├── createViewer.js
│ ├── configureCamera.js
│ └── configureScene.js
│
├── layers/
│ ├── imagery/
│ ├── terrain/
│ └── tiles/
│
├── entities/
│ ├── Aircraft.js
│ ├── Vehicle.js
│ └── Satellite.js
│
├── primitives/
│ ├── PointCloud.js
│ └── CustomPrimitive.js
│
├── widgets/
│ ├── LayerPanel.js
│ ├── MeasurementTool.js
│ └── SearchPanel.js
│
├── services/
│ ├── ApiService.js
│ ├── GeoJsonService.js
│ └── TrackingService.js
│
├── utils/
│ ├── geometry.js
│ ├── coordinates.js
│ └── time.js
│
├── styles/
│ ├── main.css
│ └── widgets.css
│
└── main.js
Подобная организация подходит как для небольших геоинформационных приложений, так и для крупных систем мониторинга, цифровых двойников, навигационных платформ и решений, работающих с 3D Tiles, Terrain и потоковыми пространственными данными в реальном времени.