トップOpenStreetMap > OSM Mapnikスタイルファイルの修正

OSM Mapnikスタイルファイルの修正

1. データベースの更新

個人利用では精々数か月に1回の更新で十分だろう。データベース名にダウンロードした年月日を付加しておこう。

OSMファイルの特定の情報だけを利用する場合、bz2形式のファイルの方が扱いやすいが、 タイル地図作成ではデータベースへのインポートが必須となるため、pbf形式の方が望ましい。

pgAdmin IIIを起動して、osmYYYYMMDD という名前のデータベースを生成しておき、次のようしてPostGIS対応にする。

CREATE EXTENSION postgis;

4GBメモリ搭載パソコンでは、-C オプションのキャッシュサイズは精々 1200MB 程度にしておくのが無難であろう。

c:\osm2pgsql\x64>set pgpassword=*******
c:\osm2pgsql\x64>osm2pgsql --slim -d osm2015**** -C 1200 -U postgres -H localhost -S c:\osm2pgsql\default.style c:\osm\japan-latest.osm

本来、pdf形式のファイルでもいいはずであるが、このパソコンでは次のようなエラーとなった。

[2015.8.25]pdf ではなく pbf である。pbfに対してはエラーは起きなかった。

Reading in file: c:\osm\japan-latest.osm.pdf
error while opening file c:\osm\japan-latest.osm.pdf

pz2ファイルを解凍したjapan-latest.osmではこのエラーは出ないので、pdfの解凍に問題があるようだ。 当面は、pz2ファイルをダウンロードする。

海岸線データなどは当面2015年2月にダウンロードして、 C:\mapnik-stylesheets-master\world_boundaries に展開したものを取り敢えずはそのまま使うことにする。 このディレクトリにある情報も年月が経ったら、最新のものをダウンロードし直す必要がある。

generate_xml.pyというPythonスクリプトファイルを使用して、 構築中の環境に合わせたMapnikスタイルファイルというXML形式の設定ファイル「my_osm.xml」を生成する。

C:\mapnik-stylesheets-master>python generate_xml.py osm2015****.xml my_osm.xml --host localhost --port 5432 --dbname osm --user postgres --password {パスワード} 

2.フォントの変更

fontset-settings.xml.inc に最初は次のように設定されている。

<FontSet name="book-fonts">
  <Font face-name="DejaVu Sans Book" />
  <Font face-name="unifont Medium" />
</FontSet>
<FontSet name="bold-fonts">
  <Font face-name="DejaVu Sans Bold" />
  <Font face-name="unifont Medium" />
</FontSet>
<FontSet name="oblique-fonts">
  <Font face-name="DejaVu Sans Oblique" />
  <Font face-name="unifont Medium" />
</FontSet>

少し調べた範囲では、日本語(漢字、カタカナ、ひらかな)のゴシックはサポートされていないようだ。

Windowsパソコンのフォントを使えるようにするのは極めて簡単であることが分かった[2]。

例えば、全てのフォントが使えるようにするには、generates_tiles.py の先頭、すなわち

try:
    import mapnik2 as mapnik
except:
    import mapnik
の次に
mapnik.register_fonts('c:/windows/fonts/')
#for face in mapnik.FontEngine.face_names(): print face
を置けばよい。どのようなフォント名になるかは、上の2行目を一度有効にしてみれば分かる。

my_osm.xml のフォントを次のように変更する。 日本語の見栄えは unifont Medium よりはるかによい。

<FontSet name="book-fonts">
  <Font face-name="DejaVu Sans Book" />
  <Font face-name="Meiryo UI Regular" />
  <Font face-name="unifont Medium" />
</FontSet>
<FontSet name="bold-fonts">
  <Font face-name="DejaVu Sans Bold" />
  <Font face-name="Meiryo UI Bold" />
  <Font face-name="unifont Medium" />
</FontSet>
<FontSet name="oblique-fonts">
  <Font face-name="DejaVu Sans Oblique" />
  <Font face-name="Meiryo UI Italic" />
  <Font face-name="unifont Medium" />
</FontSet>


【別の方法】

Windowsのフォントは c:\Windows\Fonts に置かれている。 ここから c:\mapnik\lib\mapnik\fonts に meiryo.tcc, meiryob.tcc をコピーする。

使用できるフォント名は次のようにして確認できる。

C:\mapnik-stylesheets-master>python -c "from mapnik import FontEngine as e;print
 '\n'.join(e.instance().face_names())"
DejaVu Sans Bold
DejaVu Sans Bold Oblique
DejaVu Sans Book
DejaVu Sans Condensed
DejaVu Sans Condensed Bold
DejaVu Sans Condensed Bold Oblique
DejaVu Sans Condensed Oblique
DejaVu Sans ExtraLight
DejaVu Sans Mono Bold
DejaVu Sans Mono Bold Oblique
DejaVu Sans Mono Book
DejaVu Sans Mono Oblique
DejaVu Sans Oblique
DejaVu Serif Bold
DejaVu Serif Bold Italic
DejaVu Serif Book
DejaVu Serif Condensed
DejaVu Serif Condensed Bold
DejaVu Serif Condensed Bold Italic
DejaVu Serif Condensed Italic
DejaVu Serif Italic
Meiryo Bold
Meiryo Bold Italic
Meiryo Italic
Meiryo Regular
Meiryo UI Bold
Meiryo UI Bold Italic
Meiryo UI Italic
Meiryo UI Regular
unifont Medium

fontset-settings.xml.inc.templateを次のように変更する。

<FontSet name="book-fonts">
  <Font face-name="DejaVu Sans Book" />
  <Font face-name="Meiryo UI Regular" />
  <Font face-name="unifont Medium" />
</FontSet>
<FontSet name="bold-fonts">
  <Font face-name="DejaVu Sans Bold" />
  <Font face-name="Meiryo UI Bold" />
  <Font face-name="unifont Medium" />
</FontSet>
<FontSet name="oblique-fonts">
  <Font face-name="DejaVu Sans Oblique" />
  <Font face-name="Meiryo UI Italic" />
  <Font face-name="unifont Medium" />
</FontSet>

3.表示のカスタマイズ

修正は広範囲になるため、少しずつ、確認しながら行う。

OSM公式サーバーで提供されている地図を OSM標準地図[3]と呼ぶことにする。 現在、このMapnikスタイルファイルは CartoCSS で作成されており、osm.xml で生成されるものとは 大幅に異なる。この mapnik-win-v2.2.0.zip に含まれる osm.xml で生成される地図をここでは デフォルト地図と呼ぶことにする。

主なる変更点(カスタマイズのポイント)は次の通りである。

  1. 色はパステルカラーを基調として、目が疲れない配色とする。
  2. カーナビではなく、徒歩ナビに重点を置いた地図とする。例えば、駐車場は目立たないようにする。
  3. デフォルト地図およびOSM標準地図では高圧線が目立つが、線を細く、薄い色に変え、目立たなくする。
  4. デフォルト地図では線路に「○○鉄道□□線」といった線名が道路名と同じように繰り返し表示されるが、これは表示しない。 高圧線にも同様の名前が表示されるがこれを止める。
  5. デフォルト地図は標準地図に比較して、文字やアイコンによる表示情報が非常に少ない。 表示情報はOSM標準地図を基準にして、追加、削除する。

高圧線名、鉄道線路名の表示をやめる

デフォルト地図では、高圧線、鉄道線路の線名は <ElseFilter/> 規則で表示されている。 osm.xml の次の部分をコメントにする。表示では、ElseFilterでは何が表示されるか 分かりにくいので極力使わず、Ruleで表示を明示すべきであろう。

<Style name="roads-text-name">
[前略]
<!--
    <Rule>
      <ElseFilter/>
      &maxscale_zoom15;
      &minscale_zoom16;
      <TextSymbolizer size="9" fill="#000" placement="line" fontset-name="book-fonts" halo-radius="1">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <ElseFilter/>
      &maxscale_zoom17;
      &minscale_zoom18;
      <TextSymbolizer size="11" fill="#000" placement="line" fontset-name="book-fonts" halo-radius="1">[name]</TextSymbolizer>
    </Rule>
-->
</Style>

交差点信号機名を表示する

デフォルト地図ではアイコンが表示され、交差点名は表示されない。 OSM標準地図では Zoom14〜16では交差点名、Zoom17〜18では信号機と交差点名が表示される。 他の情報と重なると表示されない。交差点の中央部に他の情報が表示されることは少ないので、 重なりでは、交差点名が非表示となり、信号機のみ表示されるケースが多い。

CartoCSSでは roads.mss で次のように定義しているので、これを参考にする。

  [highway = 'traffic_signals'] {
    [zoom >= 14] {
      text-name: "[name]";
      text-size: 8;
      text-fill: black;
      text-face-name: @book-fonts;
      text-halo-radius: 1;
      text-halo-fill: rgba(255,255,255,0.6);
      text-wrap-width: 30;
      text-min-distance: 2;
      [zoom >= 14] {
        text-size: 9;
      }
      [zoom >= 15] {
        text-size: 10;
      }
      [zoom >= 17] {
        text-size: 11;
        /* Offset name on traffic_signals on zoomlevels where they are displayed
        in order not to hide the text */
        [highway = 'traffic_signals'] {
          text-dy: 14;
        }
      }
    }

次のようにする。Zoom14以上で交差点名を表示する。 交差点名(信号機名)がない場合には Zoom16以上で信号機アイコンを表示する。 Zoom17以上では、アイコンと交差点名(信号機名)を表示する。

ルールの書き方はもっと分かりやすいものがあれば書き換える。

    <Rule>
      &maxscale_zoom14;
      &minscale_zoom15;
      <Filter>[highway]='traffic_signals' and [name] != ''</Filter>
      <TextSymbolizer size="10" fill="black" dy="0" fontset-name="book-fonts" 
	 halo-radius="1" wrap-width="30" placement="interior">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      &maxscale_zoom16;
      &minscale_zoom16;
      <Filter>[highway]='traffic_signals' and [name] = ''</Filter>
      <PointSymbolizer file="&symbols;/traffic_light.png" placement="interior"/>
    </Rule>
    <Rule>
      &maxscale_zoom17;
      <Filter>[highway]='traffic_signals'</Filter>
      <PointSymbolizer file="&symbols;/traffic_light.png" placement="interior"/>
      <TextSymbolizer size="11" fill="black" dy="12" fontset-name="book-fonts" 
	 halo-radius="1" wrap-width="30" placement="interior">[name]</TextSymbolizer>
    </Rule>

バス停は交差点の近くが多いため、交差点標識と競合しやすい。 現在は、バス停の方が優先表示されるようだ。何故かは、今の所不明。

[layer-amenity-points.xml.inc]

ゴルフコース

分類上は miniature_golf と golf_course があるが、狭い golf_course もある。

このようなゴルフ練習場はアイコンを表示し、Layer area-text での名前表示をやめる。

osm.xml では Layer area-text でゴルフ場名のみを表示している。

CartoCSS では 次の規則が使われている。Zoom15以上でアイコン、名前が表示される。 狭いゴルフ場では、重なりが起きやすい。 アイコンは中央に位置するので、重なりでは、名前の方が表示されないことが多い。

実際のデータではゴルフ練習場は sport:golf が多くあった。 デフォルト地図やOSM標準地図では無視されるかどうか未確認である。

  /* amenity-points.mss */
  [feature = 'leisure_golf_course'][zoom >= 15] {
    point-file: url('symbols/golf.p.20.png');
    point-placement: interior;
  }

  /* amenity-points.mss */
  [feature = 'leisure_miniature_golf'][zoom >= 17],
  [feature = 'leisure_golf_course'][zoom >= 15] {
    text-name: "[name]";
    text-size: 11;
    text-fill: darken(@park, 60%);
    text-face-name: @book-fonts;
    text-halo-radius: 1;
    text-halo-fill: rgba(255,255,255,0.6);
    text-placement: interior;
    text-dy: 13;
    text-wrap-width: 40;
  }

  /* landcover.mss */
  [feature = 'leisure_golf_course'][zoom >= 10],
  [feature = 'leisure_miniature_golf'][zoom >= 15] {
    polygon-fill: @golf_course;
    [way_pixels >= 4]  { polygon-gamma: 0.75; }
    [way_pixels >= 64] { polygon-gamma: 0.3;  }
  }

当面は標準OSM地図に近い次の形としよう。 mini-golf や sport:golf の扱いは標準OSM地図での表示を検討してから決めよう。

    <Rule>
      &maxscale_zoom15;
      <Filter>[leisure]='golf_course'</Filter>
      <PointSymbolizer file="&symbols;/golf.p.20.png" placement="interior"/>
      <TextSymbolizer size="11" fill="green" dy="13" fontset-name="book-fonts" 
	 halo-radius="1" wrap-width="30" placement="interior">[name]</TextSymbolizer>
    </Rule>
[osm.xml]

幹線道路highway=tertiaryの色

highway=tertiaryの色をOSM標準地図に合わせて #ffffb3 から #f8f8ba に変更する。 Googleマップなど他のネット地図よりも道が太く、少々目立ちすぎるので、 道幅をもう少し細くし、色も抑える方がいいかも知れない。 ただし、道幅は道路名の表示に関係するので、変更は慎重に検討する必要がある。 [osm.xml]

道路の枠線

道路の枠線は casing と呼んでいるようだ。この枠線の太さや色の変更方法は試行錯誤の結果、 次のようにすればよいことが分かった。 次の例のような記述がosm.xmlの離れた場所で書かれているため、気づくのに時間がかかった。

元のファイルでは最初に 幅16,色#bbb の線が定義されている。その後で、幅13,色#ffffb3 の線が定義されている。 実際には、二度描画していないかも知れないが、見かけ上は最初に幅16,色#bbbで線を描き、 その後、同じ位置に 幅13,色#ffffb3 で線を上書きする。縁の1.5幅に色#bbbの線が残り、 中央の幅13は色#ffffb3で塗りつぶされる。

枠の色を #ccc 、太さを1.0、 道路の色を #f8f8ba" に変更して、うまく行くことを確認した。

本来ならば、この二つの記述を同じ場所で対にして定義すべきであるが、 当面、修正箇所が少ないため、大枠は現状のまま、修正を加えることにする。

    <Rule>
      <Filter>([highway] = 'tertiary' or [highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road') and not [tunnel]='yes'</Filter>
      &maxscale_zoom17;
      &minscale_zoom18;
      <!--LineSymbolizer stroke-linejoin="round" stroke="#bbb" stroke-width="16" stroke-linecap="round"/-->
      <LineSymbolizer stroke-linejoin="round" stroke="#ccc" stroke-width="15" stroke-linecap="round"/>
    </Rule>
[中略]
    <Rule>
      <Filter>[highway] = 'tertiary' and not [tunnel]='yes'</Filter>
      &maxscale_zoom17;
      &minscale_zoom18;
      <!--LineSymbolizer stroke-linejoin="round" stroke="#ffffb3" stroke-width="13" stroke-linecap="round"/-->
      <LineSymbolizer stroke-linejoin="round" stroke="#f8f8ba" stroke-width="13" stroke-linecap="round"/>
    </Rule>

特にZoom14で、市内道路highway:unclassifiedの枠線がデフォルト地図では目立ちすぎる。 次の二つのルールから、枠線の色は #999 で、幅は 0.75 となる。

    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom14;
      &minscale_zoom14;
      <LineSymbolizer stroke="#999" stroke-width="4.5" stroke-dasharray="4,2"/>
    </Rule>

    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom14;
      &minscale_zoom14;
      <LineSymbolizer stroke-linejoin="round" stroke="#fff" stroke-width="3" stroke-linecap="round"/>
    </Rule>

一方、roads.mss では枠線の色は #bbb で、幅は 0.5 である。取り敢えずは、 これに合わせて次のように変更する。

    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom14;
      &minscale_zoom14;
      <LineSymbolizer stroke="#bbb" stroke-width="4" stroke-dasharray="4,2"/>
    </Rule>

    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom14;
      &minscale_zoom14;
      <LineSymbolizer stroke-linejoin="round" stroke="#fff" stroke-width="3" stroke-linecap="round"/>
    </Rule>

dyの調整

当初、店名や銀行名など場所があるのに表示されないケースが多かった。

size=10に対して、dy=11 とすると、dy=10 で表示されないレストラン名が多く表示されるようになった。 CartoCSSを基準に調整するのが良さそう。

学校名の表示

学校によっては、学校名が node と way で重複して登録されている。 OSM標準地図では way の学校名、デフォルト地図では node の学校名が表示される。 どちらかと言えば、way の学校名のほうが見栄えがよい。 そこで、Layer text と Layer text-poly の順序を入れ替えた。 これにより way での学校名が node での学校名より優先されるようになった。

橋の表示

橋はデフォルト地図と標準地図がほぼ同じで黒い線で描かれている。このままでもいいが、少し目障りである。 Google Mapでは特に何も書かれていない。

取り敢えず、数が多い市内道路と市内幹線道路の橋の表現を抑えてみよう。

元のosm.xml では bridges-casing で

    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom14;
      &minscale_zoom14;
      <LineSymbolizer stroke="black" stroke-width="4.5"/>
    </Rule>
    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom15;
      &minscale_zoom15;
      <LineSymbolizer stroke="black" stroke-width="9"/>
    </Rule>
    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom16;
      &minscale_zoom16;
      <LineSymbolizer stroke="black" stroke-width="11"/>
    </Rule>
    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified' or [highway] = 'road'</Filter>
      &maxscale_zoom17;
      &minscale_zoom18;
      <LineSymbolizer stroke="black" stroke-width="16"/>
    </Rule>
と定義され、bridges-fill で
    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified'</Filter>
      &maxscale_zoom14;
      &minscale_zoom14;
      <LineSymbolizer stroke-linejoin="round" stroke="white" stroke-width="3.5" stroke-linecap="round"/>
    </Rule>
   <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified'</Filter>
      &maxscale_zoom15;
      &minscale_zoom15;
      <LineSymbolizer stroke-linejoin="round" stroke="white" stroke-width="7.5" stroke-linecap="round"/>
    </Rule>
    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified'</Filter>
      &maxscale_zoom16;
      &minscale_zoom16;
      <LineSymbolizer stroke-linejoin="round" stroke="white" stroke-width="9.5" stroke-linecap="round"/>
    </Rule>
    <Rule>
      <Filter>[highway] = 'residential' or [highway] = 'unclassified'</Filter>
      &maxscale_zoom17;
      &minscale_zoom18;
      <LineSymbolizer stroke-linejoin="round" stroke="white" stroke-width="14" stroke-linecap="round"/>
    </Rule>
と定義されている。従って橋の色は黒で、線の太さはZoomレベルにより 0.5〜1.0 である。 これを線の色を #999 、太さを 0.5 とした。

交差点名の優先表示

Zoom14以上では、交差点名を極力表示させるために、バス停よりも優先させる。 このため、highway = 'trafic_signals' だけを抽出する Layer を作成し、前に置いた。

行政境界線

デフォルト地図(layer-admin.xml.inc)では境界線のみでやや目立つ。 標準地図(admin.mss)では、境界線に沿って、県名、市名等が表示される。

細かい相違は他にもあるが、とりあえず、境界線の色と県名、市名等の表示を標準地図に合わせる。

県名、市名等の表示には、SartoCSSの project.mml を参考にして新たなレイヤを追加した。

<Style name="admin-text">
    <Rule>
      &maxscale_zoom16;
      <TextSymbolizer size="10" dy="-10" fill="#ac46ac" 
	fontset-name="book-fonts" halo-radius="1" placement="line" clip="false">[name]</TextSymbolizer>
    </Rule>
</Style>

<Layer name="admin-text" status="on" srs="&osm2pgsql_projection;">
    <StyleName>admin-text</StyleName>
    <Datasource>
      <Parameter name="table">
      (select way,name,admin_level
       from &prefix;_polygon
       where "boundary"='administrative'
         and (admin_level in ('0','1','2','3','4','5','6','7','8','9','10'))
	 and name is not null
	order by admin_level::integer asc, way_area desc
       ) as admin_text</Parameter>
      &datasource-settings;
    </Datasource>
</Layer>

概ね所望の結果が得られた。レイヤの作り方にもかなり慣れてきた。 CartoCSS では、更に細かい調整をしている。 細かい調整はMapnikの使い方に習熟してから行うことにする。

マンションの表示

現在のカスタム地図はデフォルト地図より標準地図に近いが、 マンションの敷地に枠線がないことと何号棟という文字が縁取りされる点が異なっている。

ちょっと調べた範囲では、次のようにタグ付けされていた。

		<tag k="name" v="6号棟"/>
		<tag k="building" v="residential"/>
		<tag k="building" v="apartments"/>
		<tag k="addr:housename" v="4-12号棟"/>
		<tag k="addr:housenumber" v="4-12"/>

osm.xml等を building で検索したが、どこで表示しているか分からなかった。 標準地図では敷地は buildings.mss で

@building-fill: #d9d0c9; //Lch(84, 5, 70)
@building-line: darken(@building-fill, 15%);
@building-low-zoom: darken(@building-fill, 4%);
[その他省略]
を使用しているが、ここでは文字表示はない。 project.mml に building-text、housenames というレイヤがある。

表示は addressing.mss で次のように定義されている。 housenamesでは文字の縁取りはないが、building-text ではない。 文字の色は少し異なる。

#housenames {
  [zoom >= 17] {
    text-name: "[addr:housename]";
    text-placement: interior;
    text-wrap-width: 20;
    text-face-name: @book-fonts;
    text-size: 8;
    text-fill: #666;
    [zoom >= 18] {
      text-size: 9;
    }
  }
}

#building-text {
  [zoom >= 14][way_pixels > 3000],
  [zoom >= 17] {
    text-name: "[name]";
    text-size: 11;
    text-fill: #444;
    text-face-name: @book-fonts;
    text-halo-radius: 1;
    text-wrap-width: 20;
    text-halo-fill: rgba(255,255,255,0.5);
    text-placement: interior;
  }
}

デフォルト地図では housenames は layer-addressing.xml.inc にあった。

<Style name="housenames">
    <Rule>
      &maxscale_zoom17;
      &minscale_zoom17;
      <TextSymbolizer size="8" fill="#444" dy="0" fontset-name="book-fonts" wrap-width="20" placement="interior">[addr:housename]</TextSymbolizer>
    </Rule>
    <Rule>
      &maxscale_zoom18;
      <TextSymbolizer size="9" fill="#444" dy="0" fontset-name="book-fonts" wrap-width="20" placement="interior">[addr:housename]</TextSymbolizer>
    </Rule>
</Style>

おそらく osm.xml の Layer area-text で表示されるものと思う。


団地の敷地は layer-landcover.xml.inc の [landuse] = 'residential' で設定することが分かった。 PolygonSymbolizerしか無かったので、LineSymbolizerで境界線を描いた。

棟名は osm.xml の Style area-text で表示される。halo-radius の値を 0 に変えると、文字の縁取りは消えた。 別の事物では halo-radius を設定したくなるかも知れない。そのときは、Rule を増やすか、Style を分ける。

公園名の表示

[landcover.mss]
@park: #cdf7c9; // also recreation_ground

[amenity-points.mss]
@landcover-font-size: 10;
@landcover-font-size-big: 12;
@landcover-font-size-bigger: 15;
@landcover-wrap-width-size: 25;
@landcover-wrap-width-size-big: 35;
@landcover-wrap-width-size-bigger: 45;
@landcover-face-name: @oblique-fonts;

@standard-wrap-width: 30;

  [feature = 'leisure_park'][is_building = 'no'],
  [feature = 'leisure_recreation_ground'][is_building = 'no'],
  [feature = 'landuse_recreation_ground'][is_building = 'no'],
  [feature = 'landuse_conservation'][is_building = 'no'],
  [feature = 'landuse_village_green'][is_building = 'no'],
  [feature = 'leisure_common'][is_building = 'no'],
  [feature = 'leisure_garden'][is_building = 'no'] {
    [zoom >= 10][way_pixels > 3000],
    [zoom >= 17] {
      text-name: "[name]";
      text-size: @landcover-font-size;
      [way_pixels > 12000] { text-size: @landcover-font-size-big; }
      [way_pixels > 48000] { text-size: @landcover-font-size-bigger; }
      text-fill: darken(@park, 60%);
      text-face-name: @landcover-face-name;
      text-halo-radius: 1;
      text-halo-fill: rgba(255,255,255,0.6);
      text-wrap-width: @landcover-wrap-width-size;
      [way_pixels > 12000] {text-wrap-width: @landcover-wrap-width-size-big; }
      [way_pixels > 48000] {text-wrap-width: @landcover-wrap-width-size-bigger; }
      text-placement: interior;
    }
  }

デフォルト地図では osm.xml の area-text レイヤで描画されている。 元の osm.xml では一括りであるが、いくつかに分類して文字の色を変えよう。

取り敢えず、次のように、広さを 150000以上、40000〜150000、40000未満に分け、 それぞれ、Zoomレベルでも少し文字の大きさは変える。

<Style name="area-text-green">
    <Rule>
      <Filter>[way_area] >= 150000</Filter>
      &maxscale_zoom14;
      &minscale_zoom15;
      <TextSymbolizer size="10" fill="green" fontset-name="book-fonts" halo-radius="0" 
		wrap-width="20" placement="interior">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[way_area] >= 150000</Filter>
      &maxscale_zoom16;
      &minscale_zoom16;
      <TextSymbolizer size="12" fill="green" fontset-name="book-fonts" halo-radius="1" 
		wrap-width="20" placement="interior">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[way_area] >= 150000</Filter>
      &maxscale_zoom17;
      <TextSymbolizer size="14" fill="green" fontset-name="book-fonts" halo-radius="1" 
		wrap-width="20" placement="interior">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[way_area] >= 40000 and [way_area] < 150000</Filter>
      &maxscale_zoom15;
      &minscale_zoom16;
      <TextSymbolizer size="12" fill="green" fontset-name="book-fonts" halo-radius="1" 
		wrap-width="20" placement="interior">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[way_area] >= 40000 and [way_area] < 150000</Filter>
      &maxscale_zoom17;
      <TextSymbolizer size="14" fill="green" fontset-name="book-fonts" halo-radius="1" 
	wrap-width="20" placement="interior">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[way_area] < 40000</Filter>
      &maxscale_zoom17;
      <TextSymbolizer size="12" fill="green" fontset-name="book-fonts" halo-radius="1" 
	wrap-width="20" placement="interior">[name]</TextSymbolizer>
    </Rule>

市内幹線道路(tertiary)名の表示

Tertiaryの道路名の縁取りを止めたい。 osm.xml の Style road-text-name の [highway] = 'tertiary' に対する TextSymbolizer で halo-radius = "0" とした。

地名の表示

例えば Zoom 10 で比較すると、デフォルト地図より標準地図の方が地名も見栄えがよい。

標準地図では市名・町名は placenames.mss で次のように規定されている。

#placenames-medium::city {
  [place = 'city'] {
    [zoom >= 6][zoom < 15] {
      text-name: "[name]";
      text-size: 9;
      text-fill: @placenames;
      text-face-name: @book-fonts;
      text-halo-radius: 1.5;
      text-halo-fill: rgba(255,255,255,0.6);
      text-wrap-width: 45;
      text-min-distance: 10;
      [zoom >= 9] {
        text-size: 12;
        text-wrap-width: 60;
      }
      [zoom >= 11] {
        text-size: 15;
        text-wrap-width: 75;
      }
    }
  }
}

#placenames-medium::town {
  [place = 'town'] {
    [zoom >= 9][zoom < 16] {
      text-name: "[name]";
      text-size: 9;
      text-fill: @placenames;
      text-face-name: @book-fonts;
      text-halo-radius: 1.5;
      text-halo-fill: rgba(255,255,255,0.6);
      text-wrap-width: 45;
      text-min-distance: 10;
      [zoom >= 11] {
        text-size: 11;
        text-wrap-width: 55;
      }
      [zoom >= 12] {
        text-size: 13;
        text-wrap-width: 65;
      }
      [zoom >= 14] {
        text-size: 15;
        text-wrap-width: 75;
      }
    }
  }
}

デフォルト地図では

<Style name="city">
    <Rule>
      <Filter>[place] = 'city' or [place]='metropolis'</Filter>
      &maxscale_zoom6;
      &minscale_zoom8;
      <TextSymbolizer size="8" fill="#000" dy="0" fontset-name="book-fonts" halo-radius="1" wrap-width="0">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[place] = 'city' or [place]='metropolis'</Filter>
      &maxscale_zoom9;
      &minscale_zoom10;
      <TextSymbolizer size="11" fill="#000" dy="0" fontset-name="book-fonts" halo-radius="1" wrap-width="0">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[place] = 'city' or [place]='metropolis'</Filter>
      &maxscale_zoom11;
      &minscale_zoom14;
      <TextSymbolizer size="14" fill="#000" dy="0" fontset-name="book-fonts" halo-radius="1" wrap-width="0">[name]</TextSymbolizer>
    </Rule>
</Style>
<Style name="town">
    <Rule>
      <Filter>[place] = 'town' or [place]='large_town' or [place]='small_town'</Filter>
      &maxscale_zoom9;
      &minscale_zoom10;
      <TextSymbolizer size="8" fill="#000" fontset-name="book-fonts" halo-radius="1" wrap-width="20">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[place] = 'town' or [place]='large_town' or [place]='small_town'</Filter>
      &maxscale_zoom11;
      &minscale_zoom13;
      <TextSymbolizer size="10" fill="#000" fontset-name="book-fonts" halo-radius="1" wrap-width="20">[name]</TextSymbolizer>
    </Rule>
    <Rule>
      <Filter>[place] = 'town' or [place]='large_town' or [place]='small_town'</Filter>
      &maxscale_zoom14;
      <TextSymbolizer size="14" fill="#777777" fontset-name="book-fonts" halo-radius="1" wrap-width="20">[name]</TextSymbolizer>
    </Rule>
</Style>

タブレットはパソコンよりも高精細で文字が小さくなるため、全体的に文字を2ポイントほど大きくした。 これで標準地図よりも少し大きくなった。しかし、もう少し大きい方がいいかもしれない。 halo は標準地図に合わせた。

空港名の表示

空港名がデフォルト地図、標準地図とも非常に小さい。更に、デフォルト地図では halo の影響で読みずらい。 恐らく、halo-fill="rgba(255,255,255,0.6)"がないのだろう。

CartoCSSでは amenity-points.mss、デフォルト地図ではlayer-amenity-symbols.xml.incで設定されている。

低ズームにおける道路番号の表示

ズーム10における道路番号の表示が所々おかしい。 CartoCSS では次のように定義されており、バランスよく表示される。

 
#roads-text-ref-low-zoom {
  [highway = 'motorway'][zoom >= 10][zoom < 13] {
    shield-name: "[refs]";
    shield-size: 10;
    shield-fill: #fff;
    shield-placement: line;
    shield-file: url("symbols/shields/motorway_[width]x[height].svg");
    shield-spacing: 750;
    shield-min-distance: 30;
    shield-face-name: @bold-fonts;
    shield-clip: false;
  }
 
    {
      "name": "roads-text-ref-low-zoom",
      "srs-name": "900913",
      "geometry": "linestring",
      "class": "",
      "id": "roads-text-ref-low-zoom",
      "srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
      "Datasource": {
        "extent": "-20037508,-20037508,20037508,20037508",
        "table": "(SELECT way, highway, height, width, 
		refs FROM\n  (SELECT\n      way, highway,\n      array_length(refs,1) AS height,\n
	      (SELECT MAX(char_length(ref)) FROM unnest(refs) AS u(ref)) AS width,\n
	      array_to_string(refs, E'\\n') AS refs\n    FROM (\n      
		SELECT\n          way,\n          highway,\n          
		string_to_array(ref, ';') AS refs\n      FROM planet_osm_roads\n
	        WHERE highway IN ('motorway', 'trunk', 'primary', 'secondary')\n
	        AND ref IS NOT NULL\n      ) AS p) AS q\n  
		WHERE height <= 4 AND width <= 11) AS roads_text_ref_low_zoom",
        "geometry_field": "way",
        "type": "postgis",
        "key_field": "",
        "dbname": "gis"
      },

デフォルト地図では次のように定義されている。

 
    <Rule>
      <Filter>[highway] = 'motorway' and [length] le 6</Filter>
      &maxscale_zoom10;
      &minscale_zoom12;
      <ShieldSymbolizer size="10" fill="#fff" placement="line" file="&symbols;/mot_shield[length].png" spacing="750" minimum-distance="30" fontset-name="bold-fonts">[ref]</ShieldSymbolizer>
    </Rule>

<Layer name="roads-text-ref-low-zoom" status="on" srs="&osm2pgsql_projection;">
     <StyleName>roads-text-ref-low-zoom</StyleName>
     <Datasource>
      <Parameter name="table">
      (select way,highway,ref,char_length(ref) as length
       from &prefix;_roads
       where highway in ('motorway','trunk','primary','secondary')
         and ref is not null
         and char_length(ref) between 1 and 8
      ) as roads
      </Parameter>
      &datasource-settings;
    </Datasource>
</Layer>

CartoCSSでの SQL文の複雑さは今回の問題には多分関係しないだろう。 shild-clip: false は Mapnik の XML ではどうなるのだろう。

ネット検索したところ、Mapnik にバグがあり shild-clip: false はそのバグ対策のようだ。 Mapnik XML では avoid-edges="true"[6] がそれに当たるものと推測される。

この挿入で番号の重なりは殆どなくなった。しかし、標準地図の方が安定している。 shild-clip: false は更に別の役割も果たすのかも知れない。あるいは SQL 文の違いも関係するかも知れない。

しかし、当面はこれでいいので、先に進んで、何か分かったときのこととする。

県名の表示

デフォルト地図では県名は表示されない。province で表示されるようにした。 場所は標準地図と同じこともあるが異なるケースが多い。表示されないことがあるのは標準地図でも同じである。

大きな違いは、標準地図は「千葉県」で、カスタム地図では「千葉県(Chiba)」となる。

CartoCSSを少し調べた限りでは province はない。 また、標準では離島に都道府県名が表示されるが、province では表示されない。 CartoCSSでは、全く別の方法で都道府県名を表示しているのかも知れない。 日頃使用するのはハイズームの地図のため、余り問題にはならない。 また、少なくとも当面は、カスタム地図はハイズームに限定されるので、先送りとする。

A.リファレンス

[1] Manually building a tile server (14.04)
[2] Rendering Text Labels with Custom Fonts in Mapnik
[3] JA:Map_Features
[4] How does Mapnik handle overlap problem during rendering dense dataset
[5] Re: [mapnik] the style order on mapnik
[6] Cut Labels with Mapnik

B.履歴

課題 -> 解決[2015.06.13]

nodeとwayで学校名が重複する場合、wayの記述が表示されているが、これはたまたまか? それとも way の方が優先されるのか

node が Layer text、way が Layer text-poly と思われる。 osm.xml では Layer text-poly が定義されているが、Style text-poly は存在しない。 このため、店名や銀行名などが osmファイルの node で定義されていた場合は表示されるが、 way中で定義されていた場合には表示されないものと思われる。 従って、Style text に合わせて Style text-poly を作ってみよう。

これは勘違いだった。Layer text-poly でも Style text となっているので、集約されているようだ。 表示されないのは別の原因があるようだ。

衝突についてネットで少し調べた。


Reference 4

Mapnik, by default, does not draw overlapping features for icons and text symbolizers. So the features in the first layer are drawn first, one-by-one, and if there is no space remaining then they are not drawn.

This means that the order of the layers, and the order of the features within the layers, are important.


Reference 5

Q: when render point with name ,does the oder of the rule in a style affect the finial picture ? what about the style order in the same layer ?

A: Rule order does affect render result. And Symbolizer order within Rules also can affect render result. But all Rules will be processed unless you have `filter-mode="first"` specified. So, it is often the case that Rule order does not visually or noticably impact the render result.

The rendering goes: 1) for every layer (in the order they are in the map), 2) render each style attached to a given layer (in the order they are attached to the layer), and then 3) render all features for rules that evaluated to true for each symbolizer.


実際に少し試した範囲では、ルールの順序は余り影響しなかった。 そこで、Layer text と Layer text-poly の順序を入れ替えた。 これにより way での学校名が node での学校名より優先されるようになった。


Layer landcover を改めてチェックすると、

      (select way,aeroway,amenity,landuse,leisure,man_made,military,"natural",power,tourism,name,highway,
       case when religion in ('christian','jewish') then religion else 'INT-generic'::text end as religion
       from &prefix;_polygon
       where landuse is not null
          or leisure is not null
          or aeroway in ('apron','aerodrome')
          or amenity in ('parking','university','college','school','hospital','kindergarten','grave_yard','prison')
          or military in ('barracks','danger_area')
          or "natural" in ('field','beach','desert','heath','mud','grassland','wood','sand','scrub')
          or power in ('station','sub_station','generator')
          or tourism in ('attraction','camp_site','caravan_site','picnic_site','zoo')
          or highway in ('services','rest_area')
       order by z_order,way_area desc
      ) as leisure
となっている。Style name="leisure" のルールが適用されるのかも知れない。 しかし、そのような記述は見つからなかった。 因みに as landcover としてみたが、特に差異は見つからなかった。 どんな名称でもいいのかも知れない。