6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

あ〜CesiumJSをSSR環境でも動かしたいね〜

Last updated at Posted at 2025-01-15

正確に言えば、あ〜SSR環境で動作するアプリケーション内でもCesiumJSを利用したいね〜です。

使用環境
Svelte v5.0.0
SvelteKit v2.0.0
pnpm v9.14.4

TL;DR

ビルド時にvite-plugin-static-copyを用いて、クライアントサイドで使用するパッケージを静的ファイルにコピーする。
試してみたいくらいだったらCDN使えば良い。

はじめに

CesiumJSはロード時にパッケージ内の静的ファイル(AssetsやWorkersなど)をクライアントサイド(ブラウザ)で動的に読み込む必要がある。このため、CesiumJS自体はサーバーサイドレンダリング(SSR)には適さず、多くの場合アプリケーション全体もSSG(静的サイト生成)またはCSR(クライアントサイドレンダリング)環境で動作させることが一般的となる。
これは、CesiumJSがWebGLベースで動作し、大量の静的リソースを効率よく配信する設計だからとも言える。

一方で現代のウェブ開発要件に応じてSSRやハイブリッドレンダリングへの移行も検討されるようになった。例えばAWS Amplifyなどのモダンなホスティング環境では、Lambda@Edgeを活用したSSRによる初期表示速度向上とエッジコンピューティングによる効率的な配信が容易に実現可能となる。このような仕組みを活用することで、CesiumJSの3D描画機能と最新ウェブ技術の利点を両立することができる。

この記事では、アプリケーション全体をSSR環境で動作させつつ、クライアントサイドでのロード・動作する必要があるCesiumJSとの適合方法について解説する。

やらないこと

  • AWS Amplifyへの詳細なデプロイ手順

準備

Svelte5のプロジェクトを作成し、ローカル環境でCesiumJSが動作するところまで

プロジェクト作成

npx sv create svelte5-cesium

選択したオプション

┌  Welcome to the Svelte CLI! (v0.6.11)
│
◇  Which template would you like?
│  SvelteKit minimal
│
◇  Add type checking with Typescript?
│  Yes, using Typescript syntax
│
◆  Project created
│
◇  What would you like to add to your project? (use arrow keys / space bar)
│  tailwindcss
│
◇  Which plugins would you like to add?
│  none
│
◇  Which package manager do you want to install dependencies with?
│  pnpm

パッケージマネージャにはpnpmを採用、tailwindcssも入れた。

pnpm run devで起動すると↓が表示できる

image.png

CesiumJSの導入

src/routes/+page.svelteに以下を記載する。

<script lang="ts">
  import { onMount } from "svelte";
  import { UrlTemplateImageryProvider, Viewer, Cartesian3 } from "cesium";
  import("cesium/Build/Cesium/Widgets/widgets.css");

  // CesiumJSの静的リソース(AssetsやWorkers)をロードするためのベースURL
  const baseUrl = "/node_modules/cesium/Build/Cesium/";

  onMount(() => {
    // CesiumJSの静的リソースのベースURLをグローバル変数に設定
    (window as any).CESIUM_BASE_URL = baseUrl;

    // Cesium Viewerインスタンスの初期化(地図コンテナを指定)
    const viewer = new Viewer("cesiumContainer", {});

    // 地理院地図の衛星画像プロバイダー
    const gsiSeamless = new UrlTemplateImageryProvider({
      url: "https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg",
    });

    // 作成した画像プロバイダーをCesium Viewerに追加
    viewer.imageryLayers.addImageryProvider(gsiSeamless);

    // カメラの初期位置
    viewer.camera.setView({
      destination: Cartesian3.fromDegrees(139.767052, 35.681167, 10000),
    });
  });
</script>

<div id="cesiumContainer" class="cesiumContainer h-screen w-screen"></div>

すると画面いっぱいの地図画面が表示される。
image.png

地形は適用していないのでのっぺら

AWS Amplifyにホスティング

AWS Amplify用にビルドしてとりあえずホスティングしてみる。

ビルド設定

AWS Amplify用にビルドする必要があるので、svelte.config.js にてadapterをamplify-adapterに変更する。

import amplifyAdapter from 'amplify-adapter';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	preprocess: vitePreprocess(),

	kit: {
		adapter: amplifyAdapter()
	}
};

export default config;

pnpm run devでビルドするとPJルートのbuild配下にビルドファイルが生成されているはず(output先を他に設定していたら別)

image.png

デプロイ

GitHubにリポジトリを作成してデプロイしてみる。

リポジトリを作成後、AWS Amplifyのマネコンからデプロイの設定を行う。

リポジトリ/ブランチを選択後の「アプリケーションの設定」にて
image.png
出力ディレクトリをビルドにはビルド設定でoutputが出力されたbuildディレクトリを指定する。

当然ビルドに失敗する。

pnpmがないので、pnpm installを実行する前にインストールする必要がある。

最終的なコード

version: 1
frontend:
    phases:
        preBuild:
            commands:
                - npm install -g pnpm@9.14.4
                - pnpm install
        build:
            commands:
                - pnpm run build
    artifacts:
        baseDirectory: build
        files:
            - '**/*'
    cache:
        paths:
            - 'node_modules/**/*'
  • Cesium ionを使用するため環境変数にトークンを埋め込みたい場合は、ホスティング > 環境変数に設定後、ビルド設定にでも含めてあげる必要がある
  • キャッシュのパスに.pnpm-store/**/*を含めることで、pnpmらしい更なるビルド時間短縮と安定性向上が期待できる

デプロイ結果

デプロイ済みのドメインに接続してみると…
image.png
エラーが出ている。

コンソールにはCeisumJSのライブラリに含まれている静的ファイルが取得できなかった旨のエラーが表示されている。
スクリーンショット 2025-01-16 4.36.51.png

対処法

その1

上記のエラーはクライアントサイドでCeiumJSが静的ファイルを読み込もうとしたものの、該当のパッケージの静的ファイルがクライアントサイドに存在しないことに起因している。
そこでビルド時にvite-plugin-static-copyを用いて、クライアントサイドで使用するパッケージを静的ファイルにコピーする。
https://www.npmjs.com/package/vite-plugin-static-copy

vite.config.tsにて以下のように設定することで、ビルドファイルstaticに指定したファイル(今回はcesiumパッケージの各ファイル)を含めることができる。

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy';

export default defineConfig({
	plugins: [
		sveltekit(),
		viteStaticCopy({
			targets: [
				{
					src: 'node_modules/cesium/Build/Cesium/Workers/**/*',
					dest: 'Cesium/Workers'
				},
				{
					src: 'node_modules/cesium/Build/Cesium/Assets/**/*',
					dest: 'Cesium/Assets'
				},
				{
					src: 'node_modules/cesium/Build/Cesium/Widgets/**/*',
					dest: 'Cesium/Widgets'
				}
			]
		})
	],
});

pnpm run buildした結果、build > static配下にCesiumが存在することがわかる。
image.png

クライアントサイドの静的ファイルにCesiumJSのパッケージが含まれても、CESIUM_BASE_URLがそのままだと参照できないことに変わりはない。

そこで環境ごとにCESIUM_BASE_URLを切り替える必要がある。

// CesiumJSの静的リソース(AssetsやWorkers)をロードするためのベースURL
const baseUrl = import.meta.env.DEV
    ? "/node_modules/cesium/Build/Cesium/"
    : "/Cesium/";

ここではvite-plugin-static-copyで指定したビルドファイルに含めるディレクトリ名を指定する。

すると開発環境ではインストールしたnode_moduleを、検証・本番環境ではビルド時に含められていたパッケージを使用することができる。

デプロイすると無事見られるようになる。

image.png

その2

CESIUM_BASE_URLにCDN URLを指定する。

const baseUrl = "https://cdn.jsdelivr.net/npm/cesium@1.125.0/Build/Cesium/";

まとめ

SSR環境で動作するアプリケーションにおいて、クライアントサイドでCesiumJSのパッケージを読み込むとき、CDNを直で読み込めば終わりだが以下の観点から、私はvite-plugin-static-copyの使用を推したい。

  1. 開発環境と検証・本番環境でのCesiumJS動作環境のバージョン差異が防げる
  2. CDN依存性によるリスク(プロバイダー障害やキャッシュ問題)への対策

一方で、CDNの特性上パフォーマンスの観点やビルド成果物の軽量化においてはCDNのメリットは挙げられる。
ちょっと検証するくらいだったら正直CDNでいいかなとも思う。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?