7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TS + Next.JSでMapLibreの地図を使う方法

Last updated at Posted at 2024-06-26

どうも、最近イコラブにもハマりづつあるベイファンのユエンです。

ReactでMapLibre GL JSを使う方法は「MapTiler」のドキュメントで公開されているんですが、MapTilerのほうのチュートリアルがJSになっていたり、NextJSに対応していないので、この記事では「NextJS」と「Typescript」を使った実装方法にこだわって実装します。

ただ地図のコンポーネントが欲しいだけの方は直接コピペして使ってください。

tsxファイル

'use client';
import { useEffect, useRef } from 'react';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import React from 'react';

type Props = {
    height: string;
    width: string;
};

export default function Map({ height, width }: Props) {
    const mapContainer = useRef<HTMLDivElement | null>(null);
    const map = useRef<maplibregl.Map | null>(null);

    useEffect(() => {
        if (map.current || !mapContainer.current) return;

        map.current = new maplibregl.Map({
            container: mapContainer.current,
            style: {
                version: 8,
                sources: {
                    'base-map': {
                        type: 'raster',
                        tiles: [
                            // 背景地図のURLは適宜置き換えてください
                            'https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg',
                        ],
                        tileSize: 256,
                        attribution:
                            'Map tiles by <a target="_blank" href="https://stamen.com">Stamen Design</a>; Hosting by <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a>. Data &copy; <a href="https://www.openstreetmap.org/about" target="_blank">OpenStreetMap</a> contributors',
                        // 背景地図置き換えたらアトリビューションも忘れずに
                    },
                },
                layers: [
                    {
                        id: 'base-tiles',
                        type: 'raster',
                        source: 'base-map',
                        minzoom: 0,
                        maxzoom: 22,
                    },
                ],
            },
            center: [139.63982660835788, 35.4434200783276],
            zoom: 12,
        });

        map.current.on('load', () => {
            if (!map.current) return;

            map.current.addSource('places', {
                type: 'geojson',
                data: {
                    type: 'FeatureCollection',
                    features: [
                        {
                            type: 'Feature',
                            properties: {
                                title: '横浜スタジアム',
                                description: '\横浜優勝/',
                            },
                            geometry: {
                                type: 'Point',
                                coordinates: [
                                    139.63982660835788, 35.4434200783276,
                                ],
                            },
                        },
                    ],
                },
            });

            map.current.addLayer({
                id: 'places',
                type: 'circle',
                source: 'places',
                paint: {
                    'circle-radius': 10,
                    'circle-color': '#007cbf',
                    'circle-stroke-width': 2,
                    'circle-stroke-color': '#fff',
                },
            });
        });

        map.current.on('click', 'places', (e: maplibregl.MapMouseEvent) => {
            console.log(e);

            if (map.current === null) return;

            const coordinates = e.lngLat;
            const popup = new maplibregl.Popup();

            popup
                .setLngLat(coordinates)
                .setHTML('<p style="color:black">\横浜優勝/</p>')
                .addTo(map.current);
        });

        if (!map.current) return;
    }, []);

    return (
        <div
            ref={mapContainer}
            style={{
                height: height,
                width: width,
            }}
        ></div>
    );
}

詳しくハマりポイントについて説明します。

  1. React系のライブラリでは地図が副作用として扱われています。useEffectフックとuseRefを必ず使うことになるので、CSRできるようにしましょう
  2. おそらく型でハマる人が多いのではないかと思います。地図はMap型で定義されるので、maplibregl.Map型を使いましょう
  3. 地図コンテナの型はHTMLDivElementです
  4. 地図のクリックイベントで呼び出されるのはMapMouseEventです

正直型を定義する点とクライアントサイドレンダリングさせるあたり以外はMapTilerのドキュメントからそのままコピペしてもいいような気がする(なんなら型はCopilotがなんとかしてくれる)んですが、ググっても引っかからないかもしれないんで、とりあえず地図ライブラリを使う方法を載せておきます。

MapLibreはTSで実装するためのドキュメントが若干足りていない気がするので、もしMapLibreをメンテナンスされているMIERUNEのY.Kさんや、K.Iさんがこの記事を読んでいるのであれば、ぜひ型について(できればTSでの実装について)のドキュメントを追加することについて検討してください。

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?