はじめに
こちらの記事で、ArcGIS Onlineでベクトルタイルを表示しました。その際には、tilemapについて最低限必要なファイルを用意しましたが、実際にはズームレベルが大きくなると必要となるファイル数は膨大な数となり、そのファイルを一つ一つ用意することは現実的ではありません。そのため、Node.jsを利用してリクエストパスに応じて動的にレスポンスファイルを作成するコードを作成したいと思います。
ArcGIS Onlineのために必要なindex.json、スタイルファイルroot.jsonもルーティングします。
コードはこちらのGithubページにあります。
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でルーティングするのが良いと思います。というか、それしかないと思います。
結局、解決する方法は、Esriドキュメントにある通り、「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を以下のとおり準備します。
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);
としているのが、ポイントです。
コンフィグ設定は以下です。
{
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
つまりは、ベクトルタイルの場所は、
VectorTileServer/tile/<level>/<row>/<column>.pbf
でなくても良いと思われます。
タイルURL例
https://k96mz.net/tile/1/1/1.pbf
mbtilesも表示出来るか試してみる
index.jsonのtilesを以下のURLにして試してみました。
https://k96mz.net/rest/services/VT/zxy/VTtest/{z}/{x}/{y}.pbf
以下の通り、無事に読み込む、さらに保存することも出来ました。
エラーが発生していた以下のURLでは、下図のように表示されていました。
https://k96mz.net/rest/arcgis-readable/VectorTileServer
タイルへのリクエストが以下のようになっており、違和感を感じます。これが、レイヤを保存できなかった理由かもしれません。
Request URL:
https://geoportal.un.org/arcgis/sharing/proxy?https://k96mz.github.io/20250117tilemap/VectorTileServer/tile/3/4/4.pbf
ContentのNew itemから保存する
こちらの記事で見たように、ContentのNew itemから保存してみます。
VectorTileServerへのパスを以下にします。
https://k96mz.net/rest/services/esriIF/arcgis-readable/VectorTileServer
無事に保存出来ました。
まとめ
Node.jsを利用してリクエストパスに応じて動的にレスポンスファイルを作成するコードを作成しました。そして、そのコードを用いてレンタルサーバでホストし、そのサイトをArcGIS Onlineに保存しました。
ArcGIS Onlineに保存することが出来ないエラーが発生し、解決に手間取りましたが、ドキュメントをきちんと読む事で対応できました。闇雲に色々と試すのではなく、何がエラー原因かを考えながら、ドキュメントを読むことが大切だと感じました。
Reference