コンテンツにスキップ

Basic Visualization

このチュートリアルは、Getting Started で作成したプロジェクトを前提としています。まずそちらの手順に従って、Vite の TypeScript プロジェクトの作成、<body> のスタイル設定、Navara のパッケージと peer dependencies のインストールを済ませてください。

このチュートリアルではさらに @navaramap/three-pluginsTileJsonPlugin を使うため、このパッケージも追加でインストールします:

Terminal window
npm install @navaramap/three-plugins

src/main.ts の内容を以下のコードに置き換えます。

import ThreeView from "@navaramap/three";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
import { TileJsonPlugin } from "@navaramap/three-plugins";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({});
view.addPlugin(plugin);
const tilejson = new TileJsonPlugin();
view.addPlugin(tilejson);
await view.init();

view.init() で 3D シーンとカメラが構築されます。プラグインはこの呼び出しのでしか登録できず、後から addPlugin() を呼ぶと例外になります。DefaultPlugin を登録しておくことで、次のステップで使う ambient などの組み込み Descriptor が有効になります。

main.ts に以下のコードを追加します。

view.addLight({
ambient: {},
});
const basemapSource = await tilejson.addSource({
type: "raster-tile",
url: "https://papers.reearth.land/styles/protomaps-light/tilejson.json",
});
view.addLayer({
type: "raster",
source: basemapSource,
});

ラスタのベースマップは環境光だけで十分です。ベースマップは Re:Earth Papers の TileJSON から取得し、TileJsonPlugin.addSource() がタイル URL テンプレート・ズーム範囲・attribution をそこから導出します。詳細は TileJsonPlugin を参照してください。

シーン上の地球儀に背景地図が表示されます。

Basic Map

import ThreeView from "@navaramap/three";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
import { TileJsonPlugin } from "@navaramap/three-plugins";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({});
view.addPlugin(plugin);
const tilejson = new TileJsonPlugin();
view.addPlugin(tilejson);
await view.init();
view.addLight({
ambient: {},
});
const basemapSource = await tilejson.addSource({
type: "raster-tile",
url: "https://papers.reearth.land/styles/protomaps-light/tilejson.json",
});
view.addLayer({
type: "raster",
source: basemapSource,
});

特定の場所に地図を表示するには、カメラの位置を設定します。main.ts に以下を追加します:

view.setCamera({
lng: 139.7671,
lat: 35.6812,
height: 1000,
heading: 0, // -180 to 180
pitch: -30, // -180 to 0
roll: 0, // -180 to 180
});

パラメータの詳細は ThreeView Functions を参照してください。

東京周辺にカメラの位置が設定されます。

Camera Map

import ThreeView from "@navaramap/three";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
import { TileJsonPlugin } from "@navaramap/three-plugins";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({});
view.addPlugin(plugin);
const tilejson = new TileJsonPlugin();
view.addPlugin(tilejson);
await view.init();
view.addLight({
ambient: {},
});
const basemapSource = await tilejson.addSource({
type: "raster-tile",
url: "https://papers.reearth.land/styles/protomaps-light/tilejson.json",
});
view.addLayer({
type: "raster",
source: basemapSource,
});
view.setCamera({
lng: 139.7671,
lat: 35.6812,
height: 1000,
heading: 0, // -180 to 180
pitch: -30, // -180 to 0
roll: 0, // -180 to 180
});

地形には quantized-mesh ソースを使います。メッシュ化済みのタイルなので標高のデコードが不要で、頂点法線をリクエストすればメッシュが直接ライティングされるため、別途 hillshade レイヤーを重ねる必要もありません。

ラスタタイルレイヤーのに地形レイヤーを追加してください(レイヤーは追加順に描画されます):

const terrainSource = view.addSource({
type: "quantized-mesh",
url: "https://terrain.reearth.land/cesium-mesh/ellipsoid/{z}/{x}/{y}.terrain",
requestVertexNormals: true,
maxZoom: 18,
});
view.addLayer({
type: "terrain",
source: terrainSource,
terrain: {
castShadow: true,
receiveShadow: true,
},
});

データは Re:Earth Terrain が配信する全球の quantized-mesh です。

  • requestVertexNormals は法線をサーバーに要求します。次のステップで追加する sun ライトが、この法線を使って斜面に陰影をつけます。
  • maxZoom などの取得設定はソースに、terrain にはメッシュの描画設定(影・スカート)だけを書きます。

詳細については、Terrain Layer を参照してください。

Ambient ライトを Sun ライトに置き換える

Section titled “Ambient ライトを Sun ライトに置き換える”

環境光はすべての面を均一に照らすため、環境光だけでは地形の起伏が見えません。view.addLight({ ambient: {} }) の呼び出しを sun ライトに置き換えます。sun ライトは、先ほど要求した法線を使って斜面に陰影をつけます。

view.addLight({
sun: {},
});
view.atmosphere.date = new Date("2026-07-16T01:00:00Z");

太陽の方向は view.atmosphere.date から導出されます。どのマシンでも同じ見た目になるよう、UTC 文字列("...Z")で指定してください。詳細については、SunLightDesc を参照してください。

太陽は、地形レイヤーに設定した castShadow / receiveShadow オプションを通じて地形に影も落とします。シャドウマッピングはビューの構築時に有効化する必要があるため(shadow は初期化時にのみ設定できるオプションです)、ThreeView のコンストラクタ呼び出しを変更します。

const view = new ThreeView<DefaultDescriptions>({ shadow: true });

起伏が際立つ富士山にカメラを移動します。view.setCamera() の呼び出しを変更します。

view.setCamera({
lng: 138.75,
lat: 35.5,
height: 6000,
heading: 185, // -180 to 180
pitch: -26, // -180 to 0
roll: 0, // -180 to 180
});

Terrain Map

import ThreeView from "@navaramap/three";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
import { TileJsonPlugin } from "@navaramap/three-plugins";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({ shadow: true });
view.addPlugin(plugin);
const tilejson = new TileJsonPlugin();
view.addPlugin(tilejson);
await view.init();
view.addLight({
sun: {},
});
view.atmosphere.date = new Date("2026-07-16T01:00:00Z");
const terrainSource = view.addSource({
type: "quantized-mesh",
url: "https://terrain.reearth.land/cesium-mesh/ellipsoid/{z}/{x}/{y}.terrain",
requestVertexNormals: true,
maxZoom: 18,
});
view.addLayer({
type: "terrain",
source: terrainSource,
terrain: {
castShadow: true,
receiveShadow: true,
},
});
const basemapSource = await tilejson.addSource({
type: "raster-tile",
url: "https://papers.reearth.land/styles/protomaps-light/tilejson.json",
});
view.addLayer({
type: "raster",
source: basemapSource,
});
view.setCamera({
lng: 138.75,
lat: 35.5,
height: 6000,
heading: 185, // -180 to 180
pitch: -26, // -180 to 0
roll: 0, // -180 to 180
});

カメラが向いている富士山の北側斜面を覆うポリゴンを追加します。

const geojsonSource = view.addSource({
type: "geojson",
data: {
type: "Feature",
properties: { name: "Area" },
geometry: {
type: "Polygon",
coordinates: [
[
[138.7, 35.425],
[138.7, 35.4],
[138.745, 35.4],
[138.745, 35.425],
[138.7, 35.425],
],
],
},
},
});
view.addLayer({
type: "vector",
source: geojsonSource,
polygon: {
color: new Color().setHex(0xff3b30),
opacity: 0.75,
transparent: true,
},
});

view.addSource({ type: "geojson", ... }) で GeoJSON を Source として登録し、view.addLayer({ type: "vector", source, ... }) で描画します。スタイル(色・透明度)は polygon Material が担います。clampToGround: true を指定するとポリゴンが地形の表面にドレープされ、固定の高さで山に埋もれることなく斜面に沿って表示されます。詳細については、Vector Layer を参照してください。

富士山の斜面に、地形に沿ったポリゴンが表示されます。

GeoJSON Map

import ThreeView, { Color } from "@navaramap/three";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
import { TileJsonPlugin } from "@navaramap/three-plugins";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({ shadow: true });
view.addPlugin(plugin);
const tilejson = new TileJsonPlugin();
view.addPlugin(tilejson);
await view.init();
view.addLight({
sun: {},
});
view.atmosphere.date = new Date("2026-07-16T01:00:00Z");
const terrainSource = view.addSource({
type: "quantized-mesh",
url: "https://terrain.reearth.land/cesium-mesh/ellipsoid/{z}/{x}/{y}.terrain",
requestVertexNormals: true,
maxZoom: 18,
});
view.addLayer({
type: "terrain",
source: terrainSource,
terrain: {
castShadow: true,
receiveShadow: true,
},
});
const basemapSource = await tilejson.addSource({
type: "raster-tile",
url: "https://papers.reearth.land/styles/protomaps-light/tilejson.json",
});
view.addLayer({
type: "raster",
source: basemapSource,
});
const geojsonSource = view.addSource({
type: "geojson",
data: {
type: "Feature",
properties: { name: "Area" },
geometry: {
type: "Polygon",
coordinates: [
[
[138.7, 35.425],
[138.7, 35.4],
[138.745, 35.4],
[138.745, 35.425],
[138.7, 35.425],
],
],
},
},
});
view.addLayer({
type: "vector",
source: geojsonSource,
polygon: {
color: new Color().setHex(0xff3b30),
opacity: 0.75,
transparent: true,
},
});
view.setCamera({
lng: 138.75,
lat: 35.5,
height: 6000,
heading: 185, // -180 to 180
pitch: -26, // -180 to 0
roll: 0, // -180 to 180
});