overpassからfetchを使用して、トイレマップを取得したい

# はじめに

ReactNativeで地図上にといてを表示したいと思い立ち、
overpassからfetchを使ってトイレ情報を取得しようとしたところ
Status:400になってしまい、取得できないことがあった。

解決したので、同じところでつまづかないようにするためにこの記事を書いていく。

#ソース

```App.js
import React, { Component } from 'react';
import { StyleSheet ,Text, View,TouchableOpacity} from 'react-native';
import MapView from 'react-native-maps';
import {point } from '@turf/helpers'
import destination from '@turf/destination'

export default class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      elements:[],
      south:null,
      west:null,
      north:null,
      east:null,
    }
  }
  onRegionChangeComplete = (region) =>{
    const center = point([region.longitude,region.latitude])

    const verticalMeter = 111 * region.latitudeDelta / 2
    const horizontalMeter = 111 * region.longitudeDelta / 2


    const options = {units:'kilometers'}
    const south = destination(center,verticalMeter,180,options)
    const west = destination(center,horizontalMeter,-90,options)
    const north = destination(center,verticalMeter,0,options)
    const east = destination(center,horizontalMeter,90,options)

    this.setState({
      south:south.geometry.coordinates[1],
      west:west.geometry.coordinates[0],
      north:north.geometry.coordinates[1],
      east:east.geometry.coordinates[0],
    })
  }
  fetchToilet = async () =>{
    const south = this.state.south
    const west = this.state.west
    const north = this.state.north
    const east = this.state.east
    const body = `out:json;(node(${south},${west},${north},${east})[amenity=toilets];);out;`
    const options = {
      method:'POST',
      body:body,
    }
    try{
      const response = await fetch('https://overpass-api.de/api/interpreter',options)
      if(response.ok){
        const json = await response.json()
        this.setState({elements:json.elements})
      }else{
        console.log('失敗')
      }
      
    }catch(e){
        console.log(e)
    }
  }
  render() {
    return (
      <View style={styles.container}>
        <MapView
          onRegionChangeComplete={this.onRegionChangeComplete}
          style={styles.map}
          initialRegion={{
            latitude: 35.681262,
            longitude: 139.766403,
            latitudeDelta: 0.0922,
            longitudeDelta: 0.0421,
          }}
        >
          {
            this.state.elements.map((element)=>{
              let title = 'トイレ'
              if(element.tags["name"] !== undifined){
                title  = element.tags["name"]
              }
              return (<MapView.Marker
                coordinate={{
                  latitude:element.lat,
                  longitude:element.lon
                }}
                title={title}
                id={"id_"+element.id}
                />)
            })
          }
        
        </MapView>
        <View style={styles.buttonContainer}>
          <TouchableOpacity
            onPress={()=>this.fetchToilet()}
            style={styles.button}
            >
              <Text style={styles.buttonItem}>トイレ取得</Text>
            </TouchableOpacity>

        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container:{
    flex:1,
    backgroundColor:'#fff',
    alignItems:'center',
    justifyContent:'center',
  },
  map: { ...StyleSheet.absoluteFillObject, },
  button:{
    width:150,
    alignItems:'center',
    justifyContent:'center',
    backgroundColor:'rgba(255,255,255,0.7)',
    paddingHorizontal:18,
    paddingVertical:12,
    borderRadius:20,

  },
  buttonContainer:{
    flexDirection:'row',
    marginVertical:20,
    backgroundColor:'transparent',
    alignItems:'center',
  },
  buttonItem:{
    textAlign:'center',
  },
});
```


# 修正したソース
```App.js
import React, { Component } from 'react';
import { StyleSheet ,Text, View,TouchableOpacity} from 'react-native';
import MapView from 'react-native-maps';
import {point } from '@turf/helpers'
import destination from '@turf/destination'

export default class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      elements:[],
      south:null,
      west:null,
      north:null,
      east:null,
    }
  }
  onRegionChangeComplete = (region) =>{
    const center = point([region.longitude,region.latitude])

    const verticalMeter = 111 * region.latitudeDelta / 2
    const horizontalMeter = 111 * region.longitudeDelta / 2


    const options = {units:'kilometers'}
    const south = destination(center,verticalMeter,180,options)
    const west = destination(center,horizontalMeter,-90,options)
    const north = destination(center,verticalMeter,0,options)
    const east = destination(center,horizontalMeter,90,options)

    this.setState({
      south:south.geometry.coordinates[1],
      west:west.geometry.coordinates[0],
      north:north.geometry.coordinates[1],
      east:east.geometry.coordinates[0],
    })
  }
  fetchToilet = async () =>{
    const south = this.state.south
    const west = this.state.west
    const north = this.state.north
    const east = this.state.east
    const body = `[out:json];(node[amenity=toilets](${south},${west},${north},${east}););out;`
    const options = {
      method:'POST',
      body:body,
    }
    try{
      const response = await fetch('https://overpass-api.de/api/interpreter',options)
      if(response.ok){
        const json = await response.json()
        this.setState({elements:json.elements})
      }else{
        console.log('失敗')
      }
      
    }catch(e){
        console.log(e)
    }
  }
  render() {
    return (
      <View style={styles.container}>
        <MapView
          onRegionChangeComplete={this.onRegionChangeComplete}
          style={styles.map}
          initialRegion={{
            latitude: 35.681262,
            longitude: 139.766403,
            latitudeDelta: 0.0922,
            longitudeDelta: 0.0421,
          }}
        >
          {
            this.state.elements.map((element)=>{
              let title = 'トイレ'
              // if(element.tags["name"] !== undifined){
              //   title  = element.tags["name"]
              // }
              return (<MapView.Marker
                coordinate={{
                  latitude:element.lat,
                  longitude:element.lon
                }}
                title={title}
                id={"id_"+element.id}
                />)
            })
          }
        
        </MapView>
        <View style={styles.buttonContainer}>
          <TouchableOpacity
            onPress={()=>this.fetchToilet()}
            style={styles.button}
            >
              <Text style={styles.buttonItem}>トイレ取得</Text>
            </TouchableOpacity>

        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container:{
    flex:1,
    backgroundColor:'#fff',
    alignItems:'center',
    justifyContent:'center',
  },
  map: { ...StyleSheet.absoluteFillObject, },
  button:{
    width:150,
    alignItems:'center',
    justifyContent:'center',
    backgroundColor:'rgba(255,255,255,0.7)',
    paddingHorizontal:18,
    paddingVertical:12,
    borderRadius:20,

  },
  buttonContainer:{
    flexDirection:'row',
    marginVertical:20,
    backgroundColor:'transparent',
    alignItems:'center',
  },
  buttonItem:{
    textAlign:'center',
  },
});
```

修正した箇所を抜き出してみると

修正前

```
const body = `out:json;(node(${south},${west},${north},${east})[amenity=toilets];);out;`
```

修正後

```
const body = `[out:json];(node[amenity=toilets](${south},${west},${north},${east}););out;`
```

out:jsonを[]でくくっただけ、兎にも角にもトイレが地図上に表示できたのでOKということにしましょう。