[React Native] react-native-maps で getBounds をしたい

公開:2020/10/14
更新:2020/10/15
5 min読了の目安(約4500字TECH技術記事

この記事の対象

  • react-native-mapsを用いて開発を始めたばかりの方
  • 地図表示範囲の四隅の緯度経度を用いて何か処理をしたい
  • LeafletやMapboxGLに慣れているから四隅の緯度経度取得がすんなり出来ないのは困るなあ、、、なんて方

前提として、環境構築の説明はしません。react-native-mapsで地図を表示するところまでは完了しているものとします。

正しい理解の手引き

それでは早速解説に入りますが、一応ググればだいたい↓のようなページがヒットしませんか?
Stack Oveflow
他のページでもだいたい同じことが説明されていますね。

const getBoundingBox = (region) => ([
  region.longitude - region.longitudeDelta, // westLng
  region.latitude - region.latitudeDelta, // southLat
  region.longitude + region.longitudeDelta, // eastLng
  region.latitude + region.latitudeDelta // northLat
])

しかしながら、これらの回答は大きな手引きにはなるのですが、回答に添付されたソースコードが少しだけ間違っていますね。
回答者は本当に理解をしてんのか?と難癖を付けたくなりますが、それは置いておいて、以下に正しい理解をしていただくための説明をしていきます。

まず手始めに、react-native-mapsを扱う上で、latitude(緯度)、longitude(経度)の他に、 latitudeDeltalongitudeDelta についてはきちんと理解をされていますでしょうか。ちゃんと理解されている方であればこのような記事は読む必要がないと思いますので、わからないよ、という方のために簡単に説明させていただきます。

latitudeDelta longitudeDelta とは

  • latitudeDelta は 地図表示範囲内の北端と南端の緯度の差
  • longitudeDelta は 地図表示範囲内の東端と西端の経度の差

以上!!!簡単!!!まあ Delta って言ってんだから当たり前じゃんって感じですが。

すなわちズーム率を大きくすればこの Delta は小さくなっていきますし、ズーム率を小さくすればこの Delta は大きくなっていくということです。イメージは掴んでいただけますね?

ここまで来れば↑のソースコード何が違うかわかりますでしょうか。

例えば、

import MapView from 'react-native-maps';
...
<MapView onRegionChangeComplete={ region => doSomething(region) } />

onRegionChangeComplete で渡される引数 region は以下のようなオブジェクトになっています。

{
  latitude: 35.685175, // 表示範囲の中心緯度
  longitude: 139.7528, // 表示範囲の中心経度
  latitudeDelta: 0.05, // 表示範囲の北端と南端の緯度の差
  longitudeDelta: 0.05 // 表示範囲の東端と西端の経度の差
}

このオブジェクトを用いて北端と南端の緯度を求めてみましょう
緯度の中心が latitude であり、北端と南端の緯度の差が latitudeDelta であるので、
中心から latitudeDelta / 2 北にずれれば表示範囲の北端が、南にずれれば表示範囲の南端が求まるということです。経度についても同様に求まります。

つまり、

App.jsx
import React, { Component } from 'react';
import MapView from 'react-native-maps';

class App extends Component {
  constructor(props){
    super(props);
    this.state={};
  }
  
  getBounds = region => {
    let west = region.longitude - region.longitudeDelta / 2;
    let south = region.latitude - region.latitudeDelta / 2;
    let east = region.longitude + region.longitudeDelta / 2;
    let north = region.latitude + region.latitudeDelta / 2;
  
    // do something
  }

  render(){
    return (
      <MapView
        style={{ flex: 1 }}
        onRegionChangeComplete={ region => this.getBounds(region) }
      />
    )
  }
}

export default App;

こんな感じでうまく四隅の緯度経度が取得できます。お試しあれ。

おわりに

ちゃんと四隅の緯度経度が取得できてるのか視覚的にわかりづらかったので、一応↓のようなソースコードで四隅に円を描画させてみました。

  • ソースコード
App.jsx
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import MapView, { PROVIDER_GOOGLE, Circle } from 'react-native-maps';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      initialRegion: {
        latitude: 35.685175,
        longitude: 139.7528,
        latitudeDelta: 0.05,
        longitudeDelta: 0.05
      },
      ne: { latitude: 35.735175, longitude: 139.8028 },
      se: { latitude: 35.635175, longitude: 139.8028 },
      sw: { latitude: 35.635175, longitude: 139.7028 },
      nw: { latitude: 35.735175, longitude: 139.7028 },
    }
  }

  setBounds = region => {
    let west = region.longitude - region.longitudeDelta / 2;
    let south = region.latitude - region.latitudeDelta / 2;
    let east = region.longitude + region.longitudeDelta / 2;
    let north = region.latitude + region.latitudeDelta / 2;
    this.setState({
      ne: { latitude: north, longitude: east },
      se: { latitude: south, longitude: east },
      sw: { latitude: south, longitude: west },
      nw: { latitude: north, longitude: west },
    })
  }

  render() {
    return (
      <MapView
        style={styles.container}
        showsUserLocation
        showsMyLocationButton
        followsUserLocation
        provider={PROVIDER_GOOGLE}
        initialRegion={this.state.initialRegion}
        minZoomLevel={18}
        maxZoomLevel={18}
        onRegionChange={region => this.setBounds(region)}
      >
        <Circle center={this.state.ne} radius={100} />
        <Circle center={this.state.se} radius={100} />
        <Circle center={this.state.sw} radius={100} />
        <Circle center={this.state.nw} radius={100} />
      </MapView>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

export default App;
  • 結果のスクリーンショット

うまくいきました。
この記事の内容に間違いがあればボコボコに叩いてください!では!