Reactとmapbox-glのマーカーにポップアップを表示したい。
ReactもMapboxも初心者です。
React と Mapbox GL JS を使ってベクトルタイルを表示するを参考に、サンプルの通りに作るとちゃんと地図が表示されましたが、App.jsを少し書き換えマーカーと、ポップアップを出すことを試みました。
マーカーにポップアップを表示する時に、緯度経度を直接書き込むと表示できるのですが、下記のようにe.lngLatを入れるとエラーが出てどうしたらいいかわからず。もう一つよくわからないことがあったので下記に質問させていただきます。アドバイス頂けたら助かります。
質問1 .setLngLat(e.lngLat)の設定の仕方
.setLngLat(e.lngLat)
実行すると下記のエラーがでた:
Failed to compile
./src/App.js
Line 92:16: 'e' is not defined no-undef
質問2 ...の意味がわからない
コードの中のonClick中で、...prevStyle,という部分があり、この
「...」の意味がわからず、、これも教えていただけたら助かります。
onClick = () => {
const prevStyle = this.state.style
const nextStyle = {
...prevStyle,
import React, { Component } from 'react'
import mapboxgl from 'mapbox-gl'
import './App.css'
import 'mapbox-gl/dist/mapbox-gl.css'
const BASE_URL = 'https://api.mapbox.com/styles/v1/mapbox/streets-v9'//ここにはJSONが入っている
class App extends Component {
constructor(props) {
super(props)
this.state = { style: false }
}
componentDidMount = async () => {
const url = `${BASE_URL}?access_token=${mapboxgl.accessToken}`//URLにはJSONが入っている
const style = await fetch(url).then(res => res.json())//コードの実行は先に進まない
//非同期でfetchに入ったJsonは地図のタイル
this.map = new mapboxgl.Map({
container: this.container,
center: [138, 36.0],
zoom: 13,
style,
});
//普通のマーカー
var marker = new mapboxgl.Marker()
.setLngLat([138, 36.0])
.addTo(this.map);
//popup .setLngLat(e.lngLat)のeでエラーが出る
var markerHeight = 50, markerRadius = 10, linearOffset = 25;
var popupOffsets = {
'top': [0, 0],
'top-left': [0,0],
'top-right': [0,0],
'bottom': [0, -markerHeight],
'bottom-left': [linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
'bottom-right': [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
'left': [markerRadius, (markerHeight - markerRadius) * -1],
'right': [-markerRadius, (markerHeight - markerRadius) * -1]
};
var popup = new mapboxgl.Popup({offset: popupOffsets, className: 'my-class'})
// .setLngLat([138, 36.0]) だと普通に表示される
.setLngLat(e.lngLat)
.setHTML("<h1>Hello World!</h1>")
.setMaxWidth("300px")
.addTo(this.map);
this.setState({ style })
}
componentDidUpdate(prevProps, prevState) {
if (prevState.style !== this.state.style) {
this.map.setStyle(this.state.style)
}
}
componentWillUnmount() {
this.map.remove()
}
onClick = () => {
const prevStyle = this.state.style
const nextStyle = {
...prevStyle,
layers: prevStyle.layers.map(
layer =>
layer.id === 'landcover_snow'
? { ...layer, paint: { ...layer.paint, 'fill-color': 'red' } }
: layer,
),
}
this.setState({ style: nextStyle })
}
render() {
return (
<div className={'map'} ref={e => (this.container = e)}>
<button
style={{ position: 'absolute', zIndex: 1 }}
onClick={this.onClick}
>
{'氷雪地帯は?'}
</button>
</div>
)
}}
export default App
@chromia さんに教えていただいた Attach a popup to a marker instanceをみて、なんとかポップアップがでるところまではできました。
// src/App.js
import React, { Component } from 'react'
import mapboxgl from 'mapbox-gl'
import './App.css'
import 'mapbox-gl/dist/mapbox-gl.css'
const BASE_URL = 'https://api.mapbox.com/styles/v1/mapbox/streets-v9'//ここにはJSONが入っている
class App extends Component {
constructor(props) {
super(props)
this.state = { style: false}//--:--,ここに付け足して行けばいい?
}
componentDidMount = async () => {//ここで地図の描画をする async
const url = `${BASE_URL}?access_token=${mapboxgl.accessToken}`//URLにはJSONが入っている
const style = await fetch(url).then(res => res.json())//コードの実行は先に進まない
//非同期でfetchに入ったJsonは地図のタイル
this.map = new mapboxgl.Map({
container: this.container,
center: [138, 36.0],
zoom: 13,
style,
});
// create the popup
var popup = new mapboxgl.Popup({ offset: 25 }).setText(
'Popup Comment.'
);
// create DOM element for the marker
var el = document.createElement('div');
el.id = 'marker';
//普通のマーカーは立てることができた
var marker = new mapboxgl.Marker()
.setLngLat([138, 36.0])
.setPopup(popup)
.addTo(this.map);
this.setState({ style })
}//ここで地図の描画をする?end
componentDidUpdate(prevProps, prevState) {
//styleしか渡してないから、markerとpopupも渡せるようにしたい
if (prevState.style !== this.state.style) {
this.map.setStyle(this.state.style)
}
}
componentWillUnmount() {
this.map.remove()
}
onClick = () => {
const prevStyle = this.state.style
const nextStyle = {
...prevStyle,
layers: prevStyle.layers.map(
layer =>
layer.id === 'landcover_snow'
? { ...layer, paint: { ...layer.paint, 'fill-color': 'red' } }
: layer,
),
}
this.setState({ style: nextStyle })
}
render() {
return (
<div className={'map'} ref={e => (this.container = e)}>
<button
style={{ position: 'absolute', zIndex: 1 }}
onClick={this.onClick}
>
{'氷雪地帯は?'}
</button>
</div>
)
}
}
export default App
やや進化
// this.map.on('load', function() {//これだとthisが別のものを指す
// https://stackoverflow.com/questions/47970880/add-geojson-to-this-map-mapbox-gl-js
this.map.on('load', () =>{
//アロー関数だと内部で指定していない場合はグローバル?と同じthisを指す。画面がloadされたらmapもonになる