core_spatial_SpatialIdQaMetrics.js - hiro-nyon/cesium-heatbox GitHub Wiki

Source: core/spatial/SpatialIdQaMetrics.js

日本語 | English

English

See also: Class: SpatialIdQaMetrics

import { ZFXYConverter } from './ZFXYConverter.js';

/**
 * Compute SpatialIdEdgeCaseMetrics for a given adapter instance.
 * 指定されたアダプタに対する SpatialIdEdgeCaseMetrics を計算します。
 *
 * 主にテスト用の軽量なQAヘルパーであり、本番運用では明示的に呼び出さない限り
 * メトリクスは計算されません。
 *
 * @param {Object} adapter - Loaded SpatialIdAdapter instance / 初期化済みSpatialIdAdapterインスタンス
 * @returns {Object} SpatialIdEdgeCaseMetrics-like object / SpatialIdEdgeCaseMetrics互換オブジェクト
 */
export function computeSpatialIdEdgeCaseMetrics(adapter) {
  const datelineZoom = 24;
  const datelineN = Math.pow(2, datelineZoom);
  const datelineY = Math.floor(datelineN / 2);
  const datelineF = 0;

  const tileLeft = { z: datelineZoom, f: datelineF, x: 0, y: datelineY };
  const tileRight = { z: datelineZoom, f: datelineF, x: datelineN - 1, y: datelineY };

  const neighborsLeft = adapter.neighbors(tileLeft);
  const neighborsRight = adapter.neighbors(tileRight);

  const datelineNeighborsChecked = neighborsLeft.length + neighborsRight.length;
  let datelineNeighborsMismatched = 0;

  const hasTile = (list, target) =>
    list.some(n => n.z === target.z && n.f === target.f && n.x === target.x && n.y === target.y);

  if (!hasTile(neighborsLeft, tileRight)) {
    datelineNeighborsMismatched++;
  }
  if (!hasTile(neighborsRight, tileLeft)) {
    datelineNeighborsMismatched++;
  }

  // Polar tiles: evaluate a few representative tiles near ±85°
  const polarZoom = 20;
  const polarTestLats = [80, 82.5, 85];
  const polarLng = 0;

  let polarTilesChecked = 0;
  let polarMaxRelativeErrorXY = 0;

  polarTestLats.forEach((latDeg) => {
    const resultAdapter = adapter.getVoxelBounds(polarLng, latDeg, 0, polarZoom);
    const resultMercator = ZFXYConverter.convert(polarLng, latDeg, 0, polarZoom);

    const adapterLngs = resultAdapter.vertices.map(v => v.lng);
    const adapterLats = resultAdapter.vertices.map(v => v.lat);
    const mercLngs = resultMercator.vertices.map(v => v.lng);
    const mercLats = resultMercator.vertices.map(v => v.lat);

    const widthAdapterX = Math.max(...adapterLngs) - Math.min(...adapterLngs);
    const heightAdapterY = Math.max(...adapterLats) - Math.min(...adapterLats);
    const widthMercX = Math.max(...mercLngs) - Math.min(...mercLngs);
    const heightMercY = Math.max(...mercLats) - Math.min(...mercLats);

    if (widthMercX > 0 && heightMercY > 0) {
      const relativeErrorX = Math.abs(widthAdapterX - widthMercX) / widthMercX;
      const relativeErrorY = Math.abs(heightAdapterY - heightMercY) / heightMercY;
      const maxRelativeErrorXY = Math.max(relativeErrorX, relativeErrorY);
      if (maxRelativeErrorXY > polarMaxRelativeErrorXY) {
        polarMaxRelativeErrorXY = maxRelativeErrorXY;
      }
    }

    polarTilesChecked++;
  });

  // Hemisphere bounds: simple consistency check using dateline tiles as proxy
  const hemisphereBoundsChecked = 1;
  const hemisphereBoundsMismatched = datelineNeighborsMismatched > 0 ? 1 : 0;

  return {
    datelineNeighborsChecked,
    datelineNeighborsMismatched,
    polarTilesChecked,
    polarMaxRelativeErrorXY,
    hemisphereBoundsChecked,
    hemisphereBoundsMismatched
  };
}

日本語

関連: SpatialIdQaMetricsクラス

import { ZFXYConverter } from './ZFXYConverter.js';

/**
 * Compute SpatialIdEdgeCaseMetrics for a given adapter instance.
 * 指定されたアダプタに対する SpatialIdEdgeCaseMetrics を計算します。
 *
 * 主にテスト用の軽量なQAヘルパーであり、本番運用では明示的に呼び出さない限り
 * メトリクスは計算されません。
 *
 * @param {Object} adapter - Loaded SpatialIdAdapter instance / 初期化済みSpatialIdAdapterインスタンス
 * @returns {Object} SpatialIdEdgeCaseMetrics-like object / SpatialIdEdgeCaseMetrics互換オブジェクト
 */
export function computeSpatialIdEdgeCaseMetrics(adapter) {
  const datelineZoom = 24;
  const datelineN = Math.pow(2, datelineZoom);
  const datelineY = Math.floor(datelineN / 2);
  const datelineF = 0;

  const tileLeft = { z: datelineZoom, f: datelineF, x: 0, y: datelineY };
  const tileRight = { z: datelineZoom, f: datelineF, x: datelineN - 1, y: datelineY };

  const neighborsLeft = adapter.neighbors(tileLeft);
  const neighborsRight = adapter.neighbors(tileRight);

  const datelineNeighborsChecked = neighborsLeft.length + neighborsRight.length;
  let datelineNeighborsMismatched = 0;

  const hasTile = (list, target) =>
    list.some(n => n.z === target.z && n.f === target.f && n.x === target.x && n.y === target.y);

  if (!hasTile(neighborsLeft, tileRight)) {
    datelineNeighborsMismatched++;
  }
  if (!hasTile(neighborsRight, tileLeft)) {
    datelineNeighborsMismatched++;
  }

  // Polar tiles: evaluate a few representative tiles near ±85°
  const polarZoom = 20;
  const polarTestLats = [80, 82.5, 85];
  const polarLng = 0;

  let polarTilesChecked = 0;
  let polarMaxRelativeErrorXY = 0;

  polarTestLats.forEach((latDeg) => {
    const resultAdapter = adapter.getVoxelBounds(polarLng, latDeg, 0, polarZoom);
    const resultMercator = ZFXYConverter.convert(polarLng, latDeg, 0, polarZoom);

    const adapterLngs = resultAdapter.vertices.map(v => v.lng);
    const adapterLats = resultAdapter.vertices.map(v => v.lat);
    const mercLngs = resultMercator.vertices.map(v => v.lng);
    const mercLats = resultMercator.vertices.map(v => v.lat);

    const widthAdapterX = Math.max(...adapterLngs) - Math.min(...adapterLngs);
    const heightAdapterY = Math.max(...adapterLats) - Math.min(...adapterLats);
    const widthMercX = Math.max(...mercLngs) - Math.min(...mercLngs);
    const heightMercY = Math.max(...mercLats) - Math.min(...mercLats);

    if (widthMercX > 0 && heightMercY > 0) {
      const relativeErrorX = Math.abs(widthAdapterX - widthMercX) / widthMercX;
      const relativeErrorY = Math.abs(heightAdapterY - heightMercY) / heightMercY;
      const maxRelativeErrorXY = Math.max(relativeErrorX, relativeErrorY);
      if (maxRelativeErrorXY > polarMaxRelativeErrorXY) {
        polarMaxRelativeErrorXY = maxRelativeErrorXY;
      }
    }

    polarTilesChecked++;
  });

  // Hemisphere bounds: simple consistency check using dateline tiles as proxy
  const hemisphereBoundsChecked = 1;
  const hemisphereBoundsMismatched = datelineNeighborsMismatched > 0 ? 1 : 0;

  return {
    datelineNeighborsChecked,
    datelineNeighborsMismatched,
    polarTilesChecked,
    polarMaxRelativeErrorXY,
    hemisphereBoundsChecked,
    hemisphereBoundsMismatched
  };
}