😀

MapServerのWCSサーバでタイムアウトまで待たされる場合の対応

2020/10/27に公開

はじめに

まずそういうおかしい人はいらっしゃらないと思いますが、WCSサービスで、全国網羅した10mメッシュのラスタを配信しようとしているとします。

MapServerはWCSサーバになれるので、これを使うとするのは、おかしな話ではありません。

しかしここで、ひっかかるところがあります。

前振り

具体的な症状

  • QGISからWCSレイヤの追加をしようとすると、操作できない状態に陥る
  • 1分程度以上経った後、QGISで「ネットワークがタイムアウトしました.受信したデータは不完全かもしれません」というエラーが出て、操作不能から復帰する
  • MapServerのERRORFILEを取っていると、この時点でmsDrawRasterLayerLow(<レイヤ名>): entering.だけが出る(mapserv request processing time等が出ない)

原因

HTTPのログを見ると QGISからGetCoverageリクエストが出ていること、それを受けてMapServerが動作しているのは確かなのですが、QGISからのリクエストが
BBOX=122.875,20.41666666666419871,154.0000000000030127,45.58333333333330017&WIDTH=10&HEIGHT=10(CRSはEPSG:4612)となっていました。

このリクエストは、日本全体(280124x226499ピクセル)のデータを舐めて、10x10ピクセルでリサンプリングした結果を返せ、と求めているものです。

HTTPサーバは、QGISの無茶振りなリクエストを受け付けて、正直にMapServerを起動させてしまい、無茶振りなのでMapServerはえんえんとデータ取得を続け、HTTPサーバが途中であきらめて 504 Gateway Timeout をQGISに返して、「タイムアウトしました」と表示された、というわけです。

対応方針

BBOXが広範囲になる無茶振りは毅然と断ることにします。

MAXSCALEDENOMが効かない

LAYERに対してMAXSCALEDENOMを設定すると、指定した縮尺(の逆数)より小縮尺(広範囲)では表示しないようにできます。この指定はWMSでは動いたので、これでOKだろうと思っていましたが、効きませんでした。

対応策

ソースを見る

なんでMAXSCALEDENOMが効かないのか、ソースを見て原因を探ってみましょう。

maxscaledenomのチェックをしてくれない

GetCoverageリクエストの処理は、mapwcs.cmsWCSGetCoverage()で行っています。

この関数から、mapraster.cにあるmsDrawRasterLayerLow()を呼んで、カバレッジになるデータを取得しています。

この関数の中で、maxscaledenomが使われている箇所は次のようになっています。

  if(map->scaledenom > 0) {
    if((layer->maxscaledenom > 0) && (map->scaledenom > layer->maxscaledenom)) {

なお、map->scaledenomはデフォルトでは-1になっています。そして、mapwcs.cではmap->scaledenomは設定されていません。

ということで、maxscaledenomのチェックは行われません。

GEOWIDTH で対応する

mapraster.cの上記の箇所の直後に次のような箇所があります。

  if(layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) {
    if((layer->maxgeowidth > 0) && ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) {

これならextentは設定されているので使えます。

ということで、マップファイルに次のものを追加します。

  LAYER
    ...
    GEOWIDTH 1
  END

これで、(元データの空間参照系がEPSG:4612等の場合には)リサンプリング元のデータのサイズの最大値が緯度1度×経度1度(10mメッシュの場合12000×8000ピクセル)となります。

MAXSCALEDENOMは入れちゃダメ

先ほど示したmapraster.cの一部をもういちど出します。

  if(layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) {
    if((layer->maxgeowidth > 0) && ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) {

このコードから、MAXSCALEDENOMが設定されている場合にはMAXGEOWIDTHのチェックが行われないことが分かります。

GEOWIDTHとMAXSCALEDENOMは同居できないうえ、MAXSCALEDENOMが優先されることになります。そのうえWCSでは必ずMAXSCALEDENOMのチェックは行われません。

少なくとも現時点では、WCSではMAXSCALEDENOMを入れてはダメ、ということになります。

おわりに

MapServerのWCSサーバを使用する際に、タイムアウトの原因となる、広範囲のBBOX指定に対して、サーバが断る方法として、単独でMAXGEOWIDTHを設定することを示しました。

WCSサーバとして使う場合に特有の話です。WMSではMAXSCALEDENOMを使った方が良いかと思います。

また、こういった問題がある以上、WCSとWMSの両方に対応するレイヤは作らない方が良いと思います(もともとそんなことしないと思いますが)。

本記事のライセンス

クリエイティブ・コモンズ・ライセンス
この記事は クリエイティブ・コモンズ 表示 4.0 国際 ライセンス の下に提供されています。

Discussion