mcpField
C
cesiumjs-primitives

cesiumjs-primitives

github.com/cesiumgs開発193237 回インストール6 週間前に更新
リポジトリ

cesiumjs-primitives について

低レベルの Primitive ジオメトリ、外観、バッチ処理を扱う CesiumJS 開発者向けのドメインスキルです。大量のベクターデータ向けに、バッファコレクションや GeoJSON プリミティブも解説します。

主な機能

  1. 01Primitive ジオメトリと外観の設定
  2. 02複数のジオメトリインスタンスのバッチ化
  3. 03インスタンス属性と表示状態の更新
  4. 04バッファプリミティブコレクション
  5. 05GeoJSON プリミティブの読み込み

活用シーン

  1. 01高性能な静的シーンの構築
  2. 02円柱などのボリューム形状の一括描画
  3. 03大規模な静的 GeoJSON データセットの読み込み

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/cesiumgs/cesiumjs-skills --skill cesiumjs-primitives

インストール後、Claude Code で/cesiumjs-primitivesを入力して読み込まれたか確認します。

詳細

CesiumJS プリミティブとジオメトリ

対象: CesiumJS v1.144+(ES モジュールのインポート、defaultValue の代わりに ?? を使用)

アーキテクチャ

Primitive API は Entity API の下位にある低レベルのレンダリング層で、利便性よりパフォーマンスを優先します。

基本式: Primitive = GeometryInstance[] + Appearance

  • GeometryInstance -- インスタンスごとの属性(色、表示状態)を備えた Geometry をワールド空間に配置します。
  • Geometry -- 形状(ポリゴン、ボックス、楕円体など)を表す頂点データです。
  • Appearance -- ジオメトリにシェーディングを適用する GLSL シェーダー、レンダリング状態、およびオプションの Material です。

プリミティブは初回レンダリング後は不変です。ジオメトリは変更できませんが、primitive.getGeometryInstanceAttributes(id) を使ってインスタンスごとの属性を更新できます。

Primitive

import {
  Viewer, Primitive, GeometryInstance, EllipseGeometry,
  EllipsoidSurfaceAppearance, Material, Cartesian3, Math as CesiumMath,
} from "cesium";

const viewer = new Viewer("cesiumContainer");
const scene = viewer.scene;

const primitive = scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new EllipseGeometry({
      center: Cartesian3.fromDegrees(-100.0, 40.0),
      semiMinorAxis: 250000.0,
      semiMajorAxis: 400000.0,
      rotation: CesiumMath.PI_OVER_FOUR,
      vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT, // must match appearance
    }),
    id: "myEllipse", // returned by Scene.pick()
  }),
  appearance: new EllipsoidSurfaceAppearance({ material: Material.fromType("Stripe") }),
}));
主なオプション
オプションデフォルト用途
geometryInstances--単一のインスタンスまたは配列
appearance--シェーディング(Appearance のサブクラス)
showtrue表示の切り替え
modelMatrixMatrix4.IDENTITYすべてのインスタンスを変換
asynchronoustrueWeb ワーカーでジオメトリを構築
releaseGeometryInstancestrueGPU へのアップロード後にジオメトリを解放
allowPickingtruefalse にすると GPU メモリを節約
shadowsShadowMode.DISABLED影の投影/受け取り

複数インスタンスのバッチ処理

1 つの Primitive 内のすべてのインスタンスは、単一のドローコールを共有します。

import {
  Primitive, GeometryInstance, RectangleGeometry, EllipseGeometry,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Rectangle, Color,
} from "cesium";

scene.primitives.add(new Primitive({
  geometryInstances: [
    new GeometryInstance({
      geometry: new RectangleGeometry({
        rectangle: Rectangle.fromDegrees(-140, 30, -100, 40),
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      id: "rect",
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.5)) },
    }),
    new GeometryInstance({
      geometry: new EllipseGeometry({
        center: Cartesian3.fromDegrees(-80, 35),
        semiMinorAxis: 200000.0,
        semiMajorAxis: 300000.0,
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      id: "ellipse",
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.BLUE.withAlpha(0.5)) },
    }),
  ],
  appearance: new PerInstanceColorAppearance(),
}));
体積ジオメトリのバッチ処理(CylinderGeometry のグリッド)

体積ジオメトリ(Cylinder、Box、Ellipsoid)は、各 GeometryInstance の modelMatrix を使って配置する必要があります。Matrix4.multiply を使ってワールド空間の基準点とローカルオフセットを結合し、すべてのインスタンスを 1 つの Primitive にまとめます。

import {
  Primitive, GeometryInstance, CylinderGeometry,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Matrix4, Transforms, Color, Math as CesiumMath,
} from "cesium";

const center = Cartesian3.fromDegrees(-73.9857, 40.7580);
const anchorFrame = Transforms.eastNorthUpToFixedFrame(center, undefined, new Matrix4());
const instances = [];
const GRID = 10;
const SPACING = 50; // metres

for (let row = 0; row < GRID; row++) {
  for (let col = 0; col < GRID; col++) {
    const xOffset = (col - GRID / 2) * SPACING;
    const yOffset = (row - GRID / 2) * SPACING;
    // Combine anchor ENU frame with a local XYZ offset
    const modelMatrix = Matrix4.multiply(
      anchorFrame,
      Matrix4.fromTranslation(new Cartesian3(xOffset, yOffset, 100), new Matrix4()),
      new Matrix4(),
    );
    instances.push(new GeometryInstance({
      geometry: new CylinderGeometry({
        length: 200,
        topRadius: 8,
        bottomRadius: 8,
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      modelMatrix,
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.fromRandom({ alpha: 1.0 })) },
    }));
  }
}

scene.primitives.add(new Primitive({
  geometryInstances: instances,
  appearance: new PerInstanceColorAppearance({ flat: true }),
}));

// Frame the grid so the full batch is visible -- a shallow pitch hides cylinders
// behind the foreground; a near-nadir pitch flattens them. Aim for ~-45° (-PI/4)
// at a range that covers the grid footprint (GRID * SPACING) with margin.
const range = GRID * SPACING * 3; // ~1500 m for a 10x10x50m grid
viewer.camera.lookAt(
  center,
  new Cartesian3(0, -range * 0.7, range * 0.7), // offset south + up for -45° pitch
);

主なパターン:

  • Matrix4.multiply(anchorFrame, Matrix4.fromTranslation(offset, result), result) -- 地理的な基準点における ENU フレームと、ローカルの東/北/上方向への平行移動を合成します。
  • Color.fromRandom({ alpha: 1.0 }) は、不透明度が完全なランダムカラーを生成します。虹色のバッチに適しています。
  • フレーミングが重要です: 垂直な体積のグリッドでは、範囲をおよそ 3 * gridFootprint とし、~-45°(-CesiumMath.PI_OVER_FOUR、約 -0.785 ラジアン)のピッチを推奨します。約 -0.6 ラジアンより浅いピッチでは、グリッドが画面外に出たり、建物の背後に隠れたりすることがあります。天底から見ると円柱は点状に平坦化され、バッチ化された「フィールド」の見た目が失われます。-0.61 ラジアンのピッチでも、グリッドが完全にフレーム外になるほど浅い場合があります。
  • 表示を確認する: シナリオで必要なのが「バッチが表示されること」だけなら、primitive.ready の後は、手動で調整したオフセットよりも viewer.camera.flyToBoundingSphere(primitive._boundingSpheres[0], { duration: 0 })(または viewer.flyTo(primitive))を優先してください。

インスタンスごとの属性の更新

import { ColorGeometryInstanceAttribute, ShowGeometryInstanceAttribute } from "cesium";

// Wait for async geometry compilation
const removeListener = scene.postRender.addEventListener(() => {
  if (!primitive.ready) return;
  const attrs = primitive.getGeometryInstanceAttributes("rect");
  attrs.color = ColorGeometryInstanceAttribute.toValue(Color.YELLOW);
  attrs.show = ShowGeometryInstanceAttribute.toValue(true);
  removeListener();
});

PrimitiveCollection

入れ子にできるコンテナです。scene.primitives 自体も PrimitiveCollection です。

import { PrimitiveCollection, BillboardCollection, LabelCollection } from "cesium";

const group = new PrimitiveCollection();
group.add(new BillboardCollection());
group.add(new LabelCollection());
scene.primitives.add(group);
group.show = false; // toggle all children

ベクターデータの経路の選択

必要なもの使用するもの
Entity のライフサイクル、クラスタリング、エンティティごとのスタイル設定、時間とともに変化する値cesiumjs-entities の GeoJsonDataSource
オーバーヘッドが小さく、プリミティブレベルのパフォーマンスを備えた大きな GeoJSON オブジェクト 1 つこのスキルの GeoJsonPrimitive
タイル化されたベクターデータ、3D Tiles の LOD、メタデータによるスタイル設定、フィーチャーのピッキングcesiumjs-3d-tiles の MVTDataProvider
ポイント/ポリライン/ポリゴンのバッファを完全に手動で扱う高スループット処理BufferPointCollection、BufferPolylineCollection、BufferPolygonCollection

バッファープリミティブコレクション(実験的機能、1.140 以降)

Entity/DataSource のオーバーヘッドが大きすぎる非常に大規模なベクターデータセットには、BufferPointCollection、BufferPolylineCollection、BufferPolygonCollection を使用してください。これらの API は 1.140(#13212)で導入され、1.142 までに改良されました。実験的な機能であり、フライウェイトプリミティブオブジェクトを使用します。数千件のアイテムを追加または反復処理する際は、BufferPoint、BufferPolyline、BufferPolygon をそれぞれ 1 つずつ再利用してください。

import {
  BlendOption,
  BoundingSphere,
  BufferPoint,
  BufferPointCollection,
  BufferPointMaterial,
  Cartesian3,
  Color,
} from "cesium";

const positions = [
  Cartesian3.fromDegrees(-75.16, 39.95),
  Cartesian3.fromDegrees(-73.98, 40.75),
];

const points = scene.primitives.add(new BufferPointCollection({
  primitiveCountMax: positions.length,
  allowPicking: true,
  blendOption: BlendOption.TRANSLUCENT,
  boundingVolume: BoundingSphere.fromPoints(positions), // world space in 1.142+
}));

const point = new BufferPoint();
const material = new BufferPointMaterial({
  color: Color.CYAN.withAlpha(0.65),
  outlineColor: Color.WHITE.withAlpha(0.9),
  outlineWidth: 2,
  size: 10,
});

positions.forEach((position, featureId) => {
  points.add({
    position,
    featureId,
    material,
  }, point);
});

const picked = scene.pick(windowPosition);
if (picked?.collection === points) {
  console.log(picked.index, picked.primitive.featureId);
}

破壊的変更(1.141、#13448): BufferPrimitiveCollection.modelMatrix、boundingVolume、boundingVolumeWC は 読み取り専用 になりました。オブジェクトをその場で変更することはできますが、プロパティの再代入(collection.modelMatrix = ...)は例外をスローします。新しいオブジェクトに差し替えるのではなく、既存の行列またはボリュームを更新してください。

1.142 の注意事項:

  • boundingVolume はローカル/モデル空間ではなく、ワールド空間になりました。手動で指定する場合は、コレクションの modelMatrix による変換を自分で含めてください。
  • boundingVolume を指定すると、自動再計算はスキップされます。大規模なアニメーションコレクションには便利ですが、ボリュームを有効な状態に保つ責任は利用者にあります。
  • 3 種類すべてのバッファーコレクションで blendOption がサポートされ、BufferPrimitiveMaterial#color のアルファ値が有効になります。また、BufferPointCollection では outlineColor.alpha も反映されます。
  • すべてのマテリアルが完全に不透明な場合に限り、BlendOption.OPAQUE を使用してください。アルファ値が変化する場合は、TRANSLUCENT または混合ブレンディングを使用してください。

1.143 では、BufferPointCollection の outlineWidth が 0 のときに、outlineColor が塗りつぶしに漏れなくなりました。アウトラインを無効にするには幅を 0 に設定してください。バッチ処理を複雑にしてしまう透明なアウトラインを使った回避策は削除してください。

GeoJsonPrimitive(実験的機能、1.142 以降)

GeoJsonPrimitive は GeoJSON をバッファープリミティブコレクションに直接読み込み、GeoJsonDataSource と Entity レイヤーを介しません。大規模な静的ベクターデータセットや、一括更新するベクターデータセットにはこちらを優先してください。Entity の利便性、時間とともに変化するプロパティ、クラスタリング、または DataSource のライフサイクルとの統合が必要な場合は、引き続き GeoJsonDataSource を使用してください。

import { GeoJsonPrimitive } from "cesium";

const counties = await GeoJsonPrimitive.fromUrl("/data/counties.geojson", {
  allowPicking: true,
});
scene.primitives.add(counties);

console.log(counties.featureCount);
console.log(counties.points);    // BufferPointCollection | undefined
console.log(counties.polylines); // BufferPolylineCollection | undefined
console.log(counties.polygons);  // BufferPolygonCollection | undefined

// Picking returns the GeoJsonPrimitive pick object, including source properties.
const picked = scene.pick(windowPosition);
if (picked?.parentPrimitive === counties) {
  const featureId = picked.primitive.featureId;
  console.log(counties.getId(featureId));
  console.log(counties.getProperties(featureId));
}

GeoJSON がすでにメモリ上にある場合は、GeoJsonPrimitive.fromGeoJson(parsedObject) を使用できます。ソースフィーチャー ID は ids/getId() から、ソースプロパティは properties/getProperties() から取得できます。

組み込みジオメトリ型(31 種類)

すべてのジオメトリには、形状パラメーターと Appearance に合った vertexFormat を指定します。ほとんどのジオメトリには対応する *OutlineGeometry があります。アウトラインには別個の Primitive が必要です。

塗りつぶし+アウトラインのパターン
import {
  Primitive, GeometryInstance, PolygonGeometry, PolygonOutlineGeometry,
  PolygonHierarchy, PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Color,
} from "cesium";

const positions = Cartesian3.fromDegreesArray([-115, 37, -115, 32, -107, 33, -102, 35]);

// Fill primitive
scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolygonGeometry({
      polygonHierarchy: new PolygonHierarchy(positions),
      vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
    }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.CYAN.withAlpha(0.5)) },
  }),
  appearance: new PerInstanceColorAppearance(),
}));

// Outline primitive (separate draw call)
scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolygonOutlineGeometry({ polygonHierarchy: new PolygonHierarchy(positions) }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.WHITE) },
  }),
  appearance: new PerInstanceColorAppearance({ flat: true }),
}));
ジオメトリ一覧

特に記載がない限り、すべての XxxGeometry には対応する XxxOutlineGeometry があります。

サーフェス(GroundPrimitive で動作):CircleGeometry、CorridorGeometry、EllipseGeometry、PolygonGeometry、RectangleGeometry。

ボリューム(modelMatrix が必要):BoxGeometry(fromDimensions())、CylinderGeometry(topRadius != bottomRadius の場合は円錐)、EllipsoidGeometry、SphereGeometry、FrustumGeometry、PlaneGeometry。

パス:CorridorGeometry(バッファー付きパス)、PolylineVolumeGeometry(パスに沿って押し出す 2D 形状)、WallGeometry(垂直のカーテン)。

ポリゴン:PolygonGeometry(PolygonHierarchy で穴を指定)、CoplanarPolygonGeometry(地球表面以外)。

ライン(アウトラインなし):PolylineGeometry(ピクセル幅)、SimplePolylineGeometry(1 ピクセル)、GroundPolylineGeometry(GroundPolylinePrimitive 専用)。

地表面から離れたジオメトリの配置

Box、Ellipsoid、Cylinder、Frustum では、GeometryInstance に modelMatrix が必要です。

import { GeometryInstance, BoxGeometry, PerInstanceColorAppearance,
  ColorGeometryInstanceAttribute, Cartesian3, Matrix4, Transforms, Color } from "cesium";

const modelMatrix = Matrix4.multiplyByTranslation(
  Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-105, 40)),
  new Cartesian3(0, 0, 250000), new Matrix4(),
);
new GeometryInstance({
  geometry: BoxGeometry.fromDimensions({
    dimensions: new Cartesian3(400000, 300000, 500000),
    vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
  }),
  modelMatrix,
  id: "floatingBox",
  attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.CORAL) },
});

外観(7 種類)

外観使用例マテリアル?
PerInstanceColorAppearanceインスタンスごとの色いいえ
MaterialAppearance任意のジオメトリ+マテリアルはい
EllipsoidSurfaceAppearanceサーフェスジオメトリ+マテリアル(属性が少ない)はい
PolylineColorAppearanceインスタンスごとの色を設定したポリラインいいえ
PolylineMaterialAppearanceマテリアルを使用したポリラインはい
DebugAppearance頂点属性の可視化いいえ
Appearance基底クラス/カスタムシェーダー任意

ジオメトリの vertexFormat は外観と一致する必要があります。外観の静的な VERTEX_FORMAT を使用してください。ライティングなしで PerInstanceColorAppearance を使う場合は、FLAT_VERTEX_FORMAT を使用してください。

MaterialAppearance の例
import { Primitive, GeometryInstance, WallGeometry, MaterialAppearance, Material, Cartesian3 } from "cesium";

scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new WallGeometry({
      positions: Cartesian3.fromDegreesArrayHeights([-115, 44, 200000, -110, 44, 200000, -105, 44, 200000]),
      vertexFormat: MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat,
    }),
  }),
  appearance: new MaterialAppearance({
    material: Material.fromType("Checkerboard"),
    faceForward: true, // shade both sides
  }),
}));

GroundPrimitive

ジオメトリを地形/3D Tiles にドレープします。対応しているジオメトリ:CircleGeometry、CorridorGeometry、EllipseGeometry、PolygonGeometry、RectangleGeometry。scene.primitives に追加します(scene.groundPrimitives にも追加できますが、Ion の地形を使わずに公開ベースマップを使用する場合は、通常 scene.primitives を追加先とします)。

import { GroundPrimitive, GeometryInstance, PolygonGeometry, PolygonHierarchy,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute, ClassificationType,
  Cartesian3, Color } from "cesium";

scene.primitives.add(new GroundPrimitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolygonGeometry({
      polygonHierarchy: new PolygonHierarchy(
        Cartesian3.fromDegreesArray([-112, 36, -112, 36.1, -111.9, 36.1]),
      ),
    }),
    id: "groundPolygon",
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.5)) },
  }),
  appearance: new PerInstanceColorAppearance({ flat: true, translucent: true }),
  classificationType: ClassificationType.TERRAIN, // TERRAIN, CESIUM_3D_TILE, or BOTH
}));

GroundPolylinePrimitive

ポリラインを地形にドレープします。scene.primitives に追加します(scene.groundPrimitives には追加しません)。

import { GroundPolylinePrimitive, GeometryInstance, GroundPolylineGeometry,
  PolylineColorAppearance, ColorGeometryInstanceAttribute, Cartesian3, Color } from "cesium";

scene.primitives.add(new GroundPolylinePrimitive({
  geometryInstances: new GeometryInstance({
    geometry: new GroundPolylineGeometry({
      positions: Cartesian3.fromDegreesArray([-112.13, 36.05, -112.09, 36.10, -112.13, 36.17]),
      width: 4.0,
      loop: true,
    }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.LIME.withAlpha(0.7)) },
  }),
  appearance: new PolylineColorAppearance(),
}));

ClassificationPrimitive

地形または 3D Tiles を分類するボリュームをハイライト表示します。有効なジオメトリ:BoxGeometry、CylinderGeometry、EllipsoidGeometry、PolylineVolumeGeometry、SphereGeometry、および押し出しサーフェスジオメトリ。

import { ClassificationPrimitive, GeometryInstance, BoxGeometry, PerInstanceColorAppearance,
  ColorGeometryInstanceAttribute, ClassificationType, Cartesian3, Transforms, Color } from "cesium";

scene.primitives.add(new ClassificationPrimitive({
  geometryInstances: new GeometryInstance({
    geometry: BoxGeometry.fromDimensions({
      dimensions: new Cartesian3(100, 100, 50),
      vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
    }),
    modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-75.59, 40.04, 25)),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.YELLOW.withAlpha(0.5)) },
  }),
  classificationType: ClassificationType.BOTH,
}));

BillboardCollection

ビューポートに揃えた画像を GPU 効率よく表示します。大規模な場合、エンティティよりはるかに高いパフォーマンスを発揮します。

破壊的変更(1.140、#13253): BillboardCollection と LabelCollection は WebGL 2、または ANGLE_instanced_arrays と MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0 を備えた WebGL 1 が必要になりました。非対応デバイスでは 描画されなくなります。レガシー WebGL 1 ハードウェアも引き続き対象とする場合は、 scene.context.webgl2 を確認する(または拡張機能を機能検出する)ようにしてください。

基本的な使い方

互換性の修正(1.143): アプリケーションがグローバルの Promise 実装を置き換えている場合でも、 ビルボード画像の読み込みでクラッシュしなくなりました。URL、読み込み済み画像、またはキャンバスには公開 プロパティ image を使用し、Resource またはコールバックには setImage を使用してください。 ネイティブの Promise との同一性に基づく分岐や、このバグに対する互換性シムの保持は行わないでください。

import { BillboardCollection, Cartesian3, Color, NearFarScalar,
  HeightReference, HorizontalOrigin, VerticalOrigin } from "cesium";

const billboards = scene.primitives.add(new BillboardCollection({ scene }));
const b = billboards.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04),
  image: "marker.png",
  horizontalOrigin: HorizontalOrigin.CENTER,
  verticalOrigin: VerticalOrigin.BOTTOM,
  heightReference: HeightReference.CLAMP_TO_GROUND,
  scaleByDistance: new NearFarScalar(1000, 1.5, 1e7, 0.3),
});
b.position = Cartesian3.fromDegrees(-75.60, 40.05); // update dynamically
billboards.remove(b);
PinBuilder -- プロシージャルなピン画像

PinBuilder は、外部画像ファイルを使わずに、実行時にキャンバスベースのピンアイコンを生成します。単色のピンには fromColor を、ラベル付きのピンには fromText を使用します。返されたキャンバスをビルボードの image として渡してください。

シナリオで都市/項目の順序付きリストと、インデックスによる色分け方式が指定されている場合は、指定された順序でソース配列を反復処理し、ループのインデックスを HSL 色相インデックスとして直接使用してください。ソースリストを並べ替えると(例:緯度順にソートすると)、各都市に割り当てられる色が入れ替わり、見た目のチェックに失敗します。

import { BillboardCollection, PinBuilder, Cartesian3, Color, VerticalOrigin } from "cesium";

const pinBuilder = new PinBuilder();
const cities = [
  { name: "Boston",       lng: -71.0589, lat: 42.3601 },
  { name: "New York",     lng: -74.0060, lat: 40.7128 },
  { name: "Philadelphia", lng: -75.1652, lat: 39.9526 },
  { name: "Washington DC",lng: -77.0369, lat: 38.9072 },
  { name: "Miami",        lng: -80.1918, lat: 25.7617 },
];

const billboards = scene.primitives.add(new BillboardCollection({ scene }));

cities.forEach((city, index) => {
  billboards.add({
    position: Cartesian3.fromDegrees(city.lng, city.lat),
    // Color.fromHsl(hue 0-1, saturation, lightness) produces evenly-spaced hues
    image: pinBuilder.fromColor(Color.fromHsl(index / cities.length, 0.8, 0.5), 48),
    verticalOrigin: VerticalOrigin.BOTTOM,
  });
});

// Text label pin: pinBuilder.fromText("A", Color.ROYALBLUE, 48)
// fromColor / fromText return a canvas -- pass directly as image

Color.fromHsl(hue, saturation, lightness) -- hue(0~1、全周を循環)を変化させてスペクトル全体の色を生成します。N 個の項目を虹色にする場合に便利です:Color.fromHsl(i / n, 0.8, 0.5)。Color.fromRandom({ alpha }) -- アルファ値を固定して、色相/彩度/明度をランダムに設定します。

LabelCollection

import { LabelCollection, Cartesian3, Cartesian2, Color, LabelStyle, VerticalOrigin } from "cesium";

const labels = scene.primitives.add(new LabelCollection({ scene }));
labels.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04, 300),
  text: "Philadelphia",
  font: "16px sans-serif",
  fillColor: Color.WHITE,
  outlineColor: Color.BLACK,
  outlineWidth: 2,
  style: LabelStyle.FILL_AND_OUTLINE,
  verticalOrigin: VerticalOrigin.BOTTOM,
  pixelOffset: new Cartesian2(0, -10),
});

PointPrimitiveCollection

import { PointPrimitiveCollection, Cartesian3, Color, NearFarScalar } from "cesium";

const points = scene.primitives.add(new PointPrimitiveCollection());
points.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04),
  pixelSize: 10,
  color: Color.YELLOW,
  outlineColor: Color.BLACK,
  outlineWidth: 2,
  scaleByDistance: new NearFarScalar(1000, 1.0, 1e7, 0.1),
});

CloudCollection と PolylineCollection

import { CloudCollection, PolylineCollection, Cartesian3, Cartesian2, Color, Material } from "cesium";

// Procedural cumulus clouds
const clouds = scene.primitives.add(new CloudCollection());
clouds.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04, 1500),
  scale: new Cartesian2(40, 12),
  maximumSize: new Cartesian3(40, 12, 15),
  slice: 0.36,
});

// Low-level polyline collection
const polylines = scene.primitives.add(new PolylineCollection());
polylines.add({
  positions: Cartesian3.fromDegreesArray([-75, 40, -70, 42, -65, 38]),
  width: 3.0,
  material: Material.fromType("Color", { color: Color.AQUA }),
});

Primitive によるポリライン

import { Primitive, GeometryInstance, PolylineGeometry, PolylineColorAppearance,
  ColorGeometryInstanceAttribute, Cartesian3, Color, ArcType } from "cesium";

scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolylineGeometry({
      positions: Cartesian3.fromDegreesArray([0, 0, 5, 0]),
      width: 10.0,
      vertexFormat: PolylineColorAppearance.VERTEX_FORMAT,
      arcType: ArcType.GEODESIC, // GEODESIC, RHUMB, or NONE
    }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.WHITE) },
  }),
  appearance: new PolylineColorAppearance({ translucent: false }),
}));

列挙型

列挙型値使用箇所
ArcTypeGEODESIC、RHUMB、NONEPolylineGeometry、PolygonGeometry
CornerTypeROUNDED、MITERED、BEVELEDCorridorGeometry、PolylineVolumeGeometry
ClassificationTypeTERRAIN、CESIUM_3D_TILE、BOTHGroundPrimitive、ClassificationPrimitive
PrimitiveTypePOINTS、LINES、TRIANGLES など低レベルの Geometry
CloudTypeCUMULUSCloudCollection

プリミティブシーンのカメラフレーミング

プリミティブ数やカメラ位置などのプログラムによるチェックに合格しても、視覚的な出力がレンダリングされたジオメトリをまったく捉えていない場合があります。視覚的な評価でよく見られる経験則をいくつか紹介します。

  • ボリュームジオメトリのグリッド(円柱/ボックス): 範囲を ~3 * gridFootprint とし、ピッチを -PI/4(約-0.785 ラジアン、-45°)にします。-0.6 ラジアンより浅いピッチでは、グリッドがカメラの後ろに回ったり、フレームから外れたりすることがあります。-PI/2 に近いピッチでは垂直方向の広がりが平坦化し、一連の形状が点のように見えます。
  • 地形に沿って配置されたサーフェスポリゴン(GroundPrimitive): 急なピッチ(約-50°)と遠い範囲を組み合わせると、ポリゴンは画面下端に細い帯状に縮小されます。範囲を狭めるか、より緩やかなピッチにして、ポリゴンがフレームの中央を占めるようにします。
  • 大陸規模のポリライン(Route 66、大陸横断ルート): 重心から約5~6 Mm上空の、ほぼ真下を向いた俯瞰視点にすると、経路全体を表示できます。
  • 海岸線/ルートに沿ったピンの連なり: 連なりの中点にカメラを合わせ、すべての辺に余白を設けつつ、連なりのバウンディングボックス全体が収まる範囲にします。

判断に迷った場合は、primitive.ready の後に viewer.flyTo(primitive) または viewer.camera.flyToBoundingSphere(primitive._boundingSpheres[0]) を呼び出すと、バッチに合わせて自動的にフレーミングされます。

パフォーマンスのヒント

  1. できるだけバッチ化します。 何千もの GeometryInstances を1つの Primitive にまとめて、ドローコールを1回にします。
  2. 各インスタンスに個別の色だけが必要な場合は、PerInstanceColorAppearance を使います。
  3. ライティングが不要な場合は、PerInstanceColorAppearance で flat: true を設定します。 FLAT_VERTEX_FORMAT を使用します。
  4. ピッキングしない Primitive では allowPicking: false を設定します。 GPU メモリを節約できます。
  5. asynchronous: true(デフォルト)を維持します。 インスタンス属性にアクセスする前に primitive.ready を確認します。
  6. Billboard、Label、PointPrimitive には、より少数の大きなコレクションを優先します。 更新頻度ごとにグループ化します。
  7. すべてのアイテムが不透明な場合は、BillboardCollection/PointPrimitiveCollection で BlendOption.OPAQUE を使います(最大2倍の性能向上)。
  8. 軽量な更新で問題ない大規模なベクターデータには、バッファープリミティブコレクションを使います。
  9. 大規模なアニメーションコレクションでは、バッファーコレクションのバウンディングボリュームを事前計算します。ただし、1.142以降ではワールド座標系であることに注意してください。
  10. 地形に沿わせるには、エンティティの heightReference ではなく GroundPrimitive を使います。
  11. 塗りつぶしと輪郭線は2つの Primitive に分けます。 これらはドローコールを共有できません。
  12. 未使用の頂点属性の計算を省略できるよう、vertexFormat を appearance に正確に合わせます。
  13. サーフェスジオメトリでは、MaterialAppearance より EllipsoidSurfaceAppearance を使います。 頂点属性が少なくて済みます。

関連項目

  • cesiumjs-entities -- プリミティブをラップし、時間とともに変化するプロパティを備えた高レベルの Entity API。
  • cesiumjs-3d-tiles -- タイル化されたベクターデータをランタイム 3D Tiles として扱うには、MVTDataProvider を使います。
  • cesiumjs-materials-shaders -- Appearance やポストプロセッシングで使用される Material(Fabric)システム。
  • cesiumjs-spatial-math -- ジオメトリの配置に使う Cartesian3、Matrix4、Transforms、座標変換。

コメント