PerfectVector
著者: Irene Kim

MapLibreのSVGアイコン:ピクセルとシンボルサイズの確認

MapLibreのシンボルレイヤー用にSVGをデコードし、画像密度とアイコンサイズを制御。編集可能なマスターを残し、表示される形状と開口部を確認します。

3 分で読める
目次

MapLibre GL JSでSVGアイコン画像を使うには、ブラウザーのImageでSVGをデコードし、その画像をaddImageに渡して、シンボルレイヤーのicon-imageでIDを参照します。元のSVGは別に保管してください。登録したスタイル画像は描画用のピクセルを提供するもので、編集可能なSVGパスオブジェクトではありません。

現在の公式SVGシンボルの例に、このデコード経路が示されています。MapLibreではSVGアートワークを表示できないと一律に言うと、この方法を見落とします。この例では6.12.0に固定し、トークン不要の空白地図で、独自の中空エンブレムをテストします。

元データと登録画像を分ける

保管する元データには、内側の開口部を持つ一つの複合ひし形パスがあります。viewBoxは0 0 64 64のままです。幅と高さを64または128に明示した二つのコピーを作り、パスデータは同一に保ちます。

ブラウザーのデコードによって、それぞれのコピーが宣言した寸法の画像になります。MapLibreのaddImage APIは、HTMLImageElementやImageDataなどの画像オブジェクトを受け取れます。シンボルの形状指示としてSVGパスを受け取るものではありません。

色や輪郭を変えるときに、この違いが重要です。SVGマスターを編集し、修正した画像をデコードして、適切なAPIで地図の画像を更新してください。登録済みのピクセルを調べても、元のパスの構成は復元できません。ラスター画像を埋め込んだSVGのガイドでは、SVGの入れ物と編集可能な形状の、より広い違いを説明しています。

元のアートワークとシンボルの描画
中空のひし形エンブレムを、編集可能な輪郭とピクセルベースの表示用コピーとして示す概念図
模式図:デコードした画像の表示サイズを制御しながら、編集可能なSVGを保管します。以下のWebGL計測は、別に作成したブラウザーのサンプルによるものです。

画像密度とアイコンサイズを一緒に選ぶ

二つの設定が、シンボルの公称画像枠に影響します。画像のpixelRatioは密度を表し、レイヤーのicon-sizeは結果のアイコンを拡大縮小します。現在のスタイル画像のメタデータとシンボルレイアウト仕様を参照してください。

中央配置、回転なし、テキストへのフィットなしのこのシンボルでは、公称枠は次のようになります。

logical width = decoded width / image pixelRatio * icon-size
logical height = decoded height / image pixelRatio * icon-size

128×128の画像を比率2で登録すると、icon-sizeが1の場合、64×64の画像を比率1で登録した場合と同じ64×64の公称枠になります。前者の比率を1に変えると、枠は2倍になります。icon-sizeを1.5にすると、64×64の論理画像枠が96×96になります。

この枠には元画像の余白が含まれます。その中でエンブレムが小さく見える場合は、すべてのシンボルを大きくする前にSVGの範囲を確認してください。同じ比率でデコード画像を大きくしても、シンボルは大きくなります。入力寸法だけでは密度の方針にはなりません。

地図のキャンバスには独自のピクセル比率があります。addImageに渡す比率とは別です。この計測用ファイルでは、文書化されたMapオプションを使い、キャンバス比率を明示的に1に設定しています。ブラウザーのデバイスピクセル比率は2でした。これにより、スクリーンショットのデバイスピクセルをCSS寸法と考えることなく、以下の比較を切り分けています。

実際に塗られた範囲を比較する

WebGL 2を使うChromeで、五つのシンボルケースを描画しました。各地図には一つの点と同じ明るい背景があり、タイルもトークンもありません。両方の元SVGは、同一のパスを一つ持ち、埋め込み画像はありませんでした。

テラコッタ色のアートワークを測るため、実際のWebGLピクセルを読み取り、赤が強い色を探しました。条件は、赤が緑の1.35倍、青の1.6倍を超えることです。検出したバウンディングボックスの寸法を、キャンバスの内部バッファー寸法とCSS寸法の比率で割りました。これはしきい値で検出した塗り領域の計測であり、画像枠全体ではありません。

デコード画像画像比率icon-size公称枠観察した塗りの範囲
64×641164×6462×62 CSS px
128×12811128×128126×126 CSS px
128×1282164×6462×62 CSS px
64×6411.596×9694×94 CSS px
128×12821.596×9694×94 CSS px

縁のすべてのピクセルが色のしきい値を超えるわけではないため、計測した塗りの範囲は公称枠より小さくなります。これらのペアは、このテストファイルで意図したサイズ関係を示します。すべての密度やズームで、ピクセルの品質が同一であることを証明するものではありません。

登録画像の中央のアルファは、どのケースでもゼロでした。描画された地図では、その場所は背景レイヤー自体が不透明なため、アルファ255で背景色(244, 242, 232)を示しました。開口部を通して不透明な地図のピクセルが見えても、アイコンが透明度を失ったわけではありません。

これらの観察は、この元データ、ブラウザー、キャンバス設定、しきい値に限られます。特に細いストロークや小さな開口部がある場合は、予定の表示サイズで納品するアートワークをテストしてください。

トークン不要のシンボルのひな形をコピーする

この完全なHTMLを保存し、ローカルウェブサーバーで配信します。固定バージョンのライブラリーとCSSにはインターネット接続が必要ですが、地図にはタイルサービスもAPIキーも使いません。SVGはテキストとして埋め込み、データURLへエンコードします。

この例はimage.decode()を待つので、デコード成功後に登録します。スタイルの読み込み後に既知の画像を登録するため、この一つの固定エンブレムには、公式例の欠落画像リゾルバーは不要です。

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>MapLibre decoded SVG symbol</title>
<link rel="stylesheet"
      href="https://unpkg.com/maplibre-gl@6.12.0/dist/maplibre-gl.css">
<style>#map { width: 360px; height: 280px; }</style>
<div id="map"></div>
<pre id="status">Loading…</pre>
<script type="module">
import * as ml from
  'https://unpkg.com/maplibre-gl@6.12.0/dist/maplibre-gl.mjs';
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 64 64"><path fill="#c45b39" fill-rule="evenodd" d="M32 0 L64 32 L32 64 L0 32 Z M32 16 L16 32 L32 48 L48 32 Z"/></svg>`;
const image = new Image();
image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
try {
  await image.decode();
  const ratio = 2;
  const iconSize = 1;
  const map = new ml.Map({
    container: 'map', center: [0, 0], zoom: 2, interactive: false,
    attributionControl: false, fadeDuration: 0,
    style: { version: 8, sources: {}, layers: [{
      id: 'background', type: 'background',
      paint: { 'background-color': '#f4f2e8' }
    }] }
  });
  map.on('error', e => {
    document.querySelector('#status').textContent = e.error.message;
  });
  await map.once('load');
  map.addImage('emblem', image, { pixelRatio: ratio });
  map.addSource('point', { type: 'geojson', data: {
    type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] },
    properties: {}
  } });
  map.addLayer({ id: 'emblem', type: 'symbol', source: 'point', layout: {
    'icon-image': 'emblem', 'icon-size': iconSize, 'icon-overlap': 'always'
  } });
  await map.once('idle');
  const registered = map.getImage('emblem');
  document.querySelector('#status').textContent = JSON.stringify({
    version: ml.getVersion(),
    decoded: [image.naturalWidth, image.naturalHeight],
    registered: [registered.data.width, registered.data.height],
    imagePixelRatio: registered.pixelRatio,
    logicalFrame: [image.naturalWidth / ratio * iconSize,
                   image.naturalHeight / ratio * iconSize]
  }, null, 2);
} catch (error) {
  document.querySelector('#status').textContent = String(error);
}
</script>
</html>

ステータスには、デコードと登録の寸法[128, 128]、画像比率2、論理枠[64, 64]が表示されるはずです。これは公称枠を報告するもので、塗りのバウンディングボックスの計測値ではありません。iconSizeを1.5に変えると、96×96の枠になります。低密度の入力と比較するには、同じパスとviewBoxを保ったまま、SVGの幅と高さを64、ratioを1に変えます。

icon-imageは登録したIDと同じにしてください。アイコンが表示されない場合は、デコードと登録の成功を確認し、その後、ソース、レイヤー、表示状態、配置設定を調べます。一点だけのこの例ではicon-overlap: 'always'を使い、ほかのシンボルとの衝突から比較を独立させています。本番用スタイルでは、別の衝突処理が必要な場合があります。

提供方法に合う場合はDOMマーカーを使う

MapLibreはDOMマーカーを使う独自アイコンも文書化しています。この経路はスタイル画像を登録する代わりに、HTML要素を包み込みます。要素ベースのマーカーの手順がアプリケーションに合う場合に選び、ブラウザーのスタイルと配置は別に確認してください。

Mapbox Studioのベクターアイコン機能や、MapTiler SDKの例の説明を、このMapLibreのスタイル画像パイプラインに当てはめないでください。また、通常のフルカラーSVG画像は、sdfフラグを変えるだけで符号付き距離場にはなりません。密度設定は画像の解釈と表示を制御するもので、ピクセルを編集可能な輪郭に変えるものではありません。

利用先がPNGファイルを必要とする場合は、SharpによるSVG描画ガイドでラスターの納品用コピーの準備を説明しています。編集用には通常のSVGマスターを保管してください。インラインSVGと画像での提供も、操作のモデルが異なります。

アイコンがPNGやJPGでしか存在せず、編集可能なアートワークが必要なら、PerfectVectorでSVG輪郭を復元し、外側の形状と開口部を確認して、地図画像をデコードする前にそのマスターを残します。密度、シンボルサイズ、地理的位置は、引き続き地図コードが制御します。きれいなSVGがすでにあれば、直接デコードの手順に進めます。ベクター化ガイドでは、ラスター変換が役立つ場面を説明しています。

よくある質問

MapLibre GL JSはSVGアートワークをシンボルとして表示できますか? できます。現在の公式例は、ブラウザーのImageでSVGテキストをデコードし、その画像をシンボルレイヤーに登録します。元のSVGは登録画像と分けて保管してください。

大きい入力でアイコンも大きくなったのはなぜですか? 同じ画像ピクセル比率とicon-sizeを保ちながらデコード寸法を増やすと、公称枠が大きくなります。画像密度と表示倍率を意図的に組み合わせてください。

pixelRatioはシンボル内に編集可能なSVGパスを保持しますか? 保持しません。画像密度を表します。元のSVGを保管して編集し、その後、修正したアートワークを地図用にデコードしてください。

アイコンの穴の中の地図ピクセルが不透明なのはなぜですか? 不透明な背景レイヤーが、アイコンの透明な開口部を通して見える場合があります。登録画像のアルファは、完全に合成された地図キャンバスと分けて確認してください。

出典

  1. MapLibre — リモートSVGシンボルの表示 — 現在のSVGテキスト、ブラウザー画像、addImageを使う経路。
  2. MapLibre — Map API — 対応する画像オブジェクトと、実行時の画像登録。
  3. MapLibre — StyleImageMetadata — 画像密度とSDF解釈のメタデータ。
  4. MapLibre — シンボルレイアウト仕様 — アイコンの拡大縮小と衝突処理。
  5. MapLibre — MapOptions — 計測用ファイルの制御に使ったキャンバス設定。
  6. MDN — HTMLImageElement.decode — ブラウザーでの画像デコードの待機と、失敗の処理。
  7. MapLibre — Markersによる独自アイコンの追加 — 別のHTML要素ベースのマーカー手順。

ブログのその他の記事

PerfectVector

編集しやすい
よりきれいなSVGから始めましょう

クレジットカード不要