これは MIERUNE AdventCalendar 2024 1日目の記事です!
MapServerとは?
提供元:University of Minnesota、MIT、Wikimedia Commons(https://commons.wikimedia.org/w/index.php?curid=10007423)
MapServerは、オープンソースの地図配信ソフトウェアで、地理空間データを処理・可視化するために使用されます。1990年代後半に、アメリカのミネソタ大学ツインシティー校によって開発され、GIS(地理情報システム)の分野で広く利用されてきました。
MapServerの主な特徴の一つは、さまざまなフォーマットの地理空間データを動的に処理し、ラスタータイルとして動的に配信できる能力です。この配信能力は、OGC(Open Geospatial Consortium)が定めた国際標準規格であるWMS(Web Map Service)に対応している点で特に注目されてました。
WMSは、地理空間データをインターネット上で共有するための標準規格であり、リクエストに応じて地図画像や地理情報データを提供する仕組みを提供します。MapServerは、このWMS規格に準拠して動作することで、さまざまなクライアントソフトウェアやシステムと連携し、地理空間データの効率的な配信を実現します。
背景の変化とMapServerの現状
近年はブラウザの処理能力の向上により、地図データの描画や処理をクライアント側で行うことが可能になり、地図配信のアーキテクチャが大きく変化してきました。特に、WebGLを活用したライブラリや、ベクトルタイル形式の普及により、サーバーはデータ配信に特化し、地図のスタイリングや描画はクライアント側で行うケースが増えています。
また、MapServerのようにサーバー側でラスタータイルを動的に生成して配信するモデルの利用は減少傾向にあります。現在では、ラスタータイルは動的に生成されるのではなく、事前に生成された静的なタイルとしてキャッシュやCDNを通じて配信される傾向が強まっています。このアプローチは、パフォーマンスやスケーラビリティの観点で優れているためです。
しかし、古いWebGISシステムやレガシーな環境では、依然としてMapServerが利用されているケースが多く見られます。ちなみに、現在でもMapServerは継続的にアップデートされています。
MapServerを動かしてみる
PCを汚したくないので、今回はドッカーでMapServerを動かしてみます。ディレクトリ構成は以下になります。docker-compose.ymlとMapfile(sample.map
)を用意します。
.
├── mapfile # マップファイルを格納するディレクトリ
│ ├── data
│ │ ├──(ここにデータを入れる)
│ └── sample.map
└── docker-compose.yml
ドッカーイメージはこちらになります。
しかし、私のM1 Mac 環境だと動かず、代わりにこちらのイメージを使用しました。
docker-compose.ymlに書き込みます。
services:
mapserver:
# M1 Macの場合を想定してるので、もし動かない場合は`mapserver/mapserver`のimageを試してください
image: camptocamp/mapserver:8.0-gdal3.7-arm64
container_name: mapserver
ports:
- "8080:80"
volumes:
- ./mapfile:/etc/mapserver
environment:
- MS_MAPFILE=/etc/mapserver/sample.map
- MS_DEBUGLEVEL=5
- MS_MAP_NO_PATH=true
使用するデータ
データ名 | データタイプ | データ形式 |
---|---|---|
Airports | ポイント | Shape |
Roads | ライン | Shape |
Urban Areas | ポリゴン | Shape |
Ocean Bottom | ラスター | GeoTiff |
先ほどのディレクトリに以下のようにデータを格納します。
.
└── mapfile
├── data
│ ├── ne_10m_admin_0_countries
│ │ ├── ne_10m_admin_0_countries.cpg
│ │ ├── ne_10m_admin_0_countries.dbf
│ │ ├── ne_10m_admin_0_countries.prj
│ │ ├── ne_10m_admin_0_countries.shp
│ │ └── ne_10m_admin_0_countries.shx
│ ├── ne_10m_airports
│ │ ├── ne_10m_airports.cpg
│ │ ├── ne_10m_airports.dbf
│ │ ├── ne_10m_airports.prj
│ │ ├── ne_10m_airports.shp
│ │ └── ne_10m_airports.shx
│ ├── ne_10m_roads
│ │ ├── ne_10m_roads.cpg
│ │ ├── ne_10m_roads.dbf
│ │ ├── ne_10m_roads.prj
│ │ ├── ne_10m_roads.shp
│ │ └── ne_10m_roads.shx
│ ├── OB_LR
│ │ ├── OB_LR.prj
│ │ ├── OB_LR.tfw
│ │ └── OB_LR.tif
└── sample.map
Mapfileを書いてみる
MapServerはMapfile(.map
)を使用して、各レイヤーの描画スタイル(色、ラベル、シンボルなど)を細かく設定できます。
また、VSCodeの場合、以下の拡張機能を使うとMapfileのテキストが見やすくなります
MAP
NAME "TestMapServer"
UNITS DD
EXTENT -180 -90 180 90
PROJECTION
"init=EPSG:4326"
END
WEB
METADATA
"wms_title" "Test WMS Server"
"wms_onlineresource" "http://localhost:8080/cgi-bin?/mapserv?map=/map/m1.map"
"wms_srs" "EPSG:3857 EPSG:4326"
"wms_enable_request" "*"
END
END
OUTPUTFORMAT
NAME "png"
DRIVER AGG/PNG
MIMETYPE "image/png"
IMAGEMODE RGBA
EXTENSION "png"
TRANSPARENT ON
END
# シンボルの形状の定義
SYMBOL
NAME "circle"
TYPE ELLIPSE
POINTS 1 1 END
FILLED TRUE
END
# Point Layer
LAYER
NAME "ne_10m_airports"
TYPE POINT
STATUS ON
DATA "data/ne_10m_airports/ne_10m_airports.shp"
METADATA
"wms_title" "ne_10m_airports"
END
CLASS
NAME "ne_10m_airports"
STYLE
COLOR 255 0 0
SYMBOL "circle" # シンボルの形状を指定(デフォルト設定が必要)
SIZE 12
END
END
END
# LineLayer
LAYER
NAME "ne_10m_roads"
TYPE LINE
STATUS ON
DATA "data/ne_10m_roads/ne_10m_roads.shp"
METADATA
"wms_title" "ne_10m_roads"
END
CLASS
NAME "ne_10m_roads"
STYLE
OUTLINECOLOR 0 255 0
WIDTH 2
END
END
END
# Polygon Layer
LAYER
NAME "ne_10m_admin_0_countries"
TYPE POLYGON
STATUS ON
DATA "data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp"
METADATA
"wms_title" "ne_10m_admin_0_countries"
END
CLASS
NAME "ne_10m_admin_0_countries"
STYLE
COLOR 255 175 0
OUTLINECOLOR 0 0 0
END
END
END
# GeoTIFF Layer
LAYER
NAME "OB_LR"
TYPE RASTER
STATUS ON
DATA "data/OB_LR/OB_LR.tif" # GeoTIFFファイルのパス
METADATA
"wms_title" "OB_LR"
END
PROJECTION
"init=EPSG:4326" # GeoTIFFが使用する座標系を指定
END
CLASS
NAME "OB_LR"
END
END
END
Mapfileを書いたところでドッカーを起動します。
docker compose up -d
起動後、以下のURLにブラウザでリクエストしてみます。
http://localhost:8080/cgi-bin?/mapserv?map=/map/sample.map&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities
するとMapServerがWMS(Web Map Service)のGetCapabilitiesリクエストを処理し、サービスの詳細情報を返します。この情報には、提供される地図データのレイヤー構成やサポートされる投影法、リクエスト可能な形式などが含まれます。
返されるXML形式のレスポンスは、クライアントソフトウェアによって解釈され、地図データの利用が可能になります。
クライアントで描画してみる
QGIS
ブラウザパネルの「WMS/WMTS」の項目を右クリックして「新規接続」を選択します。
名称は任意の文字列を入れ、URLに以下のリクエストURLを記入して「OK」を選択します
http://localhost:8080/cgi-bin?/mapserv?map=/map/sample.map
ブラウザパネルの「WMS/WMTS」のトグルを展開すると先ほど登録したものが表示されるので、さらにトグルを展開すると、Mapfileに記入したレイヤー群が表示されるので、ダブルクリックすることでレイヤーパネルに追加できます。
地図に描画された様子です。
MapLibre GL JS
公式のこちらのexamplesを参考にして描画してみます。
&LAYERS=
の部分にMapfileで定義したレイヤー名をカンマ区切りで指定することとで、タイル画像として描画されます。ちなみに、後に書いたレイヤーの方が上に重なるようになっています。
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
// 地図の表示
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
sources: {
'wms-source': {
type: 'raster',
tiles: [
'http://localhost:8080/cgi-bin?/mapserv?map=/map/sample.map&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=OB_LR,ne_10m_admin_0_countries,ne_10m_roads,ne_10m_airports&STYLES=&CRS=EPSG:3857&BBOX={bbox-epsg-3857}&WIDTH=256&HEIGHT=256&FORMAT=image/png',
],
tileSize: 256,
},
},
layers: [
{
id: 'wms-layer',
type: 'raster',
source: 'wms-source',
paint: {
'raster-opacity': 1.0,
},
},
],
},
center: [135.41533470153811, 34.736675273476976],
zoom: 4,
});
// NavigationControl
map.addControl(new maplibregl.NavigationControl(), 'top-right');
MapLibreで描画した様子です。
Chromeのコンソール画面でタイルリクエストを確認すると、複数のレイヤーが一枚のタイル画像として返してくれてるのが確認できます。
おわりに
今回MapServerを触るにあたり、ネット上に公開されている情報が少なく、環境を整えるのに苦戦しました。冒頭でも書きましたが、サーバーサイドで動的にラスタータイルを生成するMapServerのアプローチが時代のニーズに合わなくなってきているためであると考えられます。
明日は@nbayashiさんによる記事です!お楽しみに!!