はじめに
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ということにしましょう。