GPX (GPS Exchange Format) представляет собой XML-структуру, предназначенную для хранения треков, маршрутов и точек интереса, полученных с GPS-устройств. В контексте трёхмерной визуализации в CesiumJS GPX чаще всего используется для отображения траекторий движения объектов во времени и пространстве.
Структура GPX обычно включает:
lat, lon, а также
ele и timeCesiumJS не всегда предоставляет единый встроенный универсальный
загрузчик GPX во всех версиях, поэтому работа с форматом строится либо
через специализированные классы, либо через преобразование в
поддерживаемые структуры: CZML,
GeoJSON или набор сущностей (Entity).
Перед визуализацией данные GPX приводятся к структуре, удобной для движка. Основные подходы:
В некоторых сборках Cesium присутствует GpxDataSource,
который позволяет загружать GPX напрямую:
const viewer = new Cesium.Viewer("cesiumContainer");
const gpxDataSource = new Cesium.GpxDataSource();
viewer.dataSources.add(
Cesium.GpxDataSource.load("track.gpx", {
clampToGround: true
})
);
Такой способ автоматически:
Если встроенная поддержка отсутствует, GPX конвертируется в GeoJSON:
function gpxToGeoJSON(xmlText) {
const parser = new DOMParser();
const xml = parser.parseFromString(xmlText, "application/xml");
const points = xml.getElementsByTagName("trkpt");
const coordinates = [];
for (let i = 0; i < points.length; i++) {
const lat = parseFloat(points[i].getAttribute("lat"));
const lon = parseFloat(points[i].getAttribute("lon"));
const ele = points[i].getElementsByTagName("ele")[0]?.textContent;
coordinates.push([lon, lat, parseFloat(ele || 0)]);
}
return {
type: "Feature",
geometry: {
type: "LineString",
coordinates
},
properties: {}
};
}
Далее GeoJSON загружается стандартным способом:
Cesium.GeoJsonDataSource.load(geojson).then(ds => {
viewer.dataSources.add(ds);
viewer.zoomTo(ds);
});
Для треков с временными метками CZML является более точным вариантом, поскольку поддерживает интерполяцию движения.
function gpxToCzml(xmlText) {
const parser = new DOMParser();
const xml = parser.parseFromString(xmlText, "application/xml");
const points = xml.getElementsByTagName("trkpt");
const position = [];
for (let i = 0; i < points.length; i++) {
const lat = parseFloat(points[i].getAttribute("lat"));
const lon = parseFloat(points[i].getAttribute("lon"));
const ele = parseFloat(points[i].getElementsByTagName("ele")[0]?.textContent || 0);
const time = points[i].getElementsByTagName("time")[0]?.textContent;
position.push(time);
position.push(lon);
position.push(lat);
position.push(ele);
}
return [
{
id: "track",
availability: `${position[0]}/${position[position.length - 4]}`,
position: {
epoch: position[0],
cartographicDegrees: position
},
path: {
material: {
solidColor: {
color: {
rgba: [0, 180, 255, 255]
}
}
},
width: 3,
leadTime: 0,
trailTime: 3600
}
}
];
}
CesiumJS позволяет визуализировать GPX как линию через
Entity:
const positions = Cesium.Cartesian3.fromDegreesArray([
71.4304, 51.1281,
71.4320, 51.1295,
71.4350, 51.1310
]);
viewer.entities.add({
polyline: {
positions: positions,
width: 4,
material: Cesium.Color.ORANGE
}
});
Для более сложных GPX-треков используется динамическое формирование массива координат.
GPX часто содержит высоты, которые могут быть некорректными или отсутствовать. CesiumJS позволяет корректировать отображение:
polyline: {
positions: positions,
clampToGround: true
}
При включённом terrain модель трека может визуально корректироваться под рельеф:
viewer.terrainProvider = Cesium.createWorldTerrain();
Для имитации движения объекта используется
SampledPositionProperty.
const property = new Cesium.SampledPositionProperty();
property.addSample(
Cesium.JulianDate.fromIso8601("2024-01-01T10:00:00Z"),
Cesium.Cartesian3.fromDegrees(71.4304, 51.1281, 100)
);
property.addSample(
Cesium.JulianDate.fromIso8601("2024-01-01T10:05:00Z"),
Cesium.Cartesian3.fromDegrees(71.4320, 51.1295, 110)
);
viewer.entities.add({
position: property,
billboard: {
image: "vehicle.png",
scale: 0.8
},
path: {
width: 2,
material: Cesium.Color.CYAN
}
});
Для синхронизации используется viewer.clock:
viewer.clock.startTime = Cesium.JulianDate.fromIso8601("2024-01-01T10:00:00Z");
viewer.clock.currentTime = viewer.clock.startTime;
viewer.clock.stopTime = Cesium.JulianDate.fromIso8601("2024-01-01T11:00:00Z");
viewer.clock.multiplier = 10;
viewer.clock.shouldAnimate = true;
GPS-данные часто содержат шум. CesiumJS поддерживает интерполяцию:
property.setInterpolationOptions({
interpolationDegree: 2,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
});
Также применяется фильтрация точек до загрузки:
Для отображения ориентации используется вычисление курса:
function computeHeading(start, end) {
const carto1 = Cesium.Cartographic.fromCartesian(start);
const carto2 = Cesium.Cartographic.fromCartesian(end);
const y = Math.sin(carto2.longitude - carto1.longitude) * Math.cos(carto2.latitude);
const x = Math.cos(carto1.latitude) * Math.sin(carto2.latitude) -
Math.sin(carto1.latitude) * Math.cos(carto2.latitude) *
Math.cos(carto2.longitude - carto1.longitude);
return Math.atan2(y, x);
}
Далее значение используется для поворота модели:
orientation: new Cesium.VelocityOrientationProperty(property)
При работе с треками, содержащими десятки тысяч точек, ключевыми становятся оптимизации:
const positions = new Float64Array(largeArray);
const geometry = new Cesium.PolylineGeometry({
positions: Cesium.Cartesian3.fromDegreesArray(coords),
width: 3
});
time делают невозможной анимациюtrksegtrkptViewerpath: {
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.2,
color: Cesium.Color.YELLOW
}),
width: 5
}
Path создаёт динамическую линию, которая растёт по мере движения объекта, что особенно эффективно при анимации GPX-трека во времени.