reactleafletのマーカー表示の仕方


# reactleafletとは

reactのライブラリで完全無料で地図データを扱うことができます。

# 今回作成したアプリ

技育博というハッカソンで実際に作ったアプリが「バズっている場所を表す」という趣旨でした。地図の機能として地図上にバズっている場所を表示、ズームしていくにつれバズ度が低い場所も表示する、数字もhtml,cssと共に表示するというものでした。

# 地図の種類

react leafletは単なるライブラリなので地図の種類はこちらで設定することができます。
[地図の種類例](https://leaflet-extras.github.io/leaflet-providers/preview/)

# 現在地マーカーの表示方法

公式のコピペです。とりあえず固定値で。

```react:MapPage.tsx
const position = [51.505, -0.09]

return(
  <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
    <TileLayer
      attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    />
    <Marker position={position}>
    </Marker>
  </MapContainer>
)
```

Markerタグはnode_modules/leaflet/dist/imagesに入っているマーカーアイコンです。このアイコン、開発環境では表示されるのですがデプロイした後は表示されなくなりました。なので変更する必要があります。

```react:MapPage.tsx
const customMarker = () => {

    return L.icon({
      iconUrl: "/img/blue.svg",
      iconSize: [35, 35],
      className: "marker",
    });
  };

<Marker position={arrCenter} icon={customMarker()}/>
```

javascriptで現在地の取得ができます。

```react:top.tsx
type CurrentPosition = {
  latitude: number;
  longitude: number;
};

  const [correntposition, setCorrentPosition] = useState<CurrentPosition>({
    latitude: 35.6586205576023,
    longitude: 139.74543043734087,
  });

  //現在地の取得
  useEffect(() => {
    navigator.geolocation.getCurrentPosition((position) => {
      const { latitude, longitude } = position.coords;
      setCorrentPosition({ latitude, longitude });
    });
  }, []);
```

これをさっきのpositionに代入します。

```react:MapPage
const position = [correntposition.latitude, correntposition.longitude]

return(
  <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
    <TileLayer
      attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    />
    <Marker position={position}>
    </Marker>
  </MapContainer>
)
```

# ピンを置く方法

このarrDistanceごとにピンを置きたい。

```react
arrDistance = [
  {
    latutude:35.7001517　//緯度
    likes:"892.5k" //バズ度(tiktokで押されたバズ度)
    longitude:139.7007061 //経度
    place:"東京都新宿区新大久保"  //場所名
  },{
    latutude:35.7194294
    likes:"525.4k"
    longitude:139.7952466
    place:"浅草にある老舗喫茶店「しゃん」"
  }
]
```

```react:MapPage.tsx
function SetViewOnClick() {
  const map = useMapEvent("click", (e) => {
    map.setView(e.latlng, map.getZoom(), {
      animate: true,
    });
  });

  return null;
}

  <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
    <TileLayer
      attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    />
    <SetViewOnClick />
    <Marker position={position}>
      <PinLocate
          arrDistance={arrDistance}
          correntposition={correntposition}
      />
    </Marker>
  </MapContainer>
```

SetViewOnClickでmap上をタップした時にそこに動くようになります。

```react:PinLocate.tsx

function PinLocate({ arrDistance }: PinLocateProps) {


  const uniqueArrDistance = Array.from(new Map(arrDistance.map((distance) => [distance.latitude, distance])).values());

   return (
    <div>
    {uniqueArrDistance.map((place, index) => (
        <Marker
          position={[place.latitude, place.longitude]}
          key={index}
        />
    ))}
    </div>
  );
}
```

uniqueArrDistanceでユニークな値にします。これでarrDistanceの値ごとで地図上にマーカーを置くことができます。

# ピン周りに文字を入力する

ピンにarrDistanceのlikes、バズ度を表示させます。
before
![スクリーンショット 2025-04-23 11.27.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990216/b93bf20e-a0a4-475a-abb6-bcf5e016d72d.png)
after
![スクリーンショット 2025-04-23 11.27.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990216/8ac5008b-42a5-45d7-b0e8-7ec6f63b7efc.png)

```react:PinLocate.tsx

function PinLocate({ arrDistance }: PinLocateProps) {

import blueicon from "../../public/img/blueIcon.svg";
import fireicon from "../../public/img/fireIcon.svg";
import greenicon from "../../public/img/greenIcon.svg";
import redIcon from "../../public/img/redIcon.svg";

  function Icon(location: { likes: number; place: string }) {
    const showIcon =
      location.like < 10000 ? {icon:blueicon , width:"120", stopColor:"#4FF8F8",startColor:"#4B89ED"} : location.like >= 10000 && location.like < 100000? {icon:greenicon,width:"60",stopColor:"#A7F84F",startColor:"#4BED74"} : location.like >= 100000 && location.like < 500000 ? {icon:redIcon,width:"140",stopColor:"#F84F90",startColor:"#ED4B4B"} : {icon:fireicon,width:"160"};

    const textSVG =
      zoomLevel > 15
        ? `
          <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle"
        font-size="20" font-weight="bold" fill="white"
        stroke="#ED4B4B" stroke-width="1.2">
        ${location.likes}
      </text>
          `
        : "";


    return L.divIcon({
      className: "custom-marker",
      html: `
              <div style="position: relative; text-align: center;">
      <img src=${showIcon.icon} style="width: 50px; height: 50px;" />

      <div style="position: absolute; top: 140%; left: 130%; transform: translate(-50%, -210%);">
        <svg viewBox="0 0 100 30" width=${showIcon.width} height="40">
          ${textSVG}
        </svg>


      </div>
    </div>
              `,
      iconSize: [50, 50],
      iconAnchor: [25, 25],
    });
  }

  const uniqueArrDistance = Array.from(new Map(arrDistance.map((distance) => [distance.latitude, distance])).values());

   return (
    <div>
    {uniqueArrDistance.map((place, index) => (
        <Marker
          icon={Icon(place)}
          position={[place.latitude, place.longitude]}
          key={index}
        />
    ))}
    </div>
  );
}
```

**showIcon**
scaleごとにアイコンの大きさとアイコンの色を変えるための条件分です。

**textSVG**
もしズームサイズが15より上の時はアイコンの周りにバズ度の表示をします。


# おまけ：ピンを選択したら画面真ん中に来るように
googlemapのようにマーカーを選択したら、画面真ん中に選択したマーカーが移動してくるようにしました。

```react:PinLocate.tsx
function PinLocate({ arrDistance }: PinLocateProps) {

import blueicon from "../../public/img/blueIcon.svg";
import fireicon from "../../public/img/fireIcon.svg";
import greenicon from "../../public/img/greenIcon.svg";
import redIcon from "../../public/img/redIcon.svg";

  function Icon(location: { likes: number; place: string }) {

    const handleOpen = () => {
      map.setView([place.latitude, place.longitude], 13, {
      animate: true,
    });
    }

   return (
    <div>
    {uniqueArrDistance.map((place, index) => (
        <Marker
          icon={Icon(place)}
          position={[place.latitude, place.longitude]}
          key={index}
          eventHandlers={{ click: () => handleOpen(place) }}
        />
    ))}
    </div>
  );
}
```


 **map.setView([place.latitude, place.longitude], 13)**
 これの13というのがズームサイズです。

 ## 最後に
 leafletはもともとjavascriptのライブラリなので文献もjavascriptに関しての記載の方が多くて大変でした。誰かの参考になれたら幸いです。