目的
Expoアプリケーションに地図を導入する
ライブラリの選定。導入手順を整理する
環境
- Expo 4.13.0
- react@17.0.2
- reactNative 4.9.0
目標
- ReactNativeアプリケーションに地図を導入する
- 地図は、現在位置の取得やカスタマイズ可能なラベルを配置できる
- GoogleMapは有償であるため、Open Street Mapを利用する
→断念。詳細はこの記事の最後「OpenStreetMapの導入について」をご確認ください。
もし、みなさんどうしているのか、知見があるようでしたら、共有いただけますと、 大変ありがたいです。
地図の導入方針
React-leafletがをそのままNativeでは使う方法はない。以下のライブラリを候補に検討した
- https://github.com/reggie3/react-native-webview-leaflet
- leafletの機能を使うこtができるが、OpenStreetMapが使えない
選択できない - react-native-maps
- 公式のライブラリだが、leafletほどの多機能ではなさそう
【React Native】「react-native-maps」ライブラリを使ってOpenStreetMapを表示する
https://cpoint-lab.co.jp/article/202011/17919/
- ピンなどの応用
- ピンの代わりにコンポーネントを渡すことも可能
ー> react-native-maps を利用することにした
導入手順 Expo
1) インストール
expo install react-native-maps
2) サンプル画面を設置
- この時点で、標準の地図は表示される
import * as React from 'react';
import MapView from 'react-native-maps';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<MapView style={styles.map} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
map: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
});
3) APIキーを設定
-
Open street mapを利用する場合
(要確認) APIキーはGoogle Mapを利用するために必要なものであり、OpenStreetMapを利用する場合は設定不要? -
Google Mapを利用する場合
Expo公式
https://docs.expo.dev/versions/latest/sdk/map-view/#deploying-google-maps-to-an-android-standalone
Android Maps SDK for Android
https://developers.google.com/maps/documentation/android-sdk/get-api-key
- app.jsonにapiキーを設置
"android": {
"config": {
"googleMaps" :{
"apiKey" : "{APIKEYを設定する}"
}
}
※ ストアリリースしていないため、実際に本番で正しく動くか確認できていません。リリース時に詳細追記します
4) Mapのカスタマイズ
- MapView周りを作り込みます
- インポートを追加し、UrlTile, Markerを追加します
- 初期表示位置を
region
にステートで持たせます - urlTemplate に、Opem Street Mapのタイルを設定します(設定する場合)
- MapViewの中に、Markerコンポーネントを配置することでマーカーを設置します
- onRegionChangeは画面表示位置の変更を検知します。regionで受け取った値を、位置をもつstatenregionに設定することで、表示位置を更新します
import MapView, { UrlTile, Marker } from 'react-native-maps';
class Map extends React.Component {
constructor(props) {
super(props);
this.state = {
// 初期表示位置を設定
region: {
latitude: 35.645736,
longitude: 139.747575,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
},
urlTemplate: 'http://c.tile.openstreetmap.org/{z}/{x}/{y}.jpg',
markers: [
{
key: 'tamachiStation',
latlng: {
latitude: 35.645736,
longitude: 139.747575,
},
title: '田町駅',
description: '田町ニューデイズ',
},
],
};
}
onRegionChange(region) {
console.log("onReasionChange");
console.log(region);
this.setState({ region });
}
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={this.state.region}
onRegionChange={this.onRegionChange.bind(this)}
>
<UrlTile
urlTemplate={this.state.urlTemplate}
maximumZ={19}
mapType={'none'}
/>
{this.state.markers.map(marker => (
<Marker
key={marker.key}
coordinate={marker.latlng}
title={marker.title}
description={marker.description}
/>
))}
</MapView>
</View>
);
}
}
5) 現在位置の取得
- パーミッションを追加
"android": {
"versionCode": 1,
"package": "com.miraiyamaneko.samplepackagename",
"googleServicesFile": "./google-services.json",
"permissions": [
"CAMERA",
"WRITE_EXTERNAL_STORAGE",
"CAMERA_ROLL"
],
- 以下のコードを追加
import * as Location from 'expo-location';
- 画面読み込み後に現在位置を取得するメソッド実行
- パーミッションを確認し、Grandなら位置情報を取得しStateに設定
componentDidMount() {
console.log("画面起動")
// 現在位置を読み込む
this._getLocationAsync();
}
// 現在位置の取得
_getLocationAsync = async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
console.log("Status");
console.log(status);
if (status !== 'granted') {
this.setState({
submitMessage: '位置情報の取得が許可されませんでした。',
});
}else if(status === 'granted'){
console.log("getCurrentPositionAsync");
await Location.getCurrentPositionAsync({}).then((location) => {
let longitude = '経度:' + JSON.stringify(location.coords.longitude);
let latitude = '緯度:' + JSON.stringify(location.coords.latitude);
console.log(longitude);
console.log(latitude);
// 現在位置をMap Viewの中心に更新
this.setState({
region: {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}
})
}).catch((e) => {
console.log("現在位置取得失敗");
console.log(e);
});
}
};
-
iOSSimulator利用の場合は、シミュレータに位置情報を設定する
Location.getCurrentPositionAsync({})実行時に、レスポンスがない、または次のエラーが発生する場合は、シミュレータに現在位置が設定されていない
Cannot obtain current location: Error Domain=kCLErrorDomain Code=0 "(null)"
シミュレータのfeatures>Location>CustomLocationを選択し、緯度・軽度を設定する -
コード全体
OpenStreetMapの導入について
- 問題事象
<UrlTile
urlTemplate={this.state.urlTemplate}
maximumZ={19}
/>
mapタイルを定義する上記を設定しても、OpenStreetMapの画面が表示されない
Androidでのみ発生する
- 原因・対策
https://github.com/react-native-maps/react-native-maps/issues/3941
OSMのポリシー変更
https://operations.osmfoundation.org/policies/tiles/ - OSMのデータを利用すること自体は問題ないが、タイルサーバの運用コストが嵩んでいるため規約が変更された
- 自身でタイルサーバを設置することが推奨されている
https://switch2osm.org/serving-tiles/
参考資料
react-native-mapsを使って地図を表示
https://honmushi.com/2019/12/13/expo-map/
【React Native】「react-native-maps」ライブラリを使ってOpenStreetMapを表示する
https://cpoint-lab.co.jp/article/202011/17919/
React Native と OpenStreetMap で散歩用のiOS地図アプリを作ってみた
https://qiita.com/lfcd85/items/248d944592b93c2c009e
Expo 公式ドキュメント
https://docs.expo.dev/versions/latest/sdk/map-view/
現在位置の取得方法
Expoを使ったGPS情報取得の実装
https://honmushi.com/2019/12/10/expo-location/
通常のReactへのleafletの導入
React-leafletの使い方メモ
https://qiita.com/studio_haneya/items/fbb52fa03ab4f212ced0
Leaflet + OpenStreetMap で地図情報を扱うサンプルコード
https://qiita.com/TakeshiNickOsanai/items/783caa9f31bcf762da16