はじめに
gzipなし(圧縮なし)のベクトルタイルデータをGitHubでホスティングすることは出来ていました。gzipありのMbtilesデータについては、サーバを立ててホスティングしました。ここでは、gzipありのpbf形式のベクトルタイルをホスティングする方法を検討してみます。
gzipつきのベクトルタイルを作成する
こちらの記事では、 pbfベクトルタイルを圧縮形式にしない方式でデータを作成しました。ここでは、同様のデータを用いて、shpファイルからGeojsonを作成し、そこからベクトルタイルへ変換したいと思います。ソースデータ、ベクトルタイルの構成は同じです。
shpからGeojsonへの変換
ogr2ogr -f GeoJSON coastl.geojson ne_110m_coastline.shp
ogr2ogr -f GeoJSON landmass.geojson ne_110m_land.shp
ogr2ogr -f GeoJSON riverl.geojson ne_110m_rivers_lake_centerlines.shp
Geojsonからベクトルタイルへの変換
tippecanoe -e VTpractice --no-feature-limit --no-tile-size-limit --drop-rate=1 -Z0 -z4 -L coastl:coastl.geojson -L landmass:landmass.geojson -L riverl:riverl.geojson
--no-tile-compression
を含めないようにして、gzip圧縮として、ベクトルタイルを作成します。
レンタルサーバでホスティング
Linux9のレンタルサーバでホスティングをします。フォルダ名を20250506gzipHostingとしました。
こちらの記事のコードを使用します。
以下はmbtiles用のコードなので削除します。
const VTRouter = require("./routes/VT");
app.use("/VT", VTRouter);
configファイルは以下のとおりです。
{
htdocsPath: htdocs
port: 8080
logDirPath: log
morganFormat: tiny
mbtilesDir: mbtiles
privkeyPath: ./key/privkey.pem
fullchainPath: ./key/cert.pem
}
keyフォルダを作成して、cert.pemとprivkey.pemを保存しました。
以下のコマンドで必要な最新版のパッケージをインストールします。
npm install @mapbox/mbtiles config cors express hh-mm-ss hjson morgan winston sqlite3 winston-daily-rotate-file spdy
こちらの記事のindex.htmlを利用します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Display a map</title>
<meta
property="og:description"
content="Initialize a map in an HTML element with MapLibre GL JS."
/>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://unpkg.com/maplibre-gl@4.5.0/dist/maplibre-gl.css"
/>
<script src="https://unpkg.com/maplibre-gl@4.5.0/dist/maplibre-gl.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new maplibregl.Map({
container: "map", // container id
style:
"./download_style.json", // style URL
center: [0, 0], // starting position [lng, lat]
zoom: 1, // starting zoom
maplibreLogo: true,
hash: true,
});
//UI
map.addControl(new maplibregl.NavigationControl(), "bottom-right");
map.addControl(new maplibregl.ScaleControl());
//debug
map.showTileBoundaries = true;
map.showCollisionBoxes = false;
</script>
</body>
</html>
スタイルファイルは上記の記事に含まれているdownload_style.jsonを利用します。
tilesの部分のみURLを変更しました。
{
"version": 8,
"name": "Empty Style",
"metadata": {"maputnik:renderer": "mlgljs"},
"sources": {
"v": {
"type": "vector",
"tiles": [
"https://k96mz.xyz/normalVT/{z}/{x}/{y}.pbf"
],
"minzoom": 0,
"maxzoom": 4
}
},
"sprite": "",
"glyphs": "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "coastl",
"type": "line",
"source": "v",
"source-layer": "coastl",
"maxzoom": 5
},
{
"id": "landmass",
"type": "fill",
"source": "v",
"source-layer": "landmass",
"maxzoom": 5,
"paint": {"fill-color": "rgba(195, 176, 176, 1)"}
},
{
"id": "riverl",
"type": "line",
"source": "v",
"source-layer": "riverl",
"maxzoom": 5,
"paint": {"line-color": "blue", "line-width": 2}
}
],
"id": "dsyugj9"
}
地図を表示する
gzip圧縮なし
まずは、gzip圧縮なしで表示できるか試します。(現在はサーバを立てていないので、アクセスできません。)
https://k96mz.xyz/index.html
無事に表示出来ました。
gzip圧縮あり
次にgzip圧縮ありの地図を表示します。
https://k96mz.xyz/index_gzip.html
Status Codeが200 OKとなっているにも関わらず、地図が表示されません。
以下のメッセージがでています。
"Unable to parse the tile at https://k96mz.xyz/gzipVT/0/0/0.pbf, please make sure the data is not gzipped and that you have configured the relevant header in the server"
ベクトルタイル (.pbf) が gzip 圧縮されているのに、HTTP レスポンスに Content-Encoding: gzip ヘッダーが設定されていないことが原因のエラーです。
そのため、以下のとおり設定します。
app.use(express.static(htdocsPath, {
setHeaders: (res, path) => {
if (path.endsWith('.pbf')) {
res.setHeader('Content-Encoding', 'gzip'); // 重要
res.setHeader('Content-Type', 'application/vnd.mapbox-vector-tile');
}
}
}));
最終的なコードは以下のとおりです。
// Set Module
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");
const fs = require("fs");
const spdy = require("spdy"); //for https
// Configure constant
const port = config.get("port");
const htdocsPath = config.get("htdocsPath");
const morganFormat = config.get("morganFormat");
const logDirPath = config.get("logDirPath");
const privkeyPath = config.get("privkeyPath");
const fullchainPath = config.get("fullchainPath");
// 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());
},
};
// Middleware
const app = express();
app.use(cors());
// const VTRouter = require("./routes/VT");
app.use(
morgan(morganFormat, {
stream: logger.stream,
})
);
// app.use(express.static(`${__dirname}/${htdocsPath}`));
app.use(express.static(htdocsPath, {
setHeaders: (res, path) => {
if (path.endsWith('.pbf')) {
res.setHeader('Content-Encoding', 'gzip'); // 重要
res.setHeader('Content-Type', 'application/vnd.mapbox-vector-tile');
// res.setHeader('Access-Control-Allow-Origin', '*'); // CORS対策
}
}
}));
// app.use("/VT", VTRouter);
// for http
// app.listen(port, () => {
// console.log(`Starting server at port ${port}`);
// });
// for https
spdy
.createServer(
{
key: fs.readFileSync(privkeyPath),
cert: fs.readFileSync(fullchainPath),
},
app
)
.listen(port);
無事に表示されました。
以下も正常に反映されています。
content-encoding:gzip
content-type:application/vnd.mapbox-vector-tile
まとめ
gzipありのpbf形式のベクトルタイルをホスティングする方法をまとめました。HTTP レスポンスに Content-Encoding: gzip ヘッダーを設定することが重要です。
Reference