> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

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

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

Source: https://perfectvector.com/ja/blog/maplibre-svg-symbol-image

Language: ja
Author: Irene Kim
Published: 2026-10-05T08:00:00+09:00
Updated: 2026-10-05T08:00:00+09:00

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

現在の[公式SVGシンボルの例](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-remote-svg-symbol/)に、このデコード経路が示されています。MapLibreではSVGアートワークを表示できないと一律に言うと、この方法を見落とします。この例では6.12.0に固定し、トークン不要の空白地図で、独自の中空エンブレムをテストします。

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

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

ブラウザーのデコードによって、それぞれのコピーが宣言した寸法の画像になります。MapLibreの[`addImage` API](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#addimage)は、`HTMLImageElement`や`ImageData`などの画像オブジェクトを受け取れます。シンボルの形状指示としてSVGパスを受け取るものではありません。

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

**元のアートワークとシンボルの描画**

![中空のひし形エンブレムを、編集可能な輪郭とピクセルベースの表示用コピーとして示す概念図](https://perfectvector.com/blog/maplibre-svg-symbol-image/source-and-symbol.webp)

*模式図：デコードした画像の表示サイズを制御しながら、編集可能なSVGを保管します。以下のWebGL計測は、別に作成したブラウザーのサンプルによるものです。*

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

二つの設定が、シンボルの公称画像枠に影響します。画像の`pixelRatio`は密度を表し、レイヤーの`icon-size`は結果のアイコンを拡大縮小します。現在の[スタイル画像のメタデータ](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/StyleImageMetadata/)と[シンボルレイアウト仕様](https://maplibre.org/maplibre-style-spec/layers/#icon-size)を参照してください。

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

```text
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オプション](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/)を使い、キャンバス比率を明示的に1に設定しています。ブラウザーのデバイスピクセル比率は2でした。これにより、スクリーンショットのデバイスピクセルをCSS寸法と考えることなく、以下の比較を切り分けています。

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

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

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

| デコード画像  | 画像比率 | icon-size | 公称枠     | 観察した塗りの範囲      |
| ------- | ---- | --------- | ------- | -------------- |
| 64×64   | 1    | 1         | 64×64   | 62×62 CSS px   |
| 128×128 | 1    | 1         | 128×128 | 126×126 CSS px |
| 128×128 | 2    | 1         | 64×64   | 62×62 CSS px   |
| 64×64   | 1    | 1.5       | 96×96   | 94×94 CSS px   |
| 128×128 | 2    | 1.5       | 96×96   | 94×94 CSS px   |

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

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

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

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

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

この例は[`image.decode()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode)を待つので、デコード成功後に登録します。スタイルの読み込み後に既知の画像を登録するため、この一つの固定エンブレムには、公式例の欠落画像リゾルバーは不要です。

```html
<!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マーカーを使う独自アイコン](https://maplibre.org/maplibre-gl-js/docs/examples/add-custom-icons-with-markers/)も文書化しています。この経路はスタイル画像を登録する代わりに、HTML要素を包み込みます。要素ベースのマーカーの手順がアプリケーションに合う場合に選び、ブラウザーのスタイルと配置は別に確認してください。

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

利用先がPNGファイルを必要とする場合は、[SharpによるSVG描画ガイド](https://perfectvector.com/ja/blog/sharp-svg-to-png)でラスターの納品用コピーの準備を説明しています。編集用には通常のSVGマスターを保管してください。[インラインSVGと画像での提供](https://perfectvector.com/ja/blog/inline-svg-vs-img)も、操作のモデルが異なります。

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

## よくある質問

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

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

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

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

## 出典

1. [MapLibre — リモートSVGシンボルの表示](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-remote-svg-symbol/) — 現在のSVGテキスト、ブラウザー画像、addImageを使う経路。
2. [MapLibre — Map API](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#addimage) — 対応する画像オブジェクトと、実行時の画像登録。
3. [MapLibre — StyleImageMetadata](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/StyleImageMetadata/) — 画像密度とSDF解釈のメタデータ。
4. [MapLibre — シンボルレイアウト仕様](https://maplibre.org/maplibre-style-spec/layers/#icon-size) — アイコンの拡大縮小と衝突処理。
5. [MapLibre — MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/) — 計測用ファイルの制御に使ったキャンバス設定。
6. [MDN — HTMLImageElement.decode](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) — ブラウザーでの画像デコードの待機と、失敗の処理。
7. [MapLibre — Markersによる独自アイコンの追加](https://maplibre.org/maplibre-gl-js/docs/examples/add-custom-icons-with-markers/) — 別のHTML要素ベースのマーカー手順。

## Explore

- [Read the HTML article](https://perfectvector.com/ja/blog/maplibre-svg-symbol-image)
- [SatoriのSVGロゴ：ブランドカード内のアートワークを保つ](https://perfectvector.com/ja/blog/satori-svg-logo.md)
- [SVGからCanvasへ：PNG書き出し前に画像を読み込む](https://perfectvector.com/ja/blog/svg-canvas-image-export.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
