ArcGIS Onlineでベクトルタイルを読み込めるコードを作る

# はじめに
[こちらの記事](https://qiita.com/k96mz/items/e50a72bdbf8c4f76a227)で、ArcGIS Onlineでベクトルタイルを表示しました。その際には、tilemapについて最低限必要なファイルを用意しましたが、実際にはズームレベルが大きくなると必要となるファイル数は膨大な数となり、そのファイルを一つ一つ用意することは現実的ではありません。そのため、Node.jsを利用してリクエストパスに応じて動的にレスポンスファイルを作成するコードを作成したいと思います。
ArcGIS Onlineのために必要なindex.json、スタイルファイルroot.jsonもルーティングします。
コードはこちらの[Githubページ](https://github.com/k96mz/20250123arcgis-readable)にあります。

## Map Viewer Classicでレイヤが保存できない問題
レンタルサーバにおいて、Map Viewer Classicでレイヤが保存できませんでした。そこで、レンタルサーバにおいても、index.json、スタイルファイルroot.jsonについて、リクエストパスに対してルーティングは行わず直接パスを用意することで、ArcGIS Onlineでのレイヤ保存時のエラーがなくなると考えました。しかし、Githubとレンタルサーバでは以下の挙動の違いがあるため、結局Node.jsのexpressでルーティングすることにしました。

### Github
ファイルが指定されていない場合は、デフォルトでまずはindex.htmlを探し、index.htmlがない場合には、index.jsonを返します。しかし、root.jsonはデフォルトでは返りません。ただ、root.jsonはindex.jsonの中でパスを定義されているのでデフォルトで返らなくても問題なさそうです。

### レンタルサーバ
レンタルサーバでは、index.jsonは自動で返りません。そのため、Node.jsのexpressでルーティングするのが良いと思います。というか、それしかないと思います。

:::note info
結局、解決する方法は、[Esriドキュメント](https://developers.arcgis.com/rest/services-reference/enterprise/vector-tile-service/)にある通り、「rest/services」をURLに含めることでした。
そうすることで、Map Viewer Classicで保存が出来ました。さらに、ContentのNew itemから保存することも出来ました。その方法を以下、記載していきます。
:::

# 環境
### レンタルサーバ
OS: Rocky Linux 8
メモリ：　1GB
ストレージ: 25GB SSD
node version: v16.20.2 ※nvmで管理
npm version: 8.19.4

# サーバーの準備

apps.jsを以下のとおり準備します。

```javascript: apps.js
const config = require('config');
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');

// config constants
const morganFormat = config.get('morganFormat');
const htdocsPath = config.get('htdocsPath');
const port = config.get('port');
const logDirPath = config.get('logDirPath');

// logger configuration
const logger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
    new DailyRotateFile({
      filename: `${logDirPath}/server-%DATE%.log`,
      datePattern: 'YYYY-MM-DD',
    }),
  ],
});

logger.stream = {
  write: message => {
    logger.info(message.trim());
  },
};

// app
const app = express();
var VTRouter = require('./routes/VT'); //tiling
var esriIFRouter = require('./routes/esriIF'); //esri interface (tilemap, etc..)
app.use(cors());
app.use(
  morgan(morganFormat, {
    stream: logger.stream,
  })
);
app.use(express.static(htdocsPath));
app.use('/rest/services/VT', VTRouter);
// app.use('/esriIF', esriIFRouter); //esri interface
app.use('/rest/services/esriIF', esriIFRouter); //esri interface

//for http
// app.listen(port, () => {
//   console.log(`Running at Port ${port} ...`);
// });

// for https
const fs = require('fs');
const spdy = require('spdy'); //for https
const privkeyPath = config.get('privkeyPath'); //for https
const fullchainPath = config.get('fullchainPath'); //for https
spdy
  .createServer(
    {
      key: fs.readFileSync(privkeyPath),
      cert: fs.readFileSync(fullchainPath),
    },
    app
  )
  .listen(port);
```

app.use('rest/services/esriIF', esriIFRouter);
としているのが、ポイントです。

コンフィグ設定は以下です。

```text: config/default.hjson
{
 morganFormat: tiny
 htdocsPath: htdocs
 privkeyPath: ./key/privkey.pem 
 fullchainPath: ./key/cert.pem
 logDirPath: log
 port: 443
 mbtilesDir: mbtiles
 esriDir: esri
 esri-tilemap-min:{
  arcgis-readable: 0
 }
 esri-tilemap-max:{
  arcgis-readable: 8
 }
}
```

ポート番号は443にしました。Tilemapで使うために、ここで読み込むデータセット(arcgis-readable)とその最大ズームレベル、最小ズームレベルを指定します。
mbtilesDir: mbtiles
の記載も追記しています。

# esri関係インターフェース
routes/esriIF.jsを準備しました。


# JSONファイルの準備
次に、esri/arcgis-readableというフォルダを作って、その中にindex.jsonとstyle.jsonを準備します。

style.jsonのurl部分は以下の通りとしています。
"url": "https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer",

index.jsonのtiles部分は以下のとおりです。
"tiles": [
    "https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/tile/{z}/{x}/{y}.pbf"
  ],


# レンタルサーバ
サーバに入り、その後cloneします。

```
git clone https://github.com/k96mz/20250123arcgis-readable.git
```

その他、以下を実行します。

・npm iでモジュールをインストール
・./key/privkey.pem と./key/cert.pemに該当ファイルを置く

```
sudo ~/.nvm/versions/node/v16.20.2/bin/node apps.js
```
とすると、以下のURLからindex.htmlファイルを見ることができます。
https://k96mz.net

### 確認
ベクトルタイル：
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/tile/{z}/{x}/{y}.pbf
例
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/tile/1/1/1.pbf


スタイルファイル：
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/resources/styles
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/resources/styles/root.json
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/resources/styles/root.json?f=pjson

ベクトルタイルサーバ情報：
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/index.json
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer?f=json

tilemap：
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/tilemap/3/0/0/32/32
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/tilemap/4/0/0/32/32
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer/tilemap/6/32/64/32/32

# ArcGIS Onlineでみる
Map Viewer Classicにおいて、以下のURLだと読み込み、及び保存が出来ました！
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer


index.jsonのtiles部分を、以下に変更して試したところ、問題なく読み込みと保存が出来ました。つまり、外部の静的サイトに置かれたタイルを使用したレイヤも保存できることが分かりました。
https://k96mz.net/tile/{z}/{x}/{y}.pbf

:::note info
つまりは、ベクトルタイルの場所は、
```
VectorTileServer/tile/<level>/<row>/<column>.pbf
```
でなくても良いと思われます。
:::


タイルURL例
https://k96mz.net/tile/1/1/1.pbf

以下が表示されたタイルURL例です。
![スクリーンショット 2025-01-28 10.57.40.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/59ff9160-134a-e024-81f7-4ce86cc9e25a.jpeg)

### mbtilesも表示出来るか試してみる
index.jsonのtilesを以下のURLにして試してみました。
https://k96mz.net/rest/services/VT/zxy/VTtest/{z}/{x}/{y}.pbf

以下の通り、無事に読み込む、さらに保存することも出来ました。

![スクリーンショット 2025-01-28 17.47.22.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/0e5090f1-f628-c1a3-d03f-37e388aa3d1d.jpeg)

エラーが発生していた以下のURLでは、下図のように表示されていました。
https://k96mz.net/rest/arcgis-readable/VectorTileServer

![スクリーンショット 2025-01-24 14.07.00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/a8d8b390-1139-21df-cb37-c6b7d586effd.jpeg)

タイルへのリクエストが以下のようになっており、違和感を感じます。これが、レイヤを保存できなかった理由かもしれません。
Request URL:
https://geoportal.un.org/arcgis/sharing/proxy?https://k96mz.github.io/20250117tilemap/VectorTileServer/tile/3/4/4.pbf

## ContentのNew itemから保存する
[こちらの記事](https://qiita.com/k96mz/items/e50a72bdbf8c4f76a227#%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BFurl%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)で見たように、ContentのNew itemから保存してみます。
VectorTileServerへのパスを以下にします。
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer

無事に保存出来ました。

![スクリーンショット 2025-01-28 10.18.07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/ff135b54-135c-83b3-76e7-2cdf54ac15d8.jpeg)





# まとめ
Node.jsを利用してリクエストパスに応じて動的にレスポンスファイルを作成するコードを作成しました。そして、そのコードを用いてレンタルサーバでホストし、そのサイトをArcGIS Onlineに保存しました。
ArcGIS Onlineに保存することが出来ないエラーが発生し、解決に手間取りましたが、ドキュメントをきちんと読む事で対応できました。闇雲に色々と試すのではなく、何がエラー原因かを考えながら、ドキュメントを読むことが大切だと感じました。

# Reference

https://qiita.com/k96mz/items/e50a72bdbf8c4f76a227

https://github.com/k96mz/20250123arcgis-readable