Введение в React Native Maps

Наличие точной информации о местоположении ваших пользователей — отличный способ улучшить взаимодействие с пользователем. Например, вы можете использовать эти данные, чтобы показывать пользователям, что их окружает, предлагать эксклюзивные предложения для продуктов и услуг в их регионе и многое другое.

К счастью, если у вас есть приложение React, реализовать карты с помощью react-native-maps библиотеки не составит труда .

Что такое React Native Maps?

React Native Maps — это система компонентов для карт, которая поставляется с собственным кодом платформы, который необходимо скомпилировать вместе с React Native. В этом руководстве мы покажем, как интегрировать Карты Google в ваше приложение React Native, и познакомим вас с основными компонентами, такими как <MapView /> и <Marker>.

Давайте начнем!

Настройка

Перед визуализацией нашего MapViewкомпонента нам сначала нужно получить ключ API, чтобы включить функции Google Maps в наше приложение. Для этого получите секреты ключ API, выполнив следующие действия .

<application>
   <!-- You will only need to add this meta-data tag, but make sure it's a child of application -->
   <meta-data
     android:name="com.google.android.geo.API_KEY"
     android:value="YOUR_API_KEY"/> <!-- Your key goes here. -->
 
   <!-- You will also only need to add this uses-library tag -->
   <uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application>

Это подключит наш ключ SDK к проекту React Native .

Затем выполните следующую команду для установки react-native-mapsв свой проект.

yarn add react-native-maps -E
npm install react-native-maps
#Expo requires a different version of this package.
# so if you are using Expo, run this instead:
expo install react-native-maps

Настройка вида карты по умолчанию

Удалите весь код по умолчанию из App.jsфайла и импортируйте <MapView />компонент из react-native-maps. Внутри компонента визуализируйте <MapView />компонент.

import React from "react";
import { StyleSheet, Text, View } from "react-native";
import MapView from "react-native-maps";
export default function App() {
  return (
    <View style={styles.container}>
    {/*Render our MapView*/}
      <MapView
        style={styles.map}
        //specify our coordinates.
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0.0922,
          longitudeDelta: 0.0421,
        }}
      />
    </View>
  );
}
//create our styling code:
const styles = StyleSheet.create({
  container: {
    ...StyleSheet.absoluteFillObject,
    flex: 1, //the container will fill the whole screen.
    justifyContent: "flex-end",
    alignItems: "center",
  },
  map: {
    ...StyleSheet.absoluteFillObject,
  },
});

Свойство initialRegionпоказывает регион, который должен отображаться на карте при монтировании компонента. Значение initialRegionне может быть изменено после инициализации.

Настройка вида карты по умолчанию

Не забудьте добавить свойство стиля, чтобы указать размеры; в противном случае вы получите белый экран. Значение гарантирует, что занимает весь экран.{ flex: 1 }<MapView />

Вам нужно будет передать объект со значениями широты, долготы и дельты региона в свойство initialRegion. Свойства latitudeDelta и longitudeDelta определяют, насколько область на карте должна быть увеличена. Для смены региона используйте region prop.

Использование React Native Maps с useState хуком

Чтобы изменить регион с помощью useState Hook, используйте onRegionChangeComplete prop, чтобы установить новый регион в состояние. Это onRegionChangeComplete prop обратного вызова, который запускается, когда пользователь прекращает панорамирование карты.

//extra code removed for brevity.
//create a Hook to store our region data.
const [region, setRegion] = useState({
  latitude: 51.5079145,
  longitude: -0.0899163,
  latitudeDelta: 0.01,
  longitudeDelta: 0.01,
});
return (
  <View style={styles.container}>
    <MapView
      style={styles.map}
      initialRegion={{
        latitude: 37.78825,
        longitude: -122.4324,
        latitudeDelta: 0.0922,
        longitudeDelta: 0.0421,
      }}
      //onRegionChangeComplete runs when the user stops dragging MapView
      onRegionChangeComplete={(region) => setRegion(region)}
    />
    {/*Display user's current region:*/}
    <Text style={styles.text}>Current latitude: {region.latitude}</Text>
    <Text style={styles.text}>Current longitude: {region.longitude}</Text>
  </View>
);
    }}
      // onRegionChangeComplete  запускается  , когда  пользователь  перестает перетаскивать MapView   
      onRegionChangeComplete = {( регион ) => setRegion(регион)} 
    />
    { /*Показать текущий регион пользователя:*/ }
    < Стиль текста = { стили . text } > Текущая широта : { region . широта } </ Text >  
    < Стиль текста = { стили . text } > Текущая долгота : { region . долгота } </ Text >  
  </ Просмотр >
);

Использование React Native Maps с хуком useState

Добавление маркера

Начните с импорта Markerиз .react-native-maps

import { Marker } from "react-native-maps";

Затем визуализируйте компонент как дочерний элемент . Передайте координату маркера в реквизите.<Marker /><MapView />coordinate

import { Marker } from "react-native-maps";
const tokyoRegion = {
  latitude: 35.6762,
  longitude: 139.6503,
  latitudeDelta: 0.01,
  longitudeDelta: 0.01,
};
return (
  <View style={styles.container}>
    <MapView
      style={styles.map}
      initialRegion={tokyoRegion} //your region data goes here.
    >
      {/*Make sure the Marker component is a child of MapView. Otherwise it won't render*/}
      <Marker coordinate={tokyoRegion} />
    </MapView>
  </View>
);

Теперь маркер должен быть виден, как показано ниже.

Теперь маркер виден

Вы можете добавить nнесколько маркеров на карту и передать ее как прямой дочерний элемент компоненту .<MapView />

Вот пример нескольких Markerкомпонентов в одном MapView:

<MapView
  ref={mapRef}
  style={styles.map}
  initialRegion={tokyoRegion}
  onRegionChangeComplete={(region) => setRegion(region)}
>
  <Marker coordinate={tokyoRegion} />
  {/*marker to a nearby location */}
  <Marker
    coordinate={{
      latitude: 35.67714827145542,
      longitude: 139.6551462687416,
    }}
  />
</MapView>;

Multiple markers are now visible in the map view

Настройка маркера карты

Изменение цвета

Чтобы изменить цвет маркера, используйте реквизит pinColor.

<Marker
  coordinate={tokyoRegion}
  pinColor="green"
/>

Здесь мы меняем цвет булавки на green:

Маркер изменился на зеленый

Изменение изображения маркера

Вы также можете добавить собственное изображение маркера, передав imageсвойство компоненту .<Marker />

<Marker
  coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}
  image={require("./japaneseFlag.png")} //uses relative file path. 
/>

Изображение маркера было изменено на японский флаг.

Изменение вида маркера

react-native-mapsтакже позволяет разработчикам использовать настраиваемые представления маркеров. Это может быть полезно в ситуациях, когда вы хотите указать местоположение с помощью символа. Примером может быть отображение Carкомпонента для отображения позиции продавца автомобилей.

Для этого сначала начните с написания кода для создания пользовательского компонента, например:

//create our custom marker component.
function CustomMarker() {
  return (
    <View style={styles.marker}>
      <Text style={styles.color}>Tokyo</Text>
    </View>
  );
}
//styles for our custom marker.
const styles = StyleSheet.create({
  marker: {
    paddingVertical: 10,
    paddingHorizontal: 30,
    backgroundColor: "#007bff",
    borderColor: "#eee",
    borderRadius: 5,
    elevation: 10,
  },
  text: {
  color: "#fff",   
  },
});

Затем включите CustomMarkerв свой проект.

 export default function App() {
  return (
    <View style={styles.container}>
      <MapView style={styles.map} initialRegion={tokyoRegion}>
        <Marker coordinate={tokyoRegion}>
          {/* CustomMarker has to be a child of Marker*/}
          <CustomMarker />
        </Marker>
      </MapView>
    </View>
  );
}

The marker view has been customized

Стилизация карты

Создайте объект JSON, который вы будете использовать для разработки карты, с помощью генератора стилей Google . Затем передайте сгенерированный объект стиля в prop customMapStyle компонента .<MapView />

import React, { useState, useRef } from "react";
import { StyleSheet, Text, View, Button } from "react-native";
import MapView, { Marker } from "react-native-maps";

const mapStyle = []; //map styles go here!
export default function App() {
  return (
    <View style={styles.container}>
      <MapView
        style={styles.map}
        initialRegion={tokyoRegion}
        customMapStyle={mapStyle} //plug in our custom style into the customMapStyle prop.
      />
    </View>
  );
}

Вы можете игнорировать mapStyleпеременную — поскольку мы генерируем стиль карты с помощью генератора, вам нужно только вставить сгенерированный объект JSON в свой код и отправить его компоненту .<MapView />

Стиль карты изменен на темную тему.

Как видите, стиль карты изменился со светлой темы по умолчанию на темную тему.

Получение текущего местоположения или конкретной координаты

Что делать, если вы хотите анимировать по определенной координате?

Сначала создайте ссылку на использование хука:<MapView />useRef()

import { useRef } from "react";

const mapRef = useRef(null);
<MapView
  ref={mapRef} //assign our ref to this MapView
  /*further code.. */
/>

Это означает, что теперь мы можем выполнять методы для управления нашим MapViewкомпонентом.

Затем добавьте этот блок:

//extra code removed for brevity
//destination coordinates
const tokyoRegion = {
  latitude: 35.6762,
  longitude: 139.6503,
  latitudeDelta: 0.01,
  longitudeDelta: 0.01,
};
const goToTokyo = () => {
  //Animate the user to new region. Complete this animation in 3 seconds
  mapRef.current.animateToRegion(tokyoRegion, 3 * 1000);
};
return (
  <View style={styles.container}>
    <Button onPress={() => goToTokyo()} title="Go to Tokyo" />
  </View>
);

Вот разбивка этого фрагмента:

Хук возвращает изменяемый объект ref, свойство которого имеет значение переданного аргумента. Если значение свойства равно , это означает, что компонент еще не смонтирован. Теперь вы можете получить доступ к любому из методов, используя .useRef()currentcurrentundefined<MapView />mapRef.current

Метод принимает любые два параметра: координаты пункта назначения (в данном случае ) и продолжительность анимации в миллисекундах.animateToRegion()tokyoRegion

Навигация к определенной координате

В итоге код в вашем файле должен выглядеть так:App.js

export default function App() {
  const mapRef = useRef(null);
  const [region, setRegion] = useState({
    latitude: 51.5079145,
    longitude: -0.0899163,
    latitudeDelta: 0.01,
    longitudeDelta: 0.01,
  });
  const tokyoRegion = {
    latitude: 35.6762,
    longitude: 139.6503,
    latitudeDelta: 0.01,
    longitudeDelta: 0.01,
  };
  const goToTokyo = () => {
    //complete this animation in 3 seconds
    mapRef.current.animateToRegion(tokyoRegion, 3 * 1000);
  };
  return (
    <View style={styles.container}>
      <MapView
        ref={mapRef}
        style={styles.map}
        initialRegion={{
          latitude: 24.8607,
          longitude: 67.0011,
          latitudeDelta: 0.0922,
          longitudeDelta: 0.0421,
        }}
        onRegionChangeComplete={(region) => setRegion(region)}
      />
      <Button onPress={() => goToTokyo()} title="Go to Tokyo" />
      <Text style={styles.text}>Current latitude{region.latitude}</Text>
      <Text style={styles.text}>Current longitude{region.longitude}</Text>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    ...StyleSheet.absoluteFillObject,
    flex: 1,
    justifyContent: "flex-end",
    alignItems: "center",
  },
  map: {
    ...StyleSheet.absoluteFillObject,
  },
  text: {
    fontSize: 20,
    backgroundColor: "lightblue",
  },
});

Использование <polyline />

Вы можете использовать компонент из библиотеки для создания линий между несколькими координатами. Он принимает массив координат в своей опоре. Вы также можете указать дополнительные реквизиты для стилизации, такие как , и т. д.<Polyline />react-native-mapscoordinatesstrokeWidthstrokeColor

Давайте создадим путь между Токио и Тибой.

import { Polyline } from "react-native-maps";

const tokyoRegion = {
  latitude: 35.6762,
  longitude: 139.6503,
  latitudeDelta: 0.01,
  longitudeDelta: 0.01,
};
const chibaRegion = {
  latitude: 35.6074,
  longitude: 140.1065,
  latitudeDelta: 0.01,
  longitudeDelta: 0.01,
};

return (
  <View style={styles.container}>
    <MapView style={styles.map} initialRegion={tokyoRegion}>
      <Polyline
        coordinates={[tokyoRegion, chibaRegion]} //specify our coordinates
        strokeColor={"#000"}
        strokeWidth={3}
        lineDashPattern={[1]}
      />
    </MapView>
  </View>
);

Сохраните координаты местоположений в соответствующих переменных и передайте их в виде массива в coordinatesсвойство компонента.<Polyline />

Ломаная линия рисуется между двумя координатами

Если посмотреть на результаты, то линия проведена прямо между этими координатами и не учитывает фактические географические пути и дороги. Для этого вам потребуется установить несколько координат между исходным и конечным местоположениями с помощью Google Maps Direction API, который возвращает все возможные маршруты между двумя местами.

Использование Google Maps Direction API

Чтобы использовать Direction API , вы должны сначала включить его для своего проекта . Обратите внимание, что для использования Google Cloud у вас должен быть действительный платежный адрес, иначе функция «Маршруты» не будет работать.

Сначала создайте вспомогательную функцию с именем getDirections. Здесь напишите следующий код:

import {decode} from "@mapbox/polyline"; //please install this package before running!
const getDirections = async (startLoc, destinationLoc) => {
  try {
    const KEY = "YOUR GOOGLE API KEY"; //put your API key here.
    //otherwise, you'll have an 'unauthorized' error.
    let resp = await fetch(
      `https://maps.googleapis.com/maps/api/directions/json?origin=${startLoc}&destination=${destinationLoc}&key=${KEY}`
    );
    let respJson = await resp.json();
    let points = decode(respJson.routes[0].overview_polyline.points);
    console.log(points);
    let coords = points.map((point, index) => {
      return {
        latitude: point[0],
        longitude: point[1]
      };
    });
    return coords;
  } catch (error) {
    return error;
  }
};

Функция преобразует закодированные точки полилинии из свойства в массив, содержащий широту и долготу всех координат.decode()overview_polyline.points

В результате это позволит PolyLineкомпоненту легко считывать координаты местоположения. Без него выдает ошибку.react-native-maps

import React, { useState, useEffect } from "react";
import { View, Text } from "react-native";
import MapView, { Polyline } from "react-native-maps";
import { decode } from "@mapbox/polyline";

const App = () => {
  const [coords, setCoords] = useState([]);

  useEffect(() => {
    //fetch the coordinates and then store its value into the coords Hook.
    getDirections("52.5200066,13.404954", "50.1109221,8.6821267")
      .then(coords => setCoords(coords))
      .catch(err => console.log("Something went wrong"));
  }, []);

  return (
    <>
      <MapView
        style={{ flex: 1 }}
        initialRegion={{
          latitude: 52.5200066,
          longitude: 13.404954,
          latitudeDelta: 0.1,
          longitudeDelta: 0.1
        }}
      >
        {/* finally, render the Polyline component with the coords data */}
        {coords.length > 0 && <Polyline coordinates={coords} />}
      </MapView>
    </>
  );
};

export default App;

Полилиния теперь правильно отображается, чтобы указать направления

Заключение

Теперь у вас должно быть общее представление о том, как реализовывать карты и создавать пользовательские функции поверх библиотеки. Вы можете использовать эти уроки в своей работе для создания множества типов приложений, от служб такси до поиска ресторанов, приложений для доставки и многого другого. Библиотека упрощает интеграцию карт и является важным игроком в экосистеме React Native.