Cesium uses czml to implement dynamic routes

demand scene

 For example, some scene roaming in the project shows a designated route in a certain area, and an object moves like a vehicle; some projects involve patrol routes, and the patrol routes of multiple personnel are displayed on the map. These demand situations all require the use of dynamic routes. I mentioned this in my previous daily problem article, but it is better to take it out separately.

reference

Cesium's wiki in github has a detailed introduction to the structure of czml and the values ​​​​that can be set for each attribute.

 https://github.com/AnalyticalGraphicsInc/czml-writer/wiki/CZML-Structure

When I was dealing with the problem, I also found a blogger on csdn introducing the use of czml. Here is the blog post link

 Cesium-CZML bird model flight path

At that time, I was troubled by how to set the velocityReference value of orientation in the czml object. Cesium's wiki only said that this value should be of string type, but there was no use case. Then I found the above article. It helped me a lot.

a brief introdction

Personally, I think that the usefulness of cesium is that its display dimension is not only geographical location, but also the concept of time and space. In a certain period of time, the location of a certain area or entity object is displayed.

After understanding this, you will understand czml, which is used to display the positional relationship of label or billboard or model in a certain period of time. The czml file itself is an array, and each sub-item in the array is an object. There is a hard requirement that the first object of this array is a declaration object, just like the first line of the h5 html file will declare <!DOCTYPE html>.

Starting from the second object, it is the entity to be displayed. There is an explanation in the official wiki, so I won't repeat it. Just explain the concept and some points of attention.

The first is the id. If you need to get these entities later, then you need to set the id value for each object, and this id value will become the id of the generated entity. If the id you use is repeated, then CzmlDataSource will think that you are describing the same entity object. If these objects with repeated ids have the same time period, the last one will prevail.

That's why, in some cases, four or five are set but only one is shown on the map. I'm having trouble plotting multiple shipping routes for the same time period. It is because the id in czml is repeated.

basic concept

Unlike setting entities with code, czml belongs to an array, which means that the data inside is in the form of key-value. Object name, value may be string, number, boolean or array. When you are not sure what type of value should be set for the property you want to use, it is best to go to the wiki above.

Use js code to set entity attributes, and these values ​​can be regarded as properties. If you use czml to set, then these will be regarded as Packets waiting to be packaged.

 There are many built-in methods in CzmlDataSource to encapsulate these values ​​into attributes using js methods. For example, for postion, its value is an array, and there are different methods to judge how to parse it. Because this array may be a simple geographic location [x, y, height], or it may contain a time attribute [time, x, y, height].

The following is the source code of line 5110-5140

CzmlDataSource._processCzml = function (
  czml,
  entityCollection,
  sourceUri,
  updaterFunctions,
  dataSource
) {
  updaterFunctions = defaultValue(updaterFunctions, CzmlDataSource.updaters);

  if (Array.isArray(czml)) {
    for (let i = 0, len = czml.length; i < len; ++i) {
      processCzmlPacket(
        czml[i],
        entityCollection,
        updaterFunctions,
        sourceUri,
        dataSource
      );
    }
  } else {
    processCzmlPacket(
      czml,
      entityCollection,
      updaterFunctions,
      sourceUri,
      dataSource
    );
  }
};

This updaterFunctions is actually the data processing method. In the source code, it is actually these. The following code is in the source code line 5006-5130

CzmlDataSource.updaters = [
  processBillboard, //
  processBox, //
  processCorridor, //
  processCylinder, //
  processEllipse, //
  processEllipsoid, //
  processLabel, //
  processModel, //
  processName, //
  processDescription, //
  processPath, //
  processPoint, //
  processPolygon, //
  processPolyline, //
  processPolylineVolume, //
  processProperties, //
  processRectangle, //
  processPosition, //
  processTileset, //
  processViewFrom, //
  processWall, //
  processOrientation, //
  processAvailability,
];

These cover almost every value that can be set. Start with process, followed by what it handles.

So since czml is treated as an array, the first object is set to a specific value. How is it identified in the source code?

function processCzmlPacket(
  packet,
  entityCollection,
  updaterFunctions,
  sourceUri,
  dataSource
) {
  let objectId = packet.id;
  if (!defined(objectId)) {
    objectId = createGuid();
  }

  currentId = objectId;

  if (!defined(dataSource._version) && objectId !== "document") {
    throw new RuntimeError(
      "The first CZML packet is required to be the document object."
    );
  }

  if (packet["delete"] === true) {
    entityCollection.removeById(objectId);
  } else if (objectId === "document") {
    processDocument(packet, dataSource);
  } else {
    const entity = entityCollection.getOrCreateEntity(objectId);

    const parentId = packet.parent;
    if (defined(parentId)) {
      entity.parent = entityCollection.getOrCreateEntity(parentId);
    }

    for (let i = updaterFunctions.length - 1; i > -1; i--) {
      updaterFunctions[i](entity, packet, entityCollection, sourceUri);
    }
  }

  currentId = undefined;
}

The above code is the method executed when processing the czml array. It can be seen that when a dataSource is initialized, the version version is not set. If the first value of your czml array does not have a document object, an error will be reported directly. After the correct setting, the first execution will enter the processDocument method. It is on line 2634-2682 of the source code.

function processDocument(packet, dataSource) {
  const version = packet.version;
  if (defined(version)) {
    if (typeof version === "string") {
      const tokens = version.split(".");
      if (tokens.length === 2) {
        if (tokens[0] !== "1") {
          throw new RuntimeError("Cesium only supports CZML version 1.");
        }
        dataSource._version = version;
      }
    }
  }

  if (!defined(dataSource._version)) {
    throw new RuntimeError(
      "CZML version information invalid.  It is expected to be a property on the document object in the <Major>.<Minor> version format."
    );
  }

  const documentPacket = dataSource._documentPacket;

  if (defined(packet.name)) {
    documentPacket.name = packet.name;
  }

  const clockPacket = packet.clock;
  if (defined(clockPacket)) {
    const clock = documentPacket.clock;
    if (!defined(clock)) {
      documentPacket.clock = {
        interval: clockPacket.interval,
        currentTime: clockPacket.currentTime,
        range: clockPacket.range,
        step: clockPacket.step,
        multiplier: clockPacket.multiplier,
      };
    } else {
      clock.interval = defaultValue(clockPacket.interval, clock.interval);
      clock.currentTime = defaultValue(
        clockPacket.currentTime,
        clock.currentTime
      );
      clock.range = defaultValue(clockPacket.range, clock.range);
      clock.step = defaultValue(clockPacket.step, clock.step);
      clock.multiplier = defaultValue(clockPacket.multiplier, clock.multiplier);
    }
  }
}

It's very simple and rude. Check the string type directly and check the length. The version must start with 1. No matter how much you have behind it, as long as the string is separated by a decimal point, two values ​​can be separated. Very rough. The next step is to set the time clock.

Caution

If it is read from a czml file, then just use the Cesium.CzmlDataSource.load method directly. But sometimes, according to certain check conditions or time settings, you want to manually set a czml array to load. Consider the following ideas:

1. Create an array entityArr to store manually created objects, each sub-item in this array is an object.

2. Create an object object, set version, clock time information. Add this object to the head of the array entityArr.

3. Use the Cesium.CzmlDataSource.load(entityArr) method to load, which returns a promise, so you can get the generated dataSource and get the corresponding entity in the then method for specialization.

In many cases, the time period in which the czml is displayed is judged by acquisition. In this case, there is no need to generate the first object first, and just add it later. After knowing the time period displayed by all entities, it is natural to set when the clock ends.

Cesium.CzmlDataSource.load(....).then(function(datasource) { processing code });

Like some custom materials, you need to add them manually after loading. datasource.entities.values ​​contains all the entities defined in the czml array, and of course you can also get specific ones through the set id. If no id value is given, the id will be automatically generated, but when you need it, you don't know which one it corresponds to.

time setting

The strange time strings in availability and clock, according to the official website wiki, are an Iso8601 time format. Cesium uses JulianDate, so there is also conversion.

The Cesium.JulianDate.toIso8601() method receives two parameters. The first parameter is a JulianDate object, and the second parameter is a number, similar to the toFixed method, which retains the same number of decimal places. Basically pass 0. It will return a string.

sample code

// 将当前时间设置为开始时间
const start = Cesium.JulianDate.fromDate(new Date());

//生成一个czml数组的子项对象
            const obj = {
                id: `test`,
                availability: `${Cesium.JulianDate.toIso8601(start, 0)}/${Cesium.JulianDate.toIso8601(Cesium.JulianDate.addSeconds(
                    start,
                    6000,
                    new Cesium.JulianDate()
                ), 0)}`,
                path: {
                    width: 8,
                    leadTime: 10,
                    trailTime: 1000,
                    resolution: 5,
                    zIndex: 3,
                },
                model: {
                    gltf: '模型文件路径',
                    minimumPixelSize: 64
                },
                orientation: {
                    interpolationAlgorithm: 'LINEAR',
                    interpolationDegree: 1,
                    epoch: `${Cesium.JulianDate.toIso8601(start, 0)}`,
                    velocityReference: "#position"
                },
                position: {
                    epoch: `${Cesium.JulianDate.toIso8601(start, 0)}`,
                    interpolationDegree: 5,
                    interpolationAlgorithm: 'HERMITE',
                    cartographicDegrees: 一个数组值
                }
            };
// 通过某些计算,得知时间将在某个值结束
const end = 通过代码设置的值;
// czml数组首个对象设置
        let czml = [
            {
                id: "document",
                name: "duangongfenxi",
                version: "1.0",
                clock: {
                    interval: `${Cesium.JulianDate.toIso8601(start, 0)}/${Cesium.JulianDate.toIso8601(end, 0)}`,
                    currentTime: `${Cesium.JulianDate.toIso8601(start, 0)}`,
                    multiplier: 10,
                    range: 'CLAMPED'
                }
            }
        ];

It is not recommended to use czml path to make a path with a large span, because it will penetrate the ground. It does not have the property of sticking to the ground like polyline, and the two coordinate points are directly too far away, and setting an interpolator cannot solve the problem of ground penetration.

If after using czml, you find that you can only see the route in some places, you can try to set the plane mode viewer.scene.model = 2 in the console; the two points with a large span cannot be solved only by setting the height. There are trade-offs.

Guess you like

Origin blog.csdn.net/GhostPaints/article/details/125818789