ドキュメント / リファレンス

API リファレンス

@mirgrid/web-sdk の公開 API を、分類ごとにすべて載せます。使い方の流れは はじめに や各ガイドで説明しているので、ここでは関数ごとの引数・戻り値・例外の条件をまとめます。

SDK には 4 つの入口があります。

import 先 中身
@mirgrid/web-sdk 初期化、位置と空間アドレス、セルの集合、voxel 化、配信データ、低レベルの LOD
@mirgrid/web-sdk/render.mjs 露出面を WebGL2 で描く描画器
@mirgrid/web-sdk/creation.mjs 作図面に描いた多角形を押し出して、形を作る関数
@mirgrid/web-sdk/lod.mjs LOD の核を wasm のメモリ越しに呼ぶための層

例の出力は、Node で実行したときにそのまま表示されたものです。

目次

共通の約束

単位と座標

  • 長さの単位は m、角度の単位は度です。ラジアンを返すのは spatial_ecef_to_llh_rad だけです。
  • 緯度経度は、経度・緯度の順に並べます。高さは楕円体高 [m] です。楕円体は WGS84 です(日本の測地系 JGD2011 が使う GRS80 との差は、日本の範囲で 0.2 mm 未満です)。
  • ECEF は、地球の中心を原点にした直交座標 [m] です。
  • ENU は、ある地点(原点)から見た東・北・上の直交座標 [m] です。
  • 平面直角座標系は、JGD2011 の 1〜19 系(EPSG 6669〜6687)に対応しています。easting は東向きの座標(測量でいう Y)、northing は北向きの座標(測量でいう X)です。

レベルとセル

空間を立方体のセルに分けて扱います。セルの細かさをレベルと呼びます。レベルは 1〜30 の整数で、数が大きいほど細かくなります。レベルを L とすると、セルの一辺は 2^(24−L) m です。

レベル セルの一辺
1 8,388,608 m
16 256 m
20 16 m
24 1 m
26 0.25 m
30 0.015625 m (1/64 m)

セルの位置は、各軸の番号 (voxel index) vx, vy, vz で表します。番号は 0 以上 2^レベル 未満の整数です。JavaScript の Number のまま正確に表せるので、BigInt は使いません。範囲の外の番号や整数でない番号を渡すと、どの関数も Error を投げます。

空間アドレスは、セルを表す 32 桁の 16 進の文字列です。レベルと位置の両方を含むので、文字列だけで 1 つのセルが決まります。

格子の種類

セルを切る格子には 2 種類あります。

  • 地球基準の格子 (ECEF): ECEF の軸に沿った格子です。地球全体で 1 つなので、どこでも同じ番号の付け方になります。ふだんはこちらを使います。
  • 現場基準の格子 (site): 現場の 1 点 (anchor) での東・北・上に沿った格子です。セルが現場で水平な立方体になります。使える範囲は anchor の中心から各軸 2048 m 未満です。

格子は次の形の object(記述子)で表せます。JSON にして保存できます。

{ kind: 'ecef' }
{ kind: 'site', datum: 'JGD2011', epoch: 2011.4, anchor: '0000000002e9380374bc9c0000000000', yaw: 0 }

datum は測地系の名前で、'JGD2011'・'ITRF2014'・'unspecified' のどれかです(大文字と小文字は区別しません)。epoch は測地系ごとに決まった値です(JGD2011 は 2011.4、ITRF2014 は 2010、unspecified には無し)。anchor はレベル 16 のセルの空間アドレスです。yaw は格子の向きで、0〜65535 の整数(1 周 = 65536)です。度からの換算は spatial_yaw_from_deg を使います。

エラー

不正な入力を受けると、関数は Error を投げます。error.message に理由が入ります。黙って別のセルや既定値に置き換えることはしません。

init の前に wasm の関数を呼ぶと TypeError になります。

free() が要るもの

次の 4 つのクラスは、データを wasm のメモリに持っています。使い終わったら free() を呼んでください。

クラス 作る関数
CellSetJs new CellSetJs・from_indices・from_ecef_points・集合演算・ops_*・VoxelizerJs.toCellSet
DecodedVoxels decode_voxel_packet・decode_voxel_packet_lod・decode_voxel_packet_in_frame
FrameJs FrameJs.ecef・FrameJs.site・FrameJs.fromDescriptor
VoxelizerJs new VoxelizerJs・VoxelizerJs.siteAuto

ガベージコレクションでもいずれ解放されますが、いつになるかは決まっていません。free() を呼んだ後にそのオブジェクトを使ったり、もう一度 free() を呼んだりすると Error になります。4 つのクラスは [Symbol.dispose]() も持っているので、using 宣言に対応した環境では、ブロックを抜けるときに自動で解放できます。

これ以外の関数が返す値(型付き配列や普通の object)は JavaScript 側の値なので、free() は要りません。

初期化

init

export default function init(
  module_or_path?: { module_or_path: InitInput | Promise<InitInput> },
): Promise<InitOutput>;

type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;

wasm を読み込んで、SDK を使える状態にします。ほかの関数より先に 1 回だけ await してください。2 回目以降の呼び出しは何もせず、1 回目と同じ結果を返します。

  • ブラウザやバンドラ(Vite など)では、引数を省いて await init() と書きます。パッケージの隣にある voxel_core_bg.wasm を new URL('voxel_core_bg.wasm', import.meta.url) で読み込みます。
  • wasm の置き場所を変えたときや Node で使うときは、module_or_path に URL・Response・バイト列・コンパイル済みの WebAssembly.Module を渡します。
  • { module_or_path } で包まずに直接渡す古い書き方も動きますが、コンソールに警告が出ます。
  • 戻り値の InitOutput は wasm の exports です。ふだんは使いません。低レベルの LOD で使います。

Node では、wasm のバイト列を読んで渡します。

import { readFileSync } from 'node:fs';
import init, { spatial_voxel_size_m } from '@mirgrid/web-sdk';

const wasmUrl = new URL('voxel_core_bg.wasm', import.meta.resolve('@mirgrid/web-sdk'));
await init({ module_or_path: readFileSync(wasmUrl) });
console.log(spatial_voxel_size_m(24));
1

initSync

export function initSync(module: { module: SyncInitInput }): InitOutput;

type SyncInitInput = BufferSource | WebAssembly.Module;

init と同じ初期化を、同期で行います。wasm のバイト列か WebAssembly.Module を、すでに手元に持っているときに使います。2 回目以降の呼び出しは何もしません。{ module } で包まずに直接渡すと、警告が出ます。

import { readFileSync } from 'node:fs';
import { initSync, spatial_voxel_size_m } from '@mirgrid/web-sdk';

const wasmUrl = new URL('voxel_core_bg.wasm', import.meta.resolve('@mirgrid/web-sdk'));
initSync({ module: readFileSync(wasmUrl) });
console.log(spatial_voxel_size_m(30));
0.015625

位置と空間アドレス

緯度経度・ECEF・ENU・平面直角座標系のあいだの変換と、セルの番号・空間アドレスの計算をする関数です。どれも @mirgrid/web-sdk から import します。

緯度経度から空間アドレスを求めて、元に戻す例です。

import init, {
  spatial_geodetic_to_ecef, spatial_ecef_to_voxel, spatial_hilbert_cellid_hex,
  spatial_hilbert_cellid_decode, spatial_cellid_to_ecef,
} from '@mirgrid/web-sdk';

await init();
const ecef = spatial_geodetic_to_ecef(139.7671, 35.6812, 40);
const [vx, vy, vz] = spatial_ecef_to_voxel(ecef[0], ecef[1], ecef[2], 24);
const id = spatial_hilbert_cellid_hex(vx, vy, vz, 24);
console.log(vx, vy, vz);
console.log(id);
console.log(spatial_hilbert_cellid_decode(id));
console.log(spatial_cellid_to_ecef(id));
4428917 11738705 12088148
0000000002e9380374bc9e7cc1940000
{ vx: 4428917, vy: 11738705, vz: 12088148, level: 24 }
Float64Array(3) [ -3959690.5078125, 3350097.4921875, 3699540.4921875 ]

spatial_voxel_size_m

function spatial_voxel_size_m(level: number): number;

レベル level のセルの一辺 [m] を返します。レベル 24 なら 1、レベル 30 なら 0.015625 です。level が 1〜30 の外なら Error を投げます。

spatial_geodetic_to_ecef

function spatial_geodetic_to_ecef(lon_deg: number, lat_deg: number, height: number): Float64Array;

経度・緯度 [度] と楕円体高 height [m] を、ECEF の [x, y, z] [m] に変換します。入力は検査しません。NaN を渡すと NaN を含む配列が返ります。

spatial_ecef_to_llh_rad

function spatial_ecef_to_llh_rad(x: number, y: number, z: number): { lon: number; lat: number };

ECEF [m] を経度・緯度に変換します。戻り値の単位はラジアンです。度にするには 180 / Math.PI を掛けます。高さは spatial_ecef_height で求めます。

spatial_ecef_height

function spatial_ecef_height(x: number, y: number, z: number): number;

ECEF [m] の点の楕円体高 [m] を返します。地表付近では誤差は 1 mm 未満です。

spatial_ecef_to_enu

function spatial_ecef_to_enu(
  x: number, y: number, z: number,
  origin_lon_deg: number, origin_lat_deg: number, origin_height: number,
): Float64Array;

ECEF の点 (x, y, z) [m] を、原点(経度 [度]・緯度 [度]・楕円体高 [m])から見た ENU の [east, north, up] [m] に変換します。画面に描くときは、ECEF のままではなく、この関数で手元の座標にしてから GPU に渡してください。

import init, { spatial_geodetic_to_ecef, spatial_ecef_to_enu } from '@mirgrid/web-sdk';

await init();
const p = spatial_geodetic_to_ecef(139.768, 35.6812, 40);
console.log(spatial_ecef_to_enu(p[0], p[1], p[2], 139.7671, 35.6812, 40));
Float64Array(3) [
  81.47317089525853,
  0.00037323128506372427,
  -0.0005197661007088072
]

spatial_enu_to_ecef

function spatial_enu_to_ecef(
  east: number, north: number, up: number,
  origin_lon_deg: number, origin_lat_deg: number, origin_height: number,
): Float64Array;

spatial_ecef_to_enu の逆です。原点から見た東・北・上 [m] を、ECEF の [x, y, z] [m] に変換します。

spatial_ecef_to_enu_many

function spatial_ecef_to_enu_many(
  ecef: Float64Array,
  origin_lon_deg: number, origin_lat_deg: number, origin_height: number,
): Float64Array;

spatial_ecef_to_enu を多くの点にまとめて行います。ecef は [x, y, z, x, y, z, …] と並べた配列で、戻り値も同じ並びの [e, n, u, …] です。数十万点でも 1 回の呼び出しで済みます。ecef の長さが 3 の倍数でなければ Error を投げます。

spatial_enu_to_ecef_many

function spatial_enu_to_ecef_many(
  enu: Float64Array,
  origin_lon_deg: number, origin_lat_deg: number, origin_height: number,
): Float64Array;

spatial_enu_to_ecef の一括版です。enu は [e, n, u, e, n, u, …] と並べます。長さが 3 の倍数でなければ Error を投げます。

spatial_ecef_to_voxel

function spatial_ecef_to_voxel(x: number, y: number, z: number, level: number): Float64Array;

ECEF の点 [m] を含む、レベル level のセルの番号 [vx, vy, vz] を返します。点がセルの境界にちょうど乗っているときの扱いも含めて、SDK のほかの関数と同じ規則で決まります。

level が 1〜30 の外なら Error を投げます。座標は検査しません。各軸の座標は有限で、絶対値が 8,388,608 m (2^23 m) 未満の値を渡してください。NaN や範囲の外の値を渡しても Error にはならず、意味の無い番号が返ります。

spatial_voxel_to_ecef

function spatial_voxel_to_ecef(vx: number, vy: number, vz: number, level: number): Float64Array;

セル (vx, vy, vz, level) の中心を、ECEF の [x, y, z] [m] で返します。level が 1〜30 の外のときや、番号が 0〜2^level − 1 の整数でないときは Error を投げます。

spatial_voxel_bounds

function spatial_voxel_bounds(vx: number, vy: number, vz: number, level: number): Float64Array;

セルが占める範囲を、ECEF の軸に沿った箱 [min_x, min_y, min_z, max_x, max_y, max_z] [m] で返します。min の面はセルに含まれ、max の面は含まれません。例外の条件は spatial_voxel_to_ecef と同じです。

spatial_hilbert_cellid_hex

function spatial_hilbert_cellid_hex(vx: number, vy: number, vz: number, level: number): string;

セルの番号から、地球基準の格子の空間アドレス(32 桁の 16 進)を作ります。空間アドレスは、セルを 3 次元のヒルベルト曲線の順に並べた番号です。近いセルほど似た値になりやすくなります。spatial_ecef_to_voxel が返した番号を、そのまま渡せます。例外の条件は spatial_voxel_to_ecef と同じです。

spatial_hilbert_cellid_decode

function spatial_hilbert_cellid_decode(cell_hex: string): { vx: number; vy: number; vz: number; level: number };

空間アドレスを、セルの番号とレベルに戻します。英字の大文字と小文字は区別しません。32 桁でない文字列や 16 進でない文字を含む文字列を渡すと、Error を投げます。

spatial_cellid_to_ecef

function spatial_cellid_to_ecef(cell_hex: string): Float64Array;

空間アドレスが表すセルの中心を、ECEF の [x, y, z] [m] で返します。空間アドレスが壊れていれば Error を投げます。

spatial_plane_rect_to_ecef

function spatial_plane_rect_to_ecef(easting: number, northing: number, height: number, epsg: number): Float64Array;

平面直角座標系の点(easting・northing [m] と楕円体高 height [m])を、ECEF の [x, y, z] [m] に変換します。epsg は 6669〜6687 のどれかで、ほかの値なら Error を投げます。標高しか分からないときは、ジオイド高を足して楕円体高にしてから渡してください。

spatial_ecef_to_plane_rect

function spatial_ecef_to_plane_rect(x: number, y: number, z: number, epsg: number): Float64Array;

ECEF [m] を平面直角座標系の [easting, northing, height] に変換します。height は楕円体高 [m] です。epsg が 6669〜6687 の外なら Error を投げます。

spatial_llh_to_plane_rect

function spatial_llh_to_plane_rect(lon_deg: number, lat_deg: number, epsg: number): Float64Array;

経度・緯度 [度] を、平面直角座標系の [easting, northing] [m] に変換します。高さや ECEF を経由しない、直接の変換です。epsg が 6669〜6687 の外なら Error を投げます。

import init, { spatial_llh_to_plane_rect, spatial_plane_rect_to_llh_deg } from '@mirgrid/web-sdk';

await init();
const [easting, northing] = spatial_llh_to_plane_rect(139.7671, 35.6812, 6677);
console.log(easting, northing);
console.log(spatial_plane_rect_to_llh_deg(easting, northing, 6677));
-5995.185165976924 -35367.23013254245
Float64Array(2) [ 139.76710000000006, 35.6811999999441 ]

spatial_llh_to_plane_rect_many

function spatial_llh_to_plane_rect_many(lonlat: Float64Array, epsg: number): Float64Array;

spatial_llh_to_plane_rect の一括版です。lonlat は [lon, lat, lon, lat, …] と並べ、戻り値は [easting, northing, …] です。矩形の 4 隅のように、いくつかの点をまとめて変換するときに使います。長さが 2 の倍数でないときや、epsg が 6669〜6687 の外のときは Error を投げます。

spatial_plane_rect_to_llh_deg

function spatial_plane_rect_to_llh_deg(easting: number, northing: number, epsg: number): Float64Array;

spatial_llh_to_plane_rect の逆です。平面直角座標系の点 [m] を、[lon_deg, lat_deg] [度] に変換します。epsg が 6669〜6687 の外なら Error を投げます。

spatial_plane_rect_scale_factor

function spatial_plane_rect_scale_factor(easting: number, northing: number, epsg: number): number;

平面直角座標系のその点での縮尺係数 k を返します。平面上の距離 = 実際の距離 × k の関係です。epsg が 6669〜6687 の外なら Error を投げます。

spatial_yaw_from_deg

function spatial_yaw_from_deg(deg: number): number;

現場基準の格子の向き [度](真北から東回り)を、格子の記述子に書く yaw の値(0〜65535 の整数。1 周 = 65536)に換算します。端数は偶数への丸めで整数にし、範囲の外の角度は 1 周ぶん回して収めます。戻り値は FrameJs.site と VoxelizerJs.siteAuto に渡します。deg が有限でなければ Error を投げます。

import init, { spatial_yaw_from_deg } from '@mirgrid/web-sdk';

await init();
console.log(spatial_yaw_from_deg(90), spatial_yaw_from_deg(-90), spatial_yaw_from_deg(360));
16384 49152 0

セルの集合

セルの集合を作り、和・積・差や範囲での切り出しをする API です。どれも @mirgrid/web-sdk から import します。

CellSetJs

class CellSetJs {
  constructor(level: number);
  static from_indices(level: number, flat: Float64Array): CellSetJs;
  static from_ecef_points(level: number, flat: Float64Array): CellSetJs;

  readonly level: number;
  readonly size: number;

  indices(): Float64Array;
  contains(vx: number, vy: number, vz: number): boolean;
  cell_ids(): string[];
  bounds_ecef(): Float64Array;
  centers_enu(lon_deg: number, lat_deg: number, h: number): Float32Array;
  area_m2(): number;
  distance_m(other: CellSetJs, max_steps: number): number | null;

  union(other: CellSetJs): CellSetJs;
  intersect(other: CellSetJs): CellSetJs;
  difference(other: CellSetJs): CellSetJs;
  shared_with(other: CellSetJs, tolerance: number): CellSetJs;
  downshift(to_level: number): CellSetJs;
  clip(roi: Roi, mode: 'touching' | 'inside'): CellSetJs;

  free(): void;
  [Symbol.dispose](): void;
}

同じレベルのセルを集めた集合です。同じセルは 1 つにまとまり、入れた順番を保ちます。データは wasm のメモリにあるので、使い終わったら free() を呼んでください。集合を返すメソッドは、どれも新しい CellSetJs を返します。元の集合は変わりません。

コンストラクタと from_indices・from_ecef_points で作る集合は、地球基準の格子のセルです。現場基準の格子の集合は VoxelizerJs.toCellSet で作ります。

import init, { CellSetJs } from '@mirgrid/web-sdk';

await init();
const a = CellSetJs.from_indices(24, Float64Array.of(4428917, 11738705, 12088148, 4428918, 11738705, 12088148));
const b = CellSetJs.from_indices(24, Float64Array.of(4428918, 11738705, 12088148, 4428919, 11738705, 12088148));
const shared = a.intersect(b);
console.log(a.size, shared.size, shared.indices());
console.log(a.area_m2(), a.distance_m(b, 10));
console.log(a.cell_ids());
a.free();
b.free();
shared.free();
2 1 Float64Array(3) [ 4428918, 11738705, 12088148 ]
10 0
[
  '0000000002e9380374bc9e7cc1940000',
  '0000000002e9380374bc9e7cc06c0000'
]

constructor

new CellSetJs(level) は、レベル level の空の集合を作ります。level が 1〜30 の外なら Error を投げます。

from_indices

CellSetJs.from_indices(level, flat) は、[vx, vy, vz, vx, vy, vz, …] と並べた番号から集合を作ります。同じセルが何度出てきても、最初の 1 つだけが残ります。flat の長さが 3 の倍数でないときや、番号が 0〜2^level − 1 の整数でないときは Error を投げます。

from_ecef_points

CellSetJs.from_ecef_points(level, flat) は、[x, y, z, …] と並べた ECEF の点 [m] から、それぞれの点を含むセルの集合を作ります。セルの決め方は spatial_ecef_to_voxel と同じです。長さが 3 の倍数でなければ Error を投げます。座標は検査しないので、NaN や範囲の外の点を入れないでください。

level と size

level は集合のレベル、size はセルの数です。どちらも読み取り専用のプロパティです。

indices

入れた順のセルの番号を、[vx, vy, vz, …] と並べた Float64Array で返します。

contains

contains(vx, vy, vz) は、そのセルが集合に入っていれば true を返します。番号が 0〜2^level − 1 の整数でなければ Error を投げます。

cell_ids

入れた順のセルの空間アドレス(32 桁の 16 進)を、文字列の配列で返します。

bounds_ecef

すべてのセルを囲む、ECEF の軸に沿った箱 [min_x, min_y, min_z, max_x, max_y, max_z] [m] を返します。空の集合では 6 つとも NaN です。

centers_enu

centers_enu(lon_deg, lat_deg, h) は、セルの中心を原点(経度・緯度 [度]・楕円体高 [m])から見た ENU [m] にして、[e, n, u, …] の Float32Array で返します。そのまま GPU に渡せる形です。

area_m2

集合の表面積 [m²] を返します。隣り合うセルどうしが接している面は数えません。空の集合では 0 です。

distance_m

distance_m(other, max_steps) は、2 つの集合のすき間の距離 [m] を返します。セル単位で測るので、結果はセルの一辺の整数倍です。

  • 重なっているか、すき間なく接していれば 0 です。
  • max_steps セルぶん広げても届かないときや、どちらかが空の集合のときは null です。0 とは意味が違うので、区別してください。
  • 2 つの集合のレベルが違うときや、max_steps が負のときは Error を投げます。

union・intersect・difference

union(other) は和、intersect(other) は積(両方にあるセル)、difference(other) は差(自分にあって other に無いセル)を返します。2 つの集合のレベルが違うと Error を投げます。先に downshift でレベルを揃えてください。

shared_with

shared_with(other, tolerance) は、自分のセルのうち、前後・左右・上下に ±tolerance セルの立方体の範囲に other のセルがあるものを返します。tolerance は 0〜8 の整数です。範囲の外の値やレベルの違う集合を渡すと、Error を投げます。

downshift

downshift(to_level) は、レベル to_level に粗くした集合を返します。それぞれのセルは、それを含む粗いセルに置き換わり、同じセルは 1 つにまとまります。to_level が今のレベルより細かいときは、今のレベルのまま写しを返します。to_level が 1〜30 の外なら Error を投げます。

clip

clip(roi, mode) は、ROI(関心のある範囲)に入るセルだけを残した集合を返します。ROI の書き方は ROI の書き方 を見てください。

  • mode が 'touching' なら、ROI と少しでも重なるセルを残します。
  • mode が 'inside' なら、全体が ROI に入るセルだけを残します。

ROI の形が正しくないときや、mode がこの 2 つのどちらでもないときは Error を投げます。

現場基準の格子の集合で使えるもの

VoxelizerJs.toCellSet で作った現場基準の格子の集合では、使えるメソッドが限られます。

  • 使えるもの: level・size・indices・contains・area_m2・downshift、それに同じ格子の集合どうしの union・intersect・difference・shared_with・distance_m と ops_diff3。
  • 使えないもの: cell_ids・bounds_ecef・centers_enu は、番号を地球基準の格子のものとして計算するので、正しい値になりません。clip と、地球基準の格子の集合との演算は、unreachable という Error になります。

現場基準の格子のセルの空間アドレスと中心は、VoxelizerJs.cellInfo か FrameJs.cellAt で求めてください。

ROI の書き方

CellSetJs.clip・ops_cells_in_roi・ops_cell_relation は、範囲を ROI という object で受け取ります。座標はすべて ECEF [m] です。ROI は、次の 6 種類を入れ子にして組み立てます。

書き方 意味
{ aabb: { min: [x, y, z], max: [x, y, z] } } ECEF の軸に沿った箱(境界を含む)
{ sphere: { center: [x, y, z], radius_m: r } } 中心 center、半径 r [m] の球(境界を含む)
{ slab: { origin: [x, y, z], normal: [x, y, z], half_thickness_m: t } } origin を通り normal に垂直な平面から、両側に t [m] までの板
{ not: roi } roi の外側
{ and: [roi, …] } すべての roi に入る範囲
{ or: [roi, …] } どれかの roi に入る範囲
  • normal は長さ 1 でなくてもかまいません。長さが 0 なら Error になります。
  • radius_m や half_thickness_m が負なら、空の範囲として扱います。
  • 中身の無い { and: [] } は空間全体、{ or: [] } は空の範囲です。

ops_cells_in_roi は、範囲を囲む有限の箱が求まる ROI だけを受け付けます。slab だけの ROI や not は使えません。and には有界な子を 1 つ以上入れ、or の子はすべて有界にしてください。

次の例は、半径 5 m の球と、厚さ 1 m の水平な板が重なる範囲のセルを集めます。板の向きは、その地点の真上の向き(ECEF で表したもの)にしています。

import init, { ops_cells_in_roi, spatial_geodetic_to_ecef } from '@mirgrid/web-sdk';

await init();
const c = spatial_geodetic_to_ecef(139.7671, 35.6812, 40);
const above = spatial_geodetic_to_ecef(139.7671, 35.6812, 41);
const up = [above[0] - c[0], above[1] - c[1], above[2] - c[2]];
const roi = {
  and: [
    { sphere: { center: Array.from(c), radius_m: 5 } },
    { slab: { origin: Array.from(c), normal: up, half_thickness_m: 0.5 } },
  ],
};
const cells = ops_cells_in_roi(roi, 24, 10000);
console.log(cells.size);
cells.free();
280

ops_cells_in_roi

function ops_cells_in_roi(roi: Roi, level: number, max_cells: number): CellSetJs;

レベル level のセルのうち、ROI と少しでも重なるものをすべて集めます。セルは番号の小さい順(vx、vy、vz の順に比べる)に並びます。戻り値は CellSetJs なので、使い終わったら free() を呼んでください。

max_cells は、調べるセルの数の上限です。調べ始める前に数を見積もり、上限を超えるなら Error を投げます。ほかに、ROI を囲む有限の箱が求まらないとき(slab だけ、not など)と、level が 1〜30 の外のときも Error を投げます。

import init, { ops_cells_in_roi, ops_cell_relation, spatial_geodetic_to_ecef } from '@mirgrid/web-sdk';

await init();
const center = Array.from(spatial_geodetic_to_ecef(139.7671, 35.6812, 40));
const roi = { sphere: { center, radius_m: 1.5 } };
const cells = ops_cells_in_roi(roi, 24, 1000);
console.log(cells.size);
console.log(ops_cell_relation(roi, 4428917, 11738705, 12088148, 24));
cells.free();
45
inside

ops_cell_relation

function ops_cell_relation(roi: Roi, vx: number, vy: number, vz: number, level: number): 'outside' | 'intersects' | 'inside';

1 つのセルと ROI の関係を返します。

  • 'outside': セルは ROI と重なりません。
  • 'intersects': セルの一部が ROI に入ります。
  • 'inside': セル全体が ROI に入ります。

and・or・not を組み合わせた ROI では、判定は安全側に倒します。本当は外にあるセルでも 'intersects' になることがありますが、'outside' のセルが ROI に入ることはありません。ROI の形が正しくないとき、level が 1〜30 の外のとき、番号が 0〜2^level − 1 の整数でないときは Error を投げます。

ops_diff3

function ops_diff3(before: CellSetJs, after: CellSetJs, compare_level: number, tolerance: number): {
  level: number;
  removed: Float64Array;
  added: Float64Array;
  unchanged: Float64Array;
  removedCount: number;
  addedCount: number;
  unchangedCount: number;
};

2 つの時点の集合を比べて、セルを「無くなった」「増えた」「変わらない」の 3 つに分けます。

  • 両方の集合をレベル compare_level に粗くしてから比べます。compare_level は、どちらの集合のレベルより細かくしてはいけません。細かいと Error を投げます。
  • tolerance(0〜8 の整数)は、ずれを許すセルの数です。before のセルの ±tolerance セルの範囲に after のセルがあれば、「変わらない」と数えます。同じ位置のセルどうしを先に対応させ、1 つの after のセルは 1 回しか使いません。範囲の外の値なら Error を投げます。
  • removed・unchanged は before 側のセル、added は after 側のセルで、どれも [vx, vy, vz, …] の並びです。level は compare_level と同じ値です。

戻り値は普通の object なので free() は要りません。before と after は呼び出しの後もそのまま使えます。

import init, { CellSetJs, ops_diff3 } from '@mirgrid/web-sdk';

await init();
const before = CellSetJs.from_indices(24, Float64Array.of(100, 200, 300, 101, 200, 300));
const after = CellSetJs.from_indices(24, Float64Array.of(101, 200, 300, 102, 200, 300));
console.log(ops_diff3(before, after, 24, 0));
console.log(ops_diff3(before, after, 24, 2).unchangedCount);
before.free();
after.free();
{
  level: 24,
  removed: Float64Array(3) [ 100, 200, 300 ],
  added: Float64Array(3) [ 102, 200, 300 ],
  unchanged: Float64Array(3) [ 101, 200, 300 ],
  removedCount: 1,
  addedCount: 1,
  unchangedCount: 1
}
2

ops_extrude_polygon_to_cells

function ops_extrude_polygon_to_cells(
  level: number,
  origin_lon_deg: number, origin_lat_deg: number, origin_h_m: number,
  polygon_flat: Float64Array,
  min_height_m: number, max_height_m: number,
  max_cells: number,
  normal_axis: 'east' | 'north' | 'up',
): CellSetJs;

床や壁の多角形を、ENU の 1 つの軸の向きに押し出した立体を、地球基準の格子のセルの集合にします。中心がその立体に入るセルを集めます。そのため、多角形の縁にセルの半分より少ししか掛からないところは、入らないことがあります。

  • origin_* は ENU の原点(経度・緯度 [度]・楕円体高 [m])です。
  • normal_axis は押し出す向きです。
  • polygon_flat は多角形の頂点を [a, b, a, b, …] と並べた配列です。a・b は、normal_axis を除いた ENU の 2 軸を東・北・上の順に並べたものです。'up' なら(東, 北)、'north' なら(東, 上)、'east' なら(北, 上)です。単位は m です。
  • min_height_m・max_height_m は、押し出す範囲を normal_axis の向きの座標 [m] で表したものです。
  • max_cells は、調べるセルの数の上限です。

次のときは Error を投げます。

  • 頂点が 3 つ未満のときや、面積がほぼ 0 のとき。
  • min_height_m > max_height_m のときや、値が有限でないとき。
  • normal_axis が 3 つのどれでもないとき。
  • 調べるセルの数が max_cells を超えるとき。
  • level が 1〜30 の外のとき。

creation.mjs で作った形から、この関数の引数を作れます。

import init, { ops_extrude_polygon_to_cells } from '@mirgrid/web-sdk';

await init();
const cells = ops_extrude_polygon_to_cells(
  24, 139.7671, 35.6812, 40,
  Float64Array.of(0, 0, 4, 0, 4, 3, 0, 3),
  0, 2.4, 10000, 'up',
);
console.log(cells.size, cells.level);
cells.free();
26 24

voxel 化

点群やメッシュをセルにする API と、格子を扱う API です。どれも @mirgrid/web-sdk から import します。

VoxelizerJs

class VoxelizerJs {
  constructor(level: number, frame?: FrameJs | object | null);
  static siteAuto(level: number, datum?: string | null, yaw?: number | null): VoxelizerJs;

  readonly level: number;
  readonly cellCount: number;

  setInput(spec: VoxelizeInputSpec): void;
  setSurfaceDensity(points_per_m2: number): void;
  setMaxCells(max?: number | null): void;
  pushPoints(xyz: Float64Array, rgb?: Uint8Array | null): void;
  pushTriangles(xyz: Float64Array, indices?: Uint32Array | null, rgb?: Uint8Array | null): number;
  pushFile(bytes: Uint8Array, format: string): VoxelizeReadSummary;

  frameDescriptor(): object | null;
  indices(): Float64Array;
  colors(): Uint8Array;
  counts(): Float64Array;
  cellInfo(i: number): VoxelCellInfo;
  toCellSet(): CellSetJs;
  toPacket(): Uint8Array;
  faces(origin?: [number, number, number] | null, options?: VoxelFacesOptions | null): VoxelFaces;

  free(): void;
  [Symbol.dispose](): void;
}

点・三角形・ファイルを積んで、セルにまとめます。セルの色は、そのセルに入った点の色の平均です。レベルと格子は作るときに決まり、後から変えられません。使い終わったら free() を呼んでください。

結果を読むメソッド(indices・colors・counts・cellInfo・faces など)は、セルを番号の小さい順(vx、vy、vz の順に比べる)に並べて返します。この並びでの位置を「セル番号」と呼びます。

import init, { VoxelizerJs } from '@mirgrid/web-sdk';

await init();
const voxelizer = new VoxelizerJs(24);
voxelizer.setInput({ coordSystem: 'latlon' });
voxelizer.pushPoints(
  Float64Array.of(139.7671, 35.6812, 40, 139.76711, 35.6812, 40, 139.7672, 35.6812, 40),
  Uint8Array.of(255, 0, 0, 0, 0, 255, 0, 255, 0),
);
console.log(voxelizer.cellCount);
console.log(voxelizer.cellInfo(0));
const faces = voxelizer.faces();
console.log(faces.faceCount, faces.originLlh);
voxelizer.free();
3
{
  index: [ 4428911, 11738698, 12088148 ],
  cellId: '0000000002e9380374bc9e7d880c0000',
  rgb: [ 0, 255, 0 ],
  count: 1,
  centerEcef: [ -3959696.5078125, 3350090.4921875, 3699540.4921875 ],
  sourceType: 2
}
18 [ 139.76714888785557, 35.68120374573367, 40.05131894722581 ]

constructor

new VoxelizerJs(level, frame?) は、レベル level(1〜30)の voxel 化を始めます。frame には FrameJs か格子の記述子を渡します。省くと地球基準の格子になります。入力の座標系は、最初は ECEF です。level が範囲の外のときや、記述子が正しくないときは Error を投げます。

渡した FrameJs は中身を読むだけなので、呼び出しの後も使えます。

siteAuto

VoxelizerJs.siteAuto(level, datum?, yaw?) は、現場基準の格子で voxel 化を始めます。格子の anchor は、最初に積んだ点を含むレベル 16 のセルに決まります。格子の軸はその地点の東・北・上なので、セルが現場で水平な立方体になります。

  • datum は測地系の名前で、既定は 'JGD2011' です。知らない名前なら Error を投げます。
  • yaw は格子の向き(0〜65535 の整数)で、既定は 0(格子が東西南北に揃う)です。度からは spatial_yaw_from_deg で換算します。
  • 積む点は、anchor の中心から各軸 2048 m 未満に収めてください。外の点を積むと Error を投げます。

決まった格子は frameDescriptor で読めます。同じ格子に別のファイルを重ねるときは、その記述子を new VoxelizerJs(level, descriptor) に渡してください。

setInput

setInput(spec) は、この後に積む点・三角形・ファイルの座標系と置き場所を決めます。すでに積んだセルは変わりません。spec の形は VoxelizeInputSpec です。知らないキーがあるとき、必要な値が足りないとき、値が正しくないときは Error を投げます。

setSurfaceDensity

setSurfaceDensity(points_per_m2) は、三角形の表面から点を拾う密度の下限 [点/m²] を決めます。既定は 16 です。実際の密度は、レベルに合わせて必要なだけ上がります。0 以下か有限でない値なら Error を投げます。

setMaxCells

setMaxCells(max) は、セルの数の上限を決めます。null か 0 なら上限はありません(既定)。積んでいる途中でセルの数が上限を超えると、push* が Error を投げます。0 以上の整数でも null でもなければ Error を投げます。

pushPoints

pushPoints(xyz, rgb?) は、点を積みます。xyz は入力の座標系での [x, y, z, …] です。rgb は 1 点 3 バイトの色で、省くと灰色 (180, 180, 180) になります。

次のときは Error を投げます。

  • xyz の長さが 3 の倍数でないとき。
  • rgb の長さが、点の数 × 3 でないとき。
  • 変換した ECEF 座標が有限でないか、範囲の外のとき。
  • セルの数が上限を超えたとき。

pushTriangles

pushTriangles(xyz, indices?, rgb?) は、三角形を積みます。三角形の表面から点を拾い、その点を積みます。戻り値は拾った点の数です。

  • xyz は頂点の座標 [x, y, z, …] です。
  • indices は 1 三角形 3 個の頂点番号です。省くと、頂点を 3 個ずつ使って三角形にします。
  • rgb は 1 三角形 3 バイトの色です。省くと (180, 190, 205) になります。

配列の長さが合わないとき、indices に頂点の数以上の番号があるとき、そのほか pushPoints と同じ条件で Error を投げます。

pushFile

pushFile(bytes, format) は、ファイルのバイト列を読んで積みます。戻り値は VoxelizeReadSummary です。

  • 対応する形式は PLY・LAS・LAZ・glTF・GLB・OBJ・STL です。format には形式名('laz' など)かファイル名('scan.laz' など)を渡します。
  • PLY は binary_little_endian だけに対応しています。ASCII の PLY は読めません。
  • glTF は、外部のファイルを参照しないもの(GLB か、data: URI に埋め込んだもの)だけを読めます。
  • LAS・LAZ のヘッダに EPSG が書いてあっても、入力の座標系は切り替わりません。座標系は setInput で決めてください。

対応していない形式や壊れたファイルのとき、そのほか pushPoints と同じ条件で Error を投げます。ファイル全体を一度に wasm のメモリへ写すので、大きなファイルは Web Worker の中で読むと、画面が止まりません。

level と cellCount

level はレベル、cellCount は今のセルの数です。どちらも読み取り専用のプロパティです。

frameDescriptor

今の格子の記述子を返します(格子の種類 を参照)。siteAuto でまだ点を積んでいないときは、格子が決まっていないので null を返します。

indices, colors, counts

indices() はセルの番号 [vx, vy, vz, …]、colors() はセルの色(1 セル 3 バイト)、counts() はセルに入った点の数を返します。どれもセル番号の順です。現場基準の格子では、番号は現場基準の格子のものです。

cellInfo

cellInfo(i) は、セル番号 i のセルの情報を VoxelCellInfo で返します。i が 0〜cellCount − 1 の整数でないときや、セルがまだ無いときは Error を投げます。

toCellSet

セルを CellSetJs にして返します。格子はこの voxel 化と同じです。使い終わったら free() を呼んでください。現場基準の格子の集合で使えるメソッドには制限があります(現場基準の格子の集合で使えるもの を参照)。

toPacket

セルを配信データ (packet) のバイト列にして返します。セルの色と、メッシュ・点群のどちら由来かを保ちます。面の向きの情報はすべての向き (63) になります。セルが無ければ Error を投げます。

packet には格子の情報が入りません。現場基準の格子で作った packet を後で読むときのために、frameDescriptor も一緒に保存してください。

faces

faces(origin?, options?) は、セルの露出面(ほかのセルに隠れていない面)を VoxelFaces で返します。結果は JavaScript 側の型付き配列だけでできているので、Web Worker から transfer できます。free() は要りません。

  • origin は描画空間の原点 [lon, lat, h](度・楕円体高 m)です。省くと、地球基準の格子ではセルを囲む箱の中心、現場基準の格子では anchor の真上(anchor の中心の経度・緯度と、セルを囲む箱の中心の高さ)になります。
  • 1 つの描画器に重ねるレイヤは、同じ原点で作ってください。2 つ目からは faces(renderer.originLlh) と書きます。
  • options は VoxelFacesOptions です。

次のときは Error を投げます。

  • セルが無いとき。
  • origin が有限でないか、緯度が −90〜90 の外のとき。
  • options に知らないキーがあるとき。
  • 最初のセルから各軸 2^23 セル以上離れたセルがあるとき(レベル 24 で 8,388,608 m)。広い範囲は、レベルを粗くするか、分けて作ってください。

FrameJs

class FrameJs {
  static ecef(): FrameJs;
  static site(datum: string, anchor_hex: string, yaw: number): FrameJs;
  static fromDescriptor(descriptor: object): FrameJs;

  kind(): 'ecef' | 'site';
  toDescriptor(): object;
  matrix(): Float64Array;
  cellAt(ix: number, iy: number, iz: number, level: number): { cellId: string; centerEcef: Float64Array };

  free(): void;
  [Symbol.dispose](): void;
}

セルを切る格子を表します(格子の種類 を参照)。VoxelizerJs・voxel_faces_from_packet・decode_voxel_packet_in_frame に渡して使います。使い終わったら free() を呼んでください。

  • FrameJs.ecef() は、地球基準の格子を返します。
  • FrameJs.site(datum, anchor_hex, yaw) は、現場基準の格子を返します。anchor_hex はレベル 16 のセルの空間アドレスです。yaw は 0〜65535 の整数です。度の値は spatial_yaw_from_deg で換算してから渡してください。datum が知らない名前のときや、anchor_hex がレベル 16 の空間アドレスでないときは Error を投げます。
  • FrameJs.fromDescriptor(descriptor) は、記述子から格子を作ります。必要なキーが無いとき、型が合わないとき、epoch が測地系の値と違うときは Error を投げます。
  • kind() は 'ecef' か 'site' を返します。
  • toDescriptor() は記述子を返します。地球基準の格子では { kind: 'ecef' } です。
  • matrix() は、格子の座標から ECEF [m] への 4×4 の変換行列を、列優先の 16 要素で返します。[0..3] が東の列、[4..7] が北の列、[8..11] が上の列、[12..15] が平行移動です。three.js の Matrix4.fromArray にそのまま渡せます。地球基準の格子では単位行列です。
  • cellAt(ix, iy, iz, level) は、この格子のセルの空間アドレスと、中心の ECEF [m] を返します。現場基準の格子では、空間アドレスは現場基準の格子のものになります。level が 1〜30 の外のときや、番号が 0〜2^level − 1 の整数でないときは Error を投げます。
import init, {
  FrameJs, spatial_geodetic_to_ecef, spatial_ecef_to_voxel, spatial_hilbert_cellid_hex,
} from '@mirgrid/web-sdk';

await init();
const c = spatial_geodetic_to_ecef(139.7671, 35.6812, 40);
const [ax, ay, az] = spatial_ecef_to_voxel(c[0], c[1], c[2], 16);
const frame = FrameJs.site('JGD2011', spatial_hilbert_cellid_hex(ax, ay, az, 16), 0);
console.log(frame.kind());
console.log(frame.toDescriptor());
frame.free();
site
{
  kind: 'site',
  datum: 'JGD2011',
  epoch: 2011.4,
  anchor: '0000000002e9380374bc9c0000000000',
  yaw: 0
}

voxel_faces_from_packet

function voxel_faces_from_packet(
  bytes: Uint8Array,
  xor_key: number,
  frame?: FrameJs | object | null,
  origin?: [number, number, number] | null,
): VoxelFaces;

配信データ (packet) を読んで、露出面を VoxelFaces で返します。色は packet のセルの色で、面の向きの情報を使って陰影を付けます。free() は要りません。

  • xor_key は packet を隠すときに使った 1 バイトの鍵(0〜255)です。隠していなければ 0 です。
  • frame は、packet をどの格子のものとして読むかです。FrameJs か記述子を渡します。省くと地球基準の格子です。packet には格子の情報が入っていないので、現場基準の格子の packet では必ず渡してください。渡さないと、別の場所のセルとして読まれます。
  • origin は描画空間の原点 [lon, lat, h] です。省いたときの決まり方は faces と同じです。

faces.cells の値は、packet の中のセルの順番です。packet が壊れているとき、セルが無いとき、そのほか faces と同じ条件で Error を投げます。

import init, { VoxelizerJs, voxel_faces_from_packet } from '@mirgrid/web-sdk';

await init();
const voxelizer = VoxelizerJs.siteAuto(26);
voxelizer.setInput({ coordSystem: 'local', origin: [139.7671, 35.6812, 40] });
voxelizer.pushPoints(Float64Array.of(0, 0, 0, 1, 0, 0, 0, 1, 0));
const descriptor = voxelizer.frameDescriptor();
const packet = voxelizer.toPacket();
console.log(descriptor);
const faces = voxel_faces_from_packet(packet, 0, descriptor);
console.log(faces.frameKind, faces.cellCount, faces.faceCount);
voxelizer.free();
{
  kind: 'site',
  datum: 'JGD2011',
  epoch: 2011.4,
  anchor: '0000000002e9380374bc9c0000000000',
  yaw: 0
}
site 3 18

VoxelizeInputSpec

interface VoxelizeInputSpec {
  coordSystem: string;
  origin?: [number, number, number] | null;
  utmZone?: number | null;
  utmHemisphere?: 'north' | 'south' | null;
  unitScale?: number | null;
  geoidHeightM?: number | null;
  rotationDeg?: number | null;
  translation?: [number, number, number] | null;
  pivot?: [number, number, number] | null;
}

setInput に渡す、入力の座標系と置き場所です。

キー 意味
coordSystem 入力の座標系。下の表のどれか(必須)
origin 'local' の原点 [lon, lat, h](度・楕円体高 m)
utmZone 'utm' のゾーン番号(1〜60)
utmHemisphere 'utm' の半球('north' か 'south')
unitScale 入力の 1 単位が何 m か。mm なら 0.001。既定 1
geoidHeightM 入力の高さが標高のときのジオイド高 [m]。楕円体高 = 標高 + この値。既定 0
rotationDeg 鉛直軸まわりの回転 [度]。正の値で反時計回り。既定 0
translation 平行移動 [東, 北, 上] [m]。既定 [0, 0, 0]
pivot 回転の中心。入力の単位で書く。既定 [0, 0, 0]

coordSystem に書ける値は次のとおりです。

値 入力の座標
'ecef' ECEF [x, y, z] [m](最初はこれ)
'latlon' [経度, 緯度, 楕円体高](度・m)
'utm' UTM の [easting, northing, 楕円体高] [m]。utmZone と utmHemisphere が必要
'local' origin から見た ENU [東, 北, 上] [m]。origin が必要
'6669'〜'6687' 平面直角座標系の [easting, northing, 楕円体高] [m]

入力の点には、回転(pivot まわり)、平行移動、unitScale の倍率の順に掛けてから、座標系に沿って ECEF に変換します。回転と平行移動は、ローカル座標や平面直角座標系のように、軸が東・北・上に近い座標系で使うものです。

VoxelizeReadSummary

interface VoxelizeReadSummary {
  format: 'ply' | 'las' | 'laz' | 'gltf' | 'glb' | 'obj' | 'stl';
  points: number;
  triangles: number;
  samples: number;
  declaredEpsg: number | null;
}

pushFile の結果です。format は読んだ形式、points は点の数、triangles は三角形の数、samples は三角形の表面から拾った点の数です。declaredEpsg は、LAS・LAZ のヘッダに書いてある EPSG です。書いていなければ null です。

VoxelCellInfo

interface VoxelCellInfo {
  index: [number, number, number];
  cellId: string;
  rgb: [number, number, number];
  count: number;
  centerEcef: [number, number, number];
  sourceType: number;
}

cellInfo が返す、1 つのセルの情報です。

キー 意味
index セルの番号。現場基準の格子では、その格子の番号
cellId 空間アドレス(32 桁の 16 進)。現場基準の格子では、その格子の空間アドレス
rgb セルの色(入った点の色の平均)
count セルに入った点の数(三角形から拾った点を含む)
centerEcef セルの中心の ECEF [m]
sourceType 由来。1 = メッシュ、2 = 点群、3 = 両方

VoxelFaces

interface VoxelFaces {
  level: number;
  cellSizeM: number;
  cellCount: number;
  faceCount: number;
  sorted: boolean;
  start: Uint32Array;
  coords: Int16Array | Float32Array;
  colors: Uint8Array;
  cells: Uint32Array;
  gridToRender: Float64Array;
  index0: [number, number, number];
  frameKind: 'ecef' | 'site';
  originLlh: [number, number, number];
  boundsRender: [number, number, number, number, number, number];
}

faces と voxel_faces_from_packet が返す露出面です。そのまま setLayer に渡して描けます。すべて JavaScript 側の値なので、free() は要りません。

キー 意味
level・cellSizeM レベルと、セルの一辺 [m]
cellCount・faceCount セルの数と面の数
coords 1 面 4 要素。格子の座標 x, y, z(最初のセルからのセル数。整数がセルの中心)と、面の向き d(0〜5 = +X, −X, +Y, −Y, +Z, −Z)
sorted true なら coords は Int16Array で、面が向きごとにまとまっています。false なら Float32Array です
start 7 要素。向き d の面は start[d] 以上 start[d + 1] 未満の番号です
colors 1 面 4 バイト。陰影を付けた r, g, b と 255
cells 面ごとのセル番号。faces では cellInfo の引数、voxel_faces_from_packet では packet の中の順番
gridToRender 格子の座標から描画空間(originLlh から見た ENU [m])への 4×4 の行列。列優先
index0 格子の座標が 0 のセル(最初のセル)の番号
frameKind 格子の種類
originLlh 描画空間の原点 [lon, lat, h]
boundsRender 描画空間でセルを囲む箱 [minE, minN, minU, maxE, maxN, maxU] [m]

面の格子の座標からセルの番号を求めるには、index0 に格子の座標を足します。その番号を FrameJs.cellAt に渡すと、空間アドレスが分かります。

VoxelFacesOptions

interface VoxelFacesOptions {
  occlusion?: boolean;
}

faces の選択肢です。occlusion を false にすると、隣のセルに塞がれた面も残します。既定は true です。

配信データ

配信データ (packet) は、セルの番号・色・由来・面の向き・地物の ID をまとめたバイト列です。1 つの packet には 1 つのレベルのセルが入ります。ここの関数はどれも @mirgrid/web-sdk から import します。

import init, { encode_voxel_packet, packet_metadata, decode_voxel_packet } from '@mirgrid/web-sdk';

await init();
const packet = encode_voxel_packet([
  { vx: 4428917, vy: 11738705, vz: 12088148, r: 255, g: 0, b: 0 },
  { vx: 4428918, vy: 11738705, vz: 12088148, r: 0, g: 0, b: 255, sourceType: 2, featureIdPrimary: 7 },
], 24);
console.log(packet_metadata(packet, 0));
const decoded = decode_voxel_packet(packet, 0, 139.7671, 35.6812, 40);
console.log(decoded.count, decoded.positions);
console.log(decoded.colors, decoded.feature_ids_primary);
decoded.free();
{
  voxelLevel: 24,
  columnMask: 31,
  headerLength: 40,
  byteLength: 87,
  vxBase: 4428917,
  vyBase: 11738705,
  vzBase: 12088148,
  count: 2
}
2 Float32Array(6) [
  -0.18406754732131958,
  0.4328923225402832,
  0.027242183685302734,
  -0.8299636840820312,
  0.8781789541244507,
  -0.5928690433502197
]
Uint8Array(8) [
  255, 0,   0, 0,
    0, 0, 255, 2
] Uint32Array(2) [ 4294967295, 7 ]

encode_voxel_packet

function encode_voxel_packet(
  voxels: Array<{
    vx: number; vy: number; vz: number;
    r?: number; g?: number; b?: number;
    sourceType?: number;
    normalMask?: number;
    featureIdPrimary?: number;
    featureIdSecondary?: number;
  }>,
  voxel_level: number,
): Uint8Array;

セルの配列から packet を 1 つ作ります。vx・vy・vz 以外は省けます。

キー 意味 省いたとき
vx・vy・vz セルの番号(整数) 省けません
r・g・b 色(0〜255) 0
sourceType 由来(下位 4 ビット。1 = メッシュ、2 = 点群、3 = 両方) 0
normalMask 露出面の向きのビット(下の表) 63(すべて)
featureIdPrimary・featureIdSecondary 地物の ID(0〜4294967294) 無し (4294967295)

normalMask のビットは、1 = +X、2 = −X、4 = +Y、8 = −Y、16 = +Z、32 = −Z です。軸は格子の軸です。

キーの名前は camelCase です。知らないキーは読み飛ばすので、綴りに気を付けてください。配列が空のとき、voxel_level が 1〜30 の外のとき、必須のキーが無いときや値の型が合わないとき(小数の番号、256 以上の色など)は Error を投げます。番号が 0〜2^level − 1 に収まっているかは確かめないので、正しい番号を渡してください。

packet_metadata

function packet_metadata(packet_data: Uint8Array, xor_key: number): {
  voxelLevel: number;
  columnMask: number;
  headerLength: number;
  byteLength: number;
  vxBase: number;
  vyBase: number;
  vzBase: number;
  count: number;
};

packet のヘッダだけを読みます。セルを展開しないので軽い処理です。

キー 意味
voxelLevel セルのレベル
columnMask 入っている列のビット。1 = 番号、2 = 色、4 = 由来、8 = 面の向き、16 = 地物の ID(主)、32 = 地物の ID(副)
headerLength ヘッダの長さ(バイト)
byteLength packet 全体の長さ(バイト)
vxBase・vyBase・vzBase 番号の基準(各軸の最小値)
count セルの数

xor_key は packet を隠すときに使った 1 バイトの鍵(0〜255)です。隠していなければ 0 です。鍵が違うときや packet が壊れているときは Error を投げます。

decode_voxel_packet

function decode_voxel_packet(
  packet_data: Uint8Array,
  xor_key: number,
  origin_lon: number, origin_lat: number, origin_height: number,
  clamp_level?: number | null,
): DecodedVoxels;

packet を展開して、セルの中心を原点(経度・緯度 [度]・楕円体高 [m])から見た ENU [m] にします。結果は DecodedVoxels で、使い終わったら free() を呼んでください。地球基準の格子の packet 用です。現場基準の格子の packet には decode_voxel_packet_in_frame を使います。

  • clamp_level を渡すと、それより細かいセルをレベル clamp_level に粗くしてから返します。1〜30 の値にしてください(0 を渡すと unreachable という Error になります)。packet のレベル以上の値なら、何も変わりません。
  • 同じセルになった複数のセルは 1 つにまとめ、最初に出てきた位置に置きます。面の向きのビットは OR を取ります。メッシュ由来と点群由来が重なったときは、由来を 3 にして点群の色を使います。地物の ID は、主と副の 2 つまで残します。

鍵が違うときや packet が壊れているときは Error を投げます。

decode_voxel_packet_lod

function decode_voxel_packet_lod(
  packet_data: Uint8Array,
  xor_key: number,
  origin_lon: number, origin_lat: number, origin_height: number,
  center_lon: number, center_lat: number,
  r1: number, r2: number, r3: number, r4: number,
  shape_mode: number,
  height_scale: number,
  is_paid: boolean,
): DecodedVoxels;

decode_voxel_packet と同じように展開しますが、注目点から遠いセルほど粗いレベルにまとめます。注目点は(center_lon、center_lat、origin_height)です。結果の位置は、原点(origin_lon、origin_lat、origin_height)から見た ENU [m] です。

セルごとに、注目点からの距離でレベルを選びます。距離の測り方は shape_mode で決まります。

  • shape_mode が 1 なら円柱で測ります。水平の距離が r 以下で、高さの差が r × height_scale 以下のとき、半径 r の内側とみなします。height_scale が負なら 0 として扱います。
  • それ以外なら球で測ります。3 次元の距離が r 以下のとき、半径 r の内側とみなします。

is_paid は、細かいレベルをどこまで許すかです。false なら最も細かくてレベル 24、true なら最も細かくてレベル 30 です。どちらも、最も粗いのはレベル 17 です。

注目点からの距離 is_paid: false is_paid: true
r1 / 32 以内 24 30
r1 / 16 以内 24 29
r1 / 8 以内 24 28
r1 / 4 以内 24 27
r1 / 2 以内 24 26
r1 × 0.75 以内 23 25
r1 以内 23 24
r2 以内 22 22
r3 以内 21 21
r4 以内 20 20
r4 × 1.5 以内 19 19
r4 × 3 以内 18 18
それより遠い 17 17

packet のレベルより細かくはなりません。セルの colors の 4 バイト目には、上位 4 ビットに(選んだレベル − 17)、下位 4 ビットに由来が入ります。鍵が違うときや packet が壊れているときは Error を投げます。

decode_voxel_packet_in_frame

function decode_voxel_packet_in_frame(
  packet_data: Uint8Array,
  xor_key: number,
  frame: FrameJs,
  origin_x: number, origin_y: number, origin_z: number,
  clamp_level?: number | null,
): DecodedVoxels;

格子を指定して packet を展開します。現場基準の格子の packet を読むときに使います。結果の位置は、セルの中心を格子の座標で表し、そこから原点 (origin_x, origin_y, origin_z) [m] を引いた値です。現場基準の格子ではセルが軸に沿った箱になるので、そのまま描けます。

  • 原点は、格子の座標で表します。現場基準の格子なら現場の座標 [m]、地球基準の格子なら ECEF [m] です。位置は 32 ビットの小数 (Float32Array) になるので、原点はデータの近くに置いてください。ECEF の値をそのまま 32 ビットにすると、0.5 m 刻みに丸まってしまいます。
  • 地球基準の格子でも呼べますが、位置は ECEF の軸のまま原点を移しただけの座標です。decode_voxel_packet の ENU とは別のものです。
  • clamp_level の意味と例外の条件は decode_voxel_packet と同じです。

渡した frame は中身を読むだけなので、呼び出しの後も使えます。

DecodedVoxels

class DecodedVoxels {
  readonly count: number;
  readonly positions: Float32Array;
  readonly colors: Uint8Array;
  readonly normal_masks: Uint8Array;
  readonly feature_ids_primary: Uint32Array;
  readonly feature_ids_secondary: Uint32Array;
  free(): void;
  [Symbol.dispose](): void;
}

packet を展開した結果です。データは wasm のメモリにあるので、使い終わったら free() を呼んでください。直接 new では作れません。

プロパティ 意味
count セルの数
positions セルの中心。1 セル 3 要素 [m]
colors 1 セル 4 バイト。r, g, b と、由来(下位 4 ビット。decode_voxel_packet_lod では上位 4 ビットにレベルも入る)
normal_masks 1 セル 1 バイト。露出面の向きのビット(encode_voxel_packet の表を参照)
feature_ids_primary・feature_ids_secondary 1 セル 1 要素。地物の ID。無いときは 4294967295

型付き配列のプロパティは、読むたびに wasm のメモリから新しい配列を写して作ります。何度も使うときは、一度変数に入れてください。

描画 (render.mjs)

露出面 (VoxelFaces) を WebGL2 で描く描画器です。ほかのライブラリに依存しません。@mirgrid/web-sdk/render.mjs から import します。

import するだけなら DOM や WebGL に触れないので、Node でも import できます。描くにはブラウザの WebGL2 が要ります。

import init, { VoxelizerJs } from '@mirgrid/web-sdk';
import { createVoxelRenderer } from '@mirgrid/web-sdk/render.mjs';

await init();
const voxelizer = new VoxelizerJs(24);
voxelizer.setInput({ coordSystem: 'latlon' });
voxelizer.pushPoints(Float64Array.of(139.7671, 35.6812, 40, 139.76711, 35.6812, 40, 139.7672, 35.6812, 40));

const canvas = document.querySelector('canvas');
const renderer = createVoxelRenderer(canvas);
renderer.setLayer('points', voxelizer.faces());
canvas.addEventListener('click', (event) => {
  const hit = renderer.pick(event.clientX, event.clientY);
  if (hit) console.log(voxelizer.cellInfo(hit.cell));
});

描画空間は、originLlh から見た ENU [m] です。z 軸が上を向きます。マウスとタッチで視点を動かせます。

操作 動き
左ドラッグ・1 本指 回転
右ドラッグ・中ボタンのドラッグ・Shift + ドラッグ 平行移動
ホイール 拡大・縮小
2 本指 つまんで拡大・縮小と平行移動

画面に変化があったときだけ、requestAnimationFrame で描き直します。canvas の大きさは ResizeObserver と devicePixelRatio に合わせます。

createVoxelRenderer

function createVoxelRenderer(canvas: HTMLCanvasElement, options?: VoxelRendererOptions): VoxelRenderer;

canvas に描く描画器を作ります。options は VoxelRendererOptions です。canvas でないものを渡したときや、WebGL2 が使えないときは Error を投げます。

VoxelRendererOptions

interface VoxelRendererOptions {
  background?: [number, number, number, number];
  lightDirection?: [number, number, number];
  ambient?: number;
  fovDeg?: number;
  azimuthDeg?: number;
  elevationDeg?: number;
  pixelRatio?: number;
  onContextLost?: () => void;
  onContextRestored?: () => void;
  onRender?: (view: VoxelView) => void;
}
キー 意味 既定
background 背景色 [r, g, b, a](各 0〜1) [0.96, 0.97, 0.98, 1]
lightDirection 光が来る向き(描画空間の ENU。長さは 1 でなくてよい) [0.35, -0.5, 1]
ambient 光が当たらない面の明るさ(0〜1) 0.55
fovDeg 縦の視野角 [度] 45
azimuthDeg 最初の視点の方位角 [度]。東から反時計回り −60
elevationDeg 最初の視点の仰角 [度] 35
pixelRatio 描く解像度の倍率 devicePixelRatio
onContextLost WebGL の context を失ったときに呼ぶ関数。この間は描画が止まります 無し
onContextRestored context が戻ったときに呼ぶ関数。GPU の資源は作り直してあります 無し
onRender 描くたびに、今の視点 (VoxelView) を渡して呼ぶ関数。方位の印などを重ねて描くのに使います 無し

VoxelRenderer

interface VoxelRenderer {
  setLayer(id: string, faces: RenderableFaces, options?: VoxelLayerOptions): void;
  removeLayer(id: string): boolean;
  setVisible(id: string, visible: boolean): void;
  fitView(): void;
  render(): void;
  pick(clientX: number, clientY: number): VoxelPick | null;
  setView(view: VoxelViewSpec): void;
  dispose(): void;
  readonly view: VoxelView;
  readonly originLlh: [number, number, number] | null;
  readonly contextLost: boolean;
  readonly layerIds: string[];
}

createVoxelRenderer が返す描画器です。

メソッド・プロパティ 動き
setLayer(id, faces, options?) 露出面をレイヤとして置きます。同じ id があれば置き換えます。最初のレイヤを置いたときに、視点をそのレイヤに合わせます。options は VoxelLayerOptions です
removeLayer(id) レイヤを取り除きます。取り除いたら true、そのレイヤが無ければ false を返します
setVisible(id, visible) レイヤを表示するか隠すかを切り替えます
fitView() 見えているレイヤ全体が収まるように、視点を合わせます
render() すぐに描きます。ふだんは変化のたびに自動で描くので、呼ばなくてかまいません
pick(clientX, clientY) 画面の座標(MouseEvent.clientX・clientY)の下にある面を VoxelPick で返します。何も無ければ null です
setView(view) 視点を VoxelViewSpec で決めます。書かなかった項目は今のままです
dispose() 操作の listener、ResizeObserver、GPU の資源を外します。2 回目以降は何もしません
view 今の視点 (VoxelView)
originLlh レイヤの描画空間の原点。レイヤが無ければ null
contextLost WebGL の context を失っている間は true
layerIds 置いてあるレイヤの id の配列

1 つの描画器に重ねるレイヤは、同じ originLlh で作ってください。2 つ目からは voxelizer.faces(renderer.originLlh) や voxel_faces_from_packet(bytes, 0, frame, renderer.originLlh) と書きます。

次のときは Error を投げます。

  • setLayer: faces の形が正しくないとき、ほかのレイヤと原点が違うとき、dispose() の後のとき、新しい id でレイヤを置いた回数が 65535 を超えたとき(取り除いたレイヤの分も数えます)。
  • setVisible: その id のレイヤが無いとき。
  • setView: 値が有限でないとき、distanceM が 0 以下のとき。

setView で targetEnu か distanceM を決めると、最初のレイヤでの自動の視点合わせはしなくなります。向き(azimuthDeg・elevationDeg)だけを決めたときは、自動で合わせます。

VoxelView

interface VoxelView {
  targetEnu: [number, number, number];
  distanceM: number;
  azimuthDeg: number;
  elevationDeg: number;
  axesOnScreen: { east: [number, number]; north: [number, number]; up: [number, number] };
}

今の視点です。VoxelRenderer の view と、onRender の引数で受け取ります。

キー 意味
targetEnu 視点が回る中心 [e, n, u] [m]
distanceM 中心から目までの距離 [m]
azimuthDeg 目の方位角 [度]。東から反時計回りで、−90 なら南から北を見ます
elevationDeg 目の仰角 [度]。±89.94 度までで、90 に近いほど真上から見ます
axesOnScreen 東・北・上の単位ベクトルが画面で向く方向 [x 右, y 下]。長さは 1 以下で、視線に沿う軸ほど短くなります。方位の印を描くのに使います

VoxelViewSpec

interface VoxelViewSpec {
  targetEnu?: [number, number, number];
  distanceM?: number;
  azimuthDeg?: number;
  elevationDeg?: number;
}

setView に渡す視点です。意味は VoxelView の同じ名前のキーと同じです。真上から見るなら elevationDeg: 90、南から見るなら azimuthDeg: -90 を渡します。elevationDeg は ±89.94 度に収めます。

VoxelLayerOptions

interface VoxelLayerOptions {
  visible?: boolean;
}

setLayer の選択肢です。visible の既定は true です。同じ id のレイヤを置き換えるときに省くと、前の値を引き継ぎます。

VoxelPick

interface VoxelPick {
  layerId: string;
  face: number;
  cell: number;
  direction: number;
}

pick の結果です。layerId はレイヤの id、face はレイヤの中の面の番号です。cell はセル番号 (faces.cells[face]) で、cellInfo にそのまま渡せます。direction は面の向き(0〜5 = +X, −X, +Y, −Y, +Z, −Z。格子の軸)です。

RenderableFaces

interface RenderableFaces {
  faceCount: number;
  coords: Int16Array | Float32Array;
  colors: Uint8Array;
  cells: Uint32Array;
  gridToRender: ArrayLike<number>;
  originLlh: ArrayLike<number>;
  boundsRender: ArrayLike<number>;
}

描画器が読む露出面のキーです。VoxelFaces の一部なので、faces() や voxel_faces_from_packet() の結果をそのまま渡せます。キーの意味は VoxelFaces と同じです。

形を作る (creation.mjs)

作図面 (View) の上に 2 次元の多角形を描き、面の法線に沿って押し出して、立体を作ります。@mirgrid/web-sdk/creation.mjs から import します。

  • 長さの単位は m です。座標は、利用者が決めた手元の [x, y, z] [m] です(x が東、y が北、z が上)。
  • 幾何の計算は wasm で行うので、init の後に呼んでください。前に呼ぶと Error を投げます。メタデータの関数だけは、init の前でも使えます。
  • 作図面の軸 u・v は直交する単位ベクトルになり、法線は u × v です。作図面の上で反時計回りに描いた多角形は、符号付きの面積が正になります。
import init, { ops_extrude_polygon_to_cells } from '@mirgrid/web-sdk';
import { createView, createPolygon, extrude, extrusionCandidateOf } from '@mirgrid/web-sdk/creation.mjs';

await init();
const view = createView({ plane: 'XY', origin: [0, 0, 0] });
const room = extrude(createPolygon(view, [[0, 0], [4, 0], [4, 3], [0, 3]]), 2.4);
console.log(room.volumeM3, room.bbox);
const candidate = extrusionCandidateOf(room);
console.log(candidate);
const cells = ops_extrude_polygon_to_cells(
  24, 139.7671, 35.6812, 40,
  Float64Array.from(candidate.polygon.flat()), candidate.min, candidate.max, 10000, candidate.normalAxis,
);
console.log(cells.size);
cells.free();
28.8 { min: [ 0, 0, 0 ], max: [ 4, 3, 2.4 ] }
{
  normalAxis: 'up',
  polygon: [ [ 0, 0 ], [ 4, 0 ], [ 4, 3 ], [ 0, 3 ] ],
  min: 0,
  max: 2.4
}
26

defineViewPlane

function defineViewPlane(spec?: ViewPlaneSpec): PlaneGeometry;

作図面の幾何だけを求めます。戻り値は { origin, u, v, normal, scaleU, scaleV } で、ID やメタデータは付きません。spec (ViewPlaneSpec) では、面の向きを次のどれか 1 つで決めます。

書き方 面
plane: 'XY' u = 東、v = 北、法線 = 上
plane: 'XZ' u = 東、v = 上、法線 = 南
plane: 'YZ' u = 北、v = 上、法線 = 東
u と v 面内の 2 軸。v は u に直交するように直します
direction と normal 面内の 1 軸と法線。direction は normal に直交するように直します

ほかに次のキーを書けます。

  • origin: 面の原点。[x, y, z] [m] か、{ longitude, latitude, height? } です。緯度経度で書くときは、fromLlh メソッドを持つ object を frame に渡してください。fromLlh({ longitude, latitude, height }) が返す [x, y, z] を原点にします。既定は [0, 0, 0] です。
  • scale: 作図の 1 単位が何 m か。数か [scaleU, scaleV] です。mm で描くなら 0.001 にします。既定は 1 です。

次のときは Error を投げます。

  • 向きの指定が 0 個か、2 個以上のとき。
  • u と v(または direction と normal)の片方しか無いとき。
  • plane が 3 つのどれでもないとき。
  • scale が正の数でないとき。
  • 緯度経度の origin に frame が無いとき。

View

class View implements PlaneGeometry, CreationMetadata {
  constructor(spec?: ViewSpec);
  id: string;
  kind: 'view';
  origin: Vec3; u: Vec3; v: Vec3; normal: Vec3;
  scaleU: number; scaleV: number;
  confidence: number | null;
  source: string | null;
  reconstructionState: ReconstructionState;
  toWorld(point: Vec2): Vec3;
  projectToPlane(point: Vec3): { u: number; v: number; distanceM: number };
}

ID とメタデータを持つ作図面です。幾何は defineViewPlane と同じです。spec には defineViewPlane のキーに加えて id を書けます。省くと 'view-1' のような ID が付きます。

  • toWorld([u, v]) は、作図の座標を [x, y, z] [m] にします。
  • projectToPlane([x, y, z]) は、projectToPlane と同じです。

createView

function createView(spec?: ViewSpec): View;

new View(spec) と同じです。

import init from '@mirgrid/web-sdk';
import { createView, setConfidence, setSource, setReconstructionState } from '@mirgrid/web-sdk/creation.mjs';

await init();
const view = setConfidence(createView({ plane: 'XZ', id: 'north-wall' }), 0.8);
setSource(view, 'manual');
setReconstructionState(view, 'inferred');
console.log(view.id, view.normal, view.confidence, view.source, view.reconstructionState);
north-wall [ 0, -1, 0 ] 0.8 manual inferred

projectToPlane

function projectToPlane(view: PlaneGeometry, point: Vec3): { u: number; v: number; distanceM: number };

3 次元の点を作図面に垂直に下ろします。u・v は作図の座標で、view.toWorld で元に戻せます。distanceM は、法線の向きを正とした、面からの距離 [m] です。view には View も defineViewPlane の結果も渡せます。点が有限の数でなければ Error を投げます。

createPolygon

function createPolygon(view: View, points: Vec2[], options?: { id?: string }): Polygon;

作図面の上に描いた環(作図の座標 [u, v] の配列)を、3 次元の多角形にします。戻り値は Polygon です。signedArea は符号付きの面積 [m²] で、作図面の上で反時計回りなら正です。areaM2 は面積 [m²]、vertices3d は頂点の [x, y, z] [m] です。options.id を省くと 'polygon-1' のような ID が付きます。

view が View でないとき、点が 3 つ未満のとき、面積が 0 のときは Error を投げます。

extrude

function extrude(polygon: Polygon, distanceM: number, options?: { id?: string }): Space;

多角形を、作図面から法線に沿って distanceM [m] 押し出した立体にします。distanceM が負なら、法線と逆の向きに押し出します。0 なら Error を投げます。戻り値は Space です。

キー 意味
distanceM 押し出した距離(符号付き) [m]
heightM 押し出した距離の大きさ [m]
baseVertices3d・topVertices3d 底面と上面の頂点 [x, y, z] [m]
volumeM3 体積 [m³]
bbox 立体を囲む箱 { min, max } [m]

options.id を省くと 'space-1' のような ID が付きます。polygon が createPolygon の結果でなければ Error を投げます。

planeAxisOf

function planeAxisOf(view: PlaneGeometry): 'east' | 'north' | 'up' | null;

作図面の法線がそろっている ENU の軸を返します。向きの符号は問いません。斜めの面では null を返します。

extrusionCandidateOf

function extrusionCandidateOf(space: Space): ExtrusionCandidate;

extrude の立体を、ops_extrude_polygon_to_cells の引数の形 { normalAxis, polygon, min, max } にします。polygon は、normalAxis を除いた ENU の 2 軸を東・北・上の順に並べた頂点です。min・max は押し出した範囲です。座標の原点は、ops_extrude_polygon_to_cells に渡す経度・緯度・楕円体高になります。

space が extrude の結果でないときや、作図面が ENU の軸にそろっていない(斜めの面の)ときは Error を投げます。

メタデータの関数

function setConfidence<T extends CreationEntity>(entity: T, value: number | null): T;
function getConfidence(entity: CreationEntity): number | null;
function setSource<T extends CreationEntity>(entity: T, value: string | null): T;
function getSource(entity: CreationEntity): string | null;
function setReconstructionState<T extends CreationEntity>(entity: T, state: ReconstructionState): T;
function getReconstructionState(entity: CreationEntity): ReconstructionState;
const RECONSTRUCTION_STATES: readonly ReconstructionState[]; // ['confirmed', 'inferred', 'unknown']

View・Polygon・Space に、どれだけ確かか・どこから来たか・どう作ったかを書き込みます。set* は渡したオブジェクトを書き換えて、そのオブジェクトを返します。作ったばかりのオブジェクトは、confidence: null、source: null、reconstructionState: 'unknown' です。どれも wasm を使わないので、init の前でも呼べます。

  • setConfidence の value は 0〜1 の数か null です。範囲の外なら Error を投げます。null で未設定に戻します。
  • setSource の value は出どころを書く自由な文字列です。null で消します。
  • setReconstructionState の state は 'confirmed'(確かめた)・'inferred'(推定した)・'unknown'(不明)のどれかです。大文字で書いてもかまいません。ほかの値なら Error を投げます。
  • RECONSTRUCTION_STATES は、この 3 つを並べた配列です。

creation.mjs の型

type Vec3 = [number, number, number];
type Vec2 = [number, number];
type ViewPlanePreset = 'XY' | 'XZ' | 'YZ';
type ReconstructionState = 'confirmed' | 'inferred' | 'unknown';
type ExtrusionNormalAxis = 'east' | 'north' | 'up';
type CreationEntity = View | Polygon | Space;

interface GeodeticOrigin { longitude: number; latitude: number; height?: number }
interface FrameLike { fromLlh(origin: { longitude: number; latitude: number; height: number }): number[] }
interface CreationMetadata { confidence: number | null; source: string | null; reconstructionState: ReconstructionState }

interface ViewPlaneSpec {
  plane?: ViewPlanePreset;
  origin?: Vec3 | GeodeticOrigin;
  frame?: FrameLike;
  u?: Vec3; v?: Vec3;
  direction?: Vec3; normal?: Vec3;
  scale?: number | [number, number];
}
interface ViewSpec extends ViewPlaneSpec { id?: string }
interface PlaneGeometry { origin: Vec3; u: Vec3; v: Vec3; normal: Vec3; scaleU: number; scaleV: number }

interface Polygon extends CreationMetadata {
  id: string; kind: 'polygon'; view: View; points: Vec2[];
  signedArea: number; areaM2: number; vertices3d: Vec3[];
}
interface Space extends CreationMetadata {
  id: string; kind: 'space'; polygon: Polygon; view: View;
  distanceM: number; heightM: number;
  baseVertices3d: Vec3[]; topVertices3d: Vec3[];
  volumeM3: number; bbox: { min: Vec3; max: Vec3 };
}
interface ExtrusionCandidate { normalAxis: ExtrusionNormalAxis; polygon: Vec2[]; min: number; max: number }

Vec3 は [x, y, z] [m](x が東、y が北、z が上)、Vec2 は作図の座標 [u, v](作図の単位)です。

creation_ で始まる wasm の関数

@mirgrid/web-sdk には、creation.mjs が中で呼ぶ次の 7 つの関数もあります。ふだんは creation.mjs の関数を使ってください。plane には View か defineViewPlane の結果を渡します。

function creation_view_plane(spec: ViewPlaneSpec): PlaneGeometry;
function creation_to_world(plane: PlaneGeometry, point: Vec2): Vec3;
function creation_project_to_plane(plane: PlaneGeometry, point: Vec3): { u: number; v: number; distanceM: number };
function creation_polygon(plane: PlaneGeometry, points: Vec2[]): { points: Vec2[]; signedArea: number; areaM2: number; vertices3d: Vec3[] };
function creation_extrude(plane: PlaneGeometry, points: Vec2[], distance_m: number): { distanceM: number; heightM: number; baseVertices3d: Vec3[]; topVertices3d: Vec3[]; volumeM3: number; bbox: { min: Vec3; max: Vec3 } };
function creation_plane_axis(plane: PlaneGeometry): 'east' | 'north' | 'up' | null;
function creation_extrusion_candidate(plane: PlaneGeometry, points: Vec2[], distance_m: number): ExtrusionCandidate;
関数 対応する creation.mjs の関数
creation_view_plane defineViewPlane(緯度経度の origin は受け付けません)
creation_to_world View.toWorld
creation_project_to_plane projectToPlane
creation_polygon createPolygon の幾何の部分
creation_extrude extrude の幾何の部分
creation_plane_axis planeAxisOf
creation_extrusion_candidate extrusionCandidateOf

例外の条件は、対応する creation.mjs の関数と同じです。

低レベルの LOD

LOD(遠くを粗く、近くを細かく表示する仕組み)の核と露出面の抽出を、wasm のメモリ越しに直接呼ぶための API です。表示するタイルやレベルを自前で決める、描画エンジンの作者向けのものです。セルを描くだけなら、voxel_faces_from_packet と 描画 (render.mjs) で足ります。

核の関数は、init が返す InitOutput に mg_ で始まる名前で入っています。引数の構造体は wasm のメモリに置き、その位置(数)を渡します。構造体の配置は lod_layout() の表から読みます。

@mirgrid/web-sdk の関数:

関数 中身
lod_layout(): string 構造体の配置の表 (JSON)
lod_api_version(): number 核の API の版
lod_alloc(size: number, align: number): number wasm のメモリを確保して、位置を返す
lod_dealloc(ptr: number, size: number, align: number): void lod_alloc で確保したメモリを返す

@mirgrid/web-sdk/lod.mjs の関数:

関数 中身
parseLayout(json: string): Map<string, StructLayout> lod_layout() の表を、構造体の名前ごとの配置に直す
createLodAbi(sdk: LodSdk, wasm: LodWasm): LodAbi 構造体の読み書きと mg_* の呼び出しをまとめたオブジェクトを作る。sdk は import * as sdk from '@mirgrid/web-sdk'、wasm は await init() の戻り値

lod.mjs の型: FieldKind・LayoutField・StructLayout・FieldValue・FieldWriter・FieldReader・LodAbi・LodSdk・LodWasm。