十八、地理位置和地图
在本章中,您将了解 React Native 的地理定位和映射功能。我们将从使用地理定位 API 开始;然后我们将继续使用MapView
组件绘制感兴趣的点和区域。
我们将依靠react-native-maps
包来实现 maps(https://github.com/airbnb/react-native-maps 。本章的目标是介绍 React Native for geolocation 和 React Native Maps for Maps 中的可用内容。
我在哪里?
web 应用用来确定用户所在位置的地理定位 API 也可以由 React 本地应用使用,因为相同的 API 已经过多次填充。在地图之外,此 API 对于从移动设备上的 GPS 获取精确坐标非常有用。然后,我们可以使用这些信息向用户显示有意义的位置数据。
不幸的是,地理定位 API 返回的数据本身用处不大;您的代码必须完成腿部工作才能将其转换为有用的内容。例如,纬度和经度对用户来说没有任何意义,但我们可以使用这些数据查找对用户有用的内容。这可能与显示用户当前所在的位置一样简单。
让我们实现一个示例,使用 React Native 的地理位置 API 查找坐标,然后使用这些坐标从 Google Maps API 查找人类可读的位置信息:
import React, { Component } from 'react';
import {
AppRegistry,
Text,
View,
} from 'react-native';
import { fromJS } from 'immutable';
import styles from './styles';
// For fetching human-readable address info.
const URL = 'https://maps.google.com/maps/api/geocode/json?latlng=';
class WhereAmI extends Component {
// The "address" state is "loading..." initially because
// it takes the longest to fetch.
state = {
data: fromJS({
address: 'loading...',
}),
}
// Getter for "Immutable.js" state data...
get data() {
return this.state.data;
}
// Setter for "Immutable.js" state data...
set data(data) {
this.setState({ data });
}
// We don't setup any geo data till the component
// is ready to mount.
componentWillMount() {
const setPosition = (pos) => {
// This component renders the "coords" data from
// a geolocation response. This can simply be merged
// into the state map.
this.data = this.data.merge(pos.coords);
// We need the "latitude" and the "longitude"
// in order to lookup the "address" from the
// Google Maps API.
const {
coords: {
latitude,
longitude,
},
} = pos;
// Fetches data from the Google Maps API then sets
// the "address" state based on the response.
fetch(`${URL}${latitude},${longitude}`)
.then(resp => resp.json(), e => console.error(e))
.then(({
results: [
{ formatted_address },
],
}) => {
this.data = this.data
.set('address', formatted_address);
});
};
// First, we try to lookup the current position
// data and update the component state.
navigator.geolocation.getCurrentPosition(setPosition);
// Then, we setup a high accuracy watcher, that
// issues a callback whenever the position changes.
this.watcher = navigator.geolocation.watchPosition(
setPosition,
err => console.error(err),
{ enableHighAccuracy: true }
);
}
// It's always a good idea to make sure that this
// "watcher" is cleared when the component is removed.
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watcher);
}
render() {
// Since we want to iterate over the properties
// in the state map, we need to convert the map
// to pairs using "entries()". Then we need to
// use the spread operator to make the map iterator
// into a plain array. The "sort()" method simply
// sorts the map based on it's keys.
const state = [
...this.data
.sortBy((v, k) => k)
.entries(),
];
// Iterates over the state properties and renders them.
return (
<View style={styles.container}>
{state.map(([k, v]) => (
<Text key={k} style={styles.label}>
{`${k[0].toUpperCase()}${k.slice(1)}`}: {v}
</Text>
))}
</View>
);
}
}
AppRegistry.registerComponent(
'WhereAmI',
() => WhereAmI
);
该组件的目标是在屏幕上呈现地理定位 API 返回的属性,以及查找用户的特定位置并显示它。如果你看一看componentWillMount()
方法,你会发现这就是大多数有趣的代码所在。我们创建了一个setPosition()
函数,在几个地方用作回调函数。它的任务是设置组件的状态。
首先,它设置coords
属性。通常,我们不会直接显示这些数据,但这是一个显示作为地理定位 API 一部分可用的数据的示例。其次,它使用latitude
和longitude
值,使用谷歌地图 API 查找用户当前所在位置的名称。
setPosition()
回调与getCurrentPosition()
一起使用,组件挂载时只调用一次。我们还将setPosition()
与watchPosition()
一起使用,它在用户位置发生变化时随时调用回调。
注
iOS emulator 和 Genymotion 允许您通过菜单选项更改位置。您不必每次都在物理设备上安装应用来测试更改位置。
让我们看看加载位置数据后此屏幕的外观:
如您所见,我们获取的地址信息在应用中可能比纬度和经度数据更有用。比物理地址文本更好的是在地图上可视化用户的物理位置;我们将在下一节讨论这个问题。
我周围是什么?
来自react-native-maps
的MapView
组件是在 React 本机应用中渲染贴图的主要工具。
让我们实现一个基本的MapView
组件,看看我们能从中得到什么:
import React from 'react';
import {
AppRegistry,
View,
} from 'react-native';
import MapView from 'react-native-maps';
import styles from './styles';
const WhatsAroundMe = () => (
<View style={styles.container}>
<MapView
style={styles.mapView}
showsUserLocation
followUserLocation
/>
</View>
);
AppRegistry.registerComponent(
'WhatsAroundMe',
() => WhatsAroundMe
);
没什么大不了的。您传递给MapView
的两个布尔属性为您做了大量工作。showsUserLocation
属性将激活地图上的标记,该标记表示运行此应用的设备的物理位置。followUserLocation
属性告诉地图在设备移动时更新位置标记。让我们看看结果图:
设备的当前位置在地图上清楚地标出。默认情况下,兴趣点也会在地图上渲染。这些东西离用户很近,这样他们就可以看到周围的东西。
在使用showsUserLocation
时,通常最好使用followUserLocation
属性。这将使地图缩放到用户所在的区域。
注释兴趣点
到目前为止,您已经看到了MapView
组件如何呈现用户的当前位置和用户周围的兴趣点。这里的挑战是,您可能希望显示与应用相关的兴趣点,而不是默认呈现的兴趣点。
在本节中,您将学习如何渲染地图上特定位置的标记,以及如何渲染地图上的区域。
标绘点
让我们规划一些当地的酿酒厂,好吗?以下是如何将注释传递给MapView
组件:
import React from 'react';
import {
AppRegistry,
View,
} from 'react-native';
import MapView from 'react-native-maps';
import styles from './styles';
const PlottingPoints = () => (
<View style={styles.container}>
<MapView
style={styles.mapView}
showsPointsOfInterest={false}
showsUserLocation
followUserLocation
>
<MapView.Marker
description="Duff beer for me, Duff beer for you"
coordinate={{
latitude: 43.8418728,
longitude: -79.086082,
}}
/>
<MapView.Marker
description="New! Patriot Light!"
coordinate={{
latitude: -79.086082,
longitude: -79.085407,
}}
/>
</MapView>
</View>
);
AppRegistry.registerComponent(
'PlottingPoints',
() => PlottingPoints
);
注释就是它们听起来的样子,是在基本地图地理之上呈现的附加信息。事实上,在渲染MapView
组件时,默认情况下会得到注释,因为它们会显示感兴趣的点。在本例中,我们通过将showsPointsOfInterest
属性设置为 false 来选择退出此功能。现在的焦点完全集中在啤酒上。让我们看看这些啤酒厂的位置:
按下地图上显示啤酒厂位置的标记时,将显示详图索引。您给<MapView.Marker>
的title
和description
属性值用于呈现此文本。
绘制覆盖图
在本章的最后一节中,您将学习如何渲染区域覆盖。点是单个纬度/经度坐标。将一个区域视为多个坐标点的连接图。地区可以用于许多目的,例如显示我们在哪里更可能找到 IPA 饮酒者而不是肥胖饮酒者。下面是代码的样子:
import React, { Component } from 'react';
import {
AppRegistry,
View,
Text,
} from 'react-native';
import MapView from 'react-native-maps';
import { fromJS } from 'immutable';
import styles from './styles';
// The "IPA" region coordinates and color...
const ipaRegion = {
coordinates: [
{ latitude: 43.8486744, longitude: -79.0695283 },
{ latitude: 43.8537168, longitude: -79.0700046 },
{ latitude: 43.8518394, longitude: -79.0725697 },
{ latitude: 43.8481651, longitude: -79.0716377 },
{ latitude: 43.8486744, longitude: -79.0695283 },
],
strokeColor: 'coral',
strokeWidth: 4,
};
// The "stout" region coordinates and color...
const stoutRegion = {
coordinates: [
{ latitude: 43.8486744, longitude: -79.0693283 },
{ latitude: 43.8517168, longitude: -79.0710046 },
{ latitude: 43.8518394, longitude: -79.0715697 },
{ latitude: 43.8491651, longitude: -79.0716377 },
{ latitude: 43.8486744, longitude: -79.0693283 },
],
strokeColor: 'firebrick',
strokeWidth: 4,
};
class PlottingOverlays extends Component {
// The "IPA" region is rendered first. So the "ipaStyles"
// list has "boldText" in it, to show it as selected. The
// "overlays" list has the "ipaRegion" in it.
state = {
data: fromJS({
ipaStyles: [styles.ipaText, styles.boldText],
stoutStyles: [styles.stoutText],
overlays: [ipaRegion],
}),
}
// Getter for "Immutable.js" state data...
get data() {
return this.state.data;
}
// Setter for "Immutable.js" state data...
set data(data) {
this.setState({ data });
}
// The "IPA" text was clicked...
onClickIpa = () => {
this.data = this.data
// Makes the IPA text bold...
.update('ipaStyles', i => i.push(styles.boldText))
// Removes the bold from the stout text...
.update('stoutStyles', i => i.pop())
// Replaces the stout overlay with the IPA overlay...
.update('overlays', i => i.set(0, ipaRegion));
}
// The "stout" text was clicked...
onClickStout = () => {
this.data = this.data
// Makes the stout text bold...
.update('stoutStyles', i => i.push(styles.boldText))
// Removes the bold from the IPA text...
.update('ipaStyles', i => i.pop())
// Replaces the IPA overlay with the stout overlay...
.update('overlays', i => i.set(0, stoutRegion));
}
render() {
const {
ipaStyles,
stoutStyles,
overlays,
} = this.data.toJS();
return (
<View style={styles.container}>
<View>
{ /* Text that when clicked, renders the IPA
map overlay. */ }
<Text
style={ipaStyles}
onPress={this.onClickIpa}
>
IPA Fans
</Text>
{ /* Text that when clicked, renders the stout
map overlay. */ }
<Text
style={stoutStyles}
onPress={this.onClickStout}
>
Stout Fans
</Text>
</View>
{ /* Renders the map with the "overlays" array.
There will only ever be a single overlay
in this array. */ }
<MapView
style={styles.mapView}
showsPointsOfInterest={false}
showsUserLocation
followUserLocation
>
{overlays.map((v, i) => (
<MapView.Polygon
key={i}
coordinates={v.coordinates}
strokeColor={v.strokeColor}
strokeWidth={v.strokeWidth}
/>
))}
</MapView>
</View>
);
}
}
AppRegistry.registerComponent(
'PlottingOverlays',
() => PlottingOverlays
);
如您所见,区域数据由几个纬度/经度坐标组成,这些坐标定义了区域的形状和位置。这段代码的其余部分主要是关于按下两个文本链接时的处理状态。默认情况下,将渲染 IPA 区域:
按下粗壮文本时,将从地图中删除 IPA 覆盖,并添加粗壮区域:
总结
在本章中,您学习了 React Native 中的地理定位和映射。地理定位 API 的工作原理与 web API 相同。在 React 本机应用中使用映射的唯一可靠方法是安装第三方react-native-maps
包。
您看到了基本配置MapView
组件,以及它如何跟踪用户的位置,并显示相关的兴趣点。然后,您了解了如何绘制自己的兴趣点和兴趣区域。
在下一章中,您将学习如何使用与 HTML 表单控件类似的 React 本机组件收集用户输入。
版权属于:月萌API www.moonapi.com,转载请注明出处