mcpField
U
ux-map

ux-map

github.com/smnandre開発18054 回インストール19 週間前に更新
リポジトリ

ux-map について

UX MapはSymfony向けのインタラクティブな地図コンポーネントです。PHPで地図を定義してTwigで表示でき、LeafletやGoogle Mapsのレンダラー、Stimulusイベント、LiveComponent連携に対応します。

主な機能

  1. 01PHPの地図オブジェクトとTwig描画
  2. 02マーカー、図形、情報ウィンドウ
  3. 03LeafletとGoogle Mapsのレンダラー
  4. 04Stimulusの地図イベント
  5. 05LiveComponentのリアクティブ連携

活用シーン

  1. 01Symfonyページで位置マーカーを表示
  2. 02多角形、線、円の描画
  3. 03地図要素の作成イベントを監視
  4. 04更新に反応する地図の構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/smnandre/symfony-ux-skills --skill ux-map

インストール後、Claude Code で/ux-mapを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api.md
gotchas.md
patterns.md

詳細

UX Map

Symfony で Leaflet または Google Maps を使ったインタラクティブマップを利用できます。PHP でマップを構築し、Twig でレンダリングして、Stimulus コントローラーで拡張できます。マーカー、ポリゴン、ポリライン、円、情報ウィンドウ、LiveComponent との連携に対応しています。

インストール

# Install the base package
composer require symfony/ux-map

# Then install ONE renderer:
composer require symfony/ux-leaflet-map    # Leaflet (free, open source)
# OR
composer require symfony/ux-google-map     # Google Maps (requires API key)

クイックリファレンス

Map                    PHP object, holds markers/shapes/options
Point                  latitude + longitude
Marker                 pin on the map, optional InfoWindow
Polygon                closed shape (array of Points)
Polyline               open line (array of Points)
Circle                 center Point + radius in meters
InfoWindow             popup attached to a Marker, Polygon, or Circle
ux_map(map, attrs)     Twig function to render a Map
<twig:ux:map />        Twig component (requires ux-twig-component)

PHP でマップを構築する

use Symfony\UX\Map\Map;
use Symfony\UX\Map\Point;
use Symfony\UX\Map\Marker;
use Symfony\UX\Map\InfoWindow;
use Symfony\UX\Map\Polygon;
use Symfony\UX\Map\Polyline;
use Symfony\UX\Map\Circle;

$map = (new Map())
    ->center(new Point(48.8566, 2.3522))
    ->zoom(12)
    ->minZoom(3)
    ->maxZoom(18);

// Marker with info window
$map->addMarker(new Marker(
    position: new Point(48.8566, 2.3522),
    title: 'Paris',
    infoWindow: new InfoWindow(
        headerContent: '<b>Paris</b>',
        content: 'The capital of France',
    ),
));

// Marker with custom icon (UX Icons integration)
$map->addMarker(new Marker(
    position: new Point(48.8738, 2.2950),
    title: 'Eiffel Tower',
    icon: Icon::ux('mdi:tower-eiffel')->width(32)->height(32),
    extra: ['category' => 'landmark'],
));

// Polygon (closed area)
$map->addPolygon(new Polygon(
    points: [
        new Point(48.8566, 2.3522),
        new Point(48.8606, 2.3376),
        new Point(48.8530, 2.3499),
    ],
    infoWindow: new InfoWindow(content: 'Central Paris area'),
));

// Polyline (open line)
$map->addPolyline(new Polyline(
    points: [
        new Point(48.8566, 2.3522),
        new Point(48.8738, 2.2950),
    ],
));

// Circle (center + radius in meters)
$map->addCircle(new Circle(
    center: new Point(48.8566, 2.3522),
    radius: 500,
    infoWindow: new InfoWindow(content: '500m radius'),
));

// Auto-fit bounds to show all markers
$map->fitBoundsToMarkers();

Twig でレンダリングする

Twig 関数
{# Basic rendering #}
{{ ux_map(map, {style: 'height: 400px; width: 100%;'}) }}

{# With custom attributes and Stimulus controller #}
{{ ux_map(map, {
    'data-controller': 'custom-map',
    style: 'height: 400px;',
    class: 'rounded shadow'
}) }}
Twig コンポーネント(HTML 構文)

symfony/ux-twig-component が必要です。PHP を使わずにマップをインラインで定義できます。

<twig:ux:map
    :center="[48.8566, 2.3522]"
    zoom="12"
    :markers='[
        {"position": [48.8566, 2.3522], "title": "Paris"},
        {"position": [48.8738, 2.2950], "title": "Eiffel Tower",
         "infoWindow": {"content": "324m tall"}}
    ]'
    :fitBoundsToMarkers="true"
    style="height: 400px; width: 100%;"
    class="rounded shadow"
/>

設定

# config/packages/ux_map.yaml
ux_map:
    renderer: '%env(resolve:default::UX_MAP_DSN)%'

    # Google Maps specific
    google_maps:
        default_map_id: null    # Optional: default Map ID for all maps
レンダラー DSN

.env で設定します。

# Leaflet (free)
UX_MAP_DSN=leaflet://default

# Google Maps (requires API key)
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default

レンダラーのオプション

Leaflet のオプション
use Symfony\UX\Map\Bridge\Leaflet\LeafletOptions;
use Symfony\UX\Map\Bridge\Leaflet\Option\TileLayer;
use Symfony\UX\Map\Bridge\Leaflet\Option\ZoomControlOptions;
use Symfony\UX\Map\Bridge\Leaflet\Option\AttributionControlOptions;
use Symfony\UX\Map\Bridge\Leaflet\Option\ControlPosition;

$leafletOptions = (new LeafletOptions())
    ->tileLayer(new TileLayer(
        url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
        attribution: '&copy; OpenStreetMap contributors',
        options: ['minZoom' => 5, 'maxZoom' => 18],
    ))
    ->zoomControl(true)
    ->zoomControlOptions(new ZoomControlOptions(ControlPosition::TOP_LEFT))
    ->attributionControl(true)
    ->attributionControlOptions(new AttributionControlOptions(ControlPosition::BOTTOM_LEFT));

$map->options($leafletOptions);
Google Maps のオプション
use Symfony\UX\Map\Bridge\Google\GoogleOptions;
use Symfony\UX\Map\Bridge\Google\Option\ControlPosition;
use Symfony\UX\Map\Bridge\Google\Option\GestureHandling;
use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlOptions;
use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlStyle;
use Symfony\UX\Map\Bridge\Google\Option\ZoomControlOptions;
use Symfony\UX\Map\Bridge\Google\Option\StreetViewControlOptions;
use Symfony\UX\Map\Bridge\Google\Option\FullscreenControlOptions;

$googleOptions = (new GoogleOptions())
    ->mapId('YOUR_MAP_ID')
    ->gestureHandling(GestureHandling::GREEDY)
    ->backgroundColor('#f00')
    ->doubleClickZoom(true)
    ->zoomControlOptions(new ZoomControlOptions(
        position: ControlPosition::BLOCK_START_INLINE_END,
    ))
    ->mapTypeControlOptions(new MapTypeControlOptions(
        mapTypeIds: ['roadmap'],
        position: ControlPosition::INLINE_END_BLOCK_START,
        style: MapTypeControlStyle::DROPDOWN_MENU,
    ))
    ->streetViewControlOptions(new StreetViewControlOptions(
        position: ControlPosition::BLOCK_END_INLINE_START,
    ))
    ->fullscreenControlOptions(new FullscreenControlOptions(
        position: ControlPosition::INLINE_START_BLOCK_END,
    ));

$map->options($googleOptions);

コントロールを無効にするには、次のようにします。

$googleOptions = (new GoogleOptions())
    ->mapId('YOUR_MAP_ID')
    ->zoomControl(false)
    ->mapTypeControl(false)
    ->streetViewControl(false)
    ->fullscreenControl(false);

マップイベント(Stimulus)

カスタム Stimulus コントローラーをアタッチして、基盤となるマップを操作します。

// assets/controllers/custom-map_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    connect() {
        this.element.addEventListener('ux:map:connect', this._onConnect.bind(this));
        this.element.addEventListener('ux:map:marker:after-create', this._onMarkerCreated.bind(this));
    }

    _onConnect(event) {
        const { map, markers } = event.detail;
        console.log('Map ready:', map);
    }

    _onMarkerCreated(event) {
        const { marker, definition } = event.detail;
        if (definition.extra?.category === 'landmark') {
            console.log('Landmark marker created');
        }
    }
}
{{ ux_map(map, {'data-controller': 'custom-map', style: 'height: 400px;'}) }}
利用可能なイベント
イベント詳細説明
ux:map:connect{map, markers}マップが初期化され、使用できる状態になったとき
ux:map:marker:before-create{definition}マーカーが追加される前
ux:map:marker:after-create{marker, definition}マーカーが追加された後
ux:map:polygon:before-create{definition}ポリゴンが追加される前
ux:map:polygon:after-create{polygon, definition}ポリゴンが追加された後
ux:map:polyline:before-create{definition}ポリラインが追加される前
ux:map:polyline:after-create{polyline, definition}ポリラインが追加された後
ux:map:circle:before-create{definition}円が追加される前
ux:map:circle:after-create{circle, definition}円が追加された後
ux:map:info-window:before-create{definition}情報ウィンドウが作成される前
ux:map:info-window:after-create{infoWindow, definition}情報ウィンドウが作成された後

LiveComponent との連携

ComponentWithMapTrait を使って、マップをリアクティブにします。

namespace App\Twig\Components;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\Map\InfoWindow;
use Symfony\UX\Map\Live\ComponentWithMapTrait;
use Symfony\UX\Map\Map;
use Symfony\UX\Map\Marker;
use Symfony\UX\Map\Point;

#[AsLiveComponent]
final class MapSearch
{
    use DefaultActionTrait;
    use ComponentWithMapTrait;

    #[LiveProp(writable: true)]
    public string $query = '';

    protected function instantiateMap(): Map
    {
        $map = (new Map())
            ->center(new Point(48.8566, 2.3522))
            ->zoom(12)
            ->fitBoundsToMarkers();

        foreach ($this->getFilteredLocations() as $location) {
            $map->addMarker(new Marker(
                position: new Point($location->lat, $location->lng),
                title: $location->name,
                infoWindow: new InfoWindow($location->name),
            ));
        }

        return $map;
    }

    private function getFilteredLocations(): array
    {
        // Filter based on $this->query
        return [];
    }
}
{# templates/components/MapSearch.html.twig #}
<div {{ attributes }}>
    <input type="search" data-model="debounce(300)|query" placeholder="Search locations...">
    {{ ux_map(this.map, {style: 'height: 400px;'}) }}
</div>

主な原則

PHP で構築し、Twig でレンダリングします。 Map オブジェクトはデータモデルです。データベースやサービスにアクセスできる PHP でマーカー、ポリゴン、円を追加します。Twig では、単一の関数呼び出しでレンダリングします。

レンダラーは切り替え可能です。 Leaflet と Google Maps は相互に切り替えられます。レンダラーにかかわらず、PHP コードでは同じ Map、Marker、Point クラスを使用します。異なるのはレンダラー固有のオプションだけです。

カスタムデータには extra を使います。 マーカー、ポリゴン、円の extra パラメーターを使うと、JavaScript イベントで利用できる任意のデータを付加できます。

レンダラーをインストールします。 ベースとなる symfony/ux-map パッケージは PHP API を提供しますが、symfony/ux-leaflet-map(無料)または symfony/ux-google-map(API キーが必要)もインストールする必要があります。レンダラーがないと、マップはレンダリングされません。

Point(lat, lng) は緯度が先です。 これは Google Maps の規約に沿っていますが、GeoJSON の [lng, lat] とは逆です。順序を間違えると、マーカーが誤った場所や海上に表示されます。

fitBoundsToMarkers() を活用しましょう。 中心座標やズームを手動で計算する代わりに、マップにすべてのマーカーが表示されるよう自動調整させます。マーカーがない場合、マップは設定した center と zoom にフォールバックするため、デフォルト値として両方を必ず指定してください。

参考資料

コメント