React Native’de harita nasıl kullanılır?

Zafer Ayan
10 min readApr 4, 2020

--

Günümüzde yer-yön bulma, trafiği görüntüleme, mevcut konumu paylaşma, fotoğraf etiketleme gibi pek çok işlem için harita özelliği kullanılıyor. Haritaların bu denli hayatımızın içinde yer alması, geliştireceğiniz uygulamanızda da eninde sonunda harita özelliğini eklemenize neden olacaktır. Bu nedenle harita özelliğinin nasıl kullanılacağına değineceğim.

Öncelikle projeminizi oluşturalım ve boş haliyle çalıştıralım:

npx react-native init SampleRNMap --template react-native-template-typescript
cd SampleRNMap
npx react-native run-ios

Projemize harita kütüphanesini ekleyelim, pod’ları yükleyelim, git’e ekleyelim ve vscode ile açalım

yarn add react-native-maps -E
pod install --project-directory=ios/
git init
git add .
git commit -m "First commit"
code .

Google Maps API kullanmak için API Key almak gerekiyor. Buradaki yönergeleri izleyerek edinebilirsiniz. Edindikten sonra aşağıdaki gibi Maps SDK for Android ve Maps SDK for IOS menülerine giderek ENABLE seçeneğini seçmeniz gereklidir. Ayrıca IP bazlı kısıtlama gibi kısıtlamalar eklerseniz sadece kendi ağınızdan istek göndererek API Key’i güvenli hale getirmiş olursunuz.

İndirilen kütüphanenin kullanılabilmesi için native tarafta ilgili yapılandırmaların da uygulanması gerekiyor.

IOS tarafında yapılandırma

XCode üzerinde projeyi açarak IOS için native tarafı yapılandırmaya başlayalım:

open ios/SampleRNMap.xcworkspace

Sonrasında AppDelegate.m dosyasını açalım ve aşağıdaki header’ı ekleyelim:

#import <GoogleMaps/GoogleMaps.h>

Devamında didFinishLaunchingWithOptions fonksiyonunun içerisine ilk satıra Maps API Key’i ekleyelim:

[GMSServices provideAPIKey:@"YOUR_API_KEY"];

Not: [GMSServices provideAPIKey] kısmı metodun içerisindeki ilk satıra eklenmelidir. Aksi halde kararsız çalışabilir.

Sonrasında PodFile dosyası açılarak use_native_modules satırının hemen üstüne aşağıdaki kod eklenmelidir:

# React Native Maps dependencies
rn_maps_path = '../node_modules/react-native-maps'
pod 'react-native-google-maps', :path => rn_maps_path
pod 'GoogleMaps'
pod 'Google-Maps-iOS-Utils'

Pod satırları eklendikten sonra aşağıdaki komut ile pod’lar yüklenebilir:

pod install --project-directory=ios/

Devamında App.tsx’i açarak aşağıdaki gibi değiştirebilirsiniz:

import React from 'react';
import MapView, {PROVIDER_GOOGLE} from 'react-native-maps';
const App = () => {
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
);
};
export default App;

Kodu açıklamak gerekirse:

  • provider: PROVIDER_GOOGLE ve PROVIDER_DEFAULT olmak üzere iki değer alır. PROVIDER_DEFAULT değer verildiğinde Apple Maps görüntülenir.
  • flex 1: Bu değer verilerek, haritanın ekranın tamamını kaplaması sağlanır.
  • initialRegion: Haritanın hangi noktaya konumlanacağını belirtir. latitudeDelta ve longitudeDelta değerleri ise ekrana sığacak koordinat farkını belirlemektedir. Bu koordinat farkı ne kadar az olursa harita üzerinde ilgili noktaya daha fazla zoom yapılmış olur. Açıklaması stackoverflow’da yer almaktadır.

Uygulamayı çalıştırdığınızda aşağıdaki gibi Dolmabahçe Sarayı görüntülenecektir:

Şimdi Android tarafı için değişiklikleri uygulayalım.

Android tarafında yapılandırma

Projeyi Android Studio ile açalım:

open -a /Applications/Android\ Studio.app ./android

build.gradle (Project:android) dosyasını açalım ve ext içerisine aşağıdaki play services versiyonunu ekleyelim:

playServicesVersion = "17.0.0"

AndroidManifest.xml’i açalım ve <application> tag’inin içine aşağıdaki satırları ekleyelim:

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY"/>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>

Bu kadarı yeterli. Şimdi aşağıdaki kod ile çalıştıralım:

npx react-native run-android

Uygulama iOS’deki ile aynı şekilde Dolmabahçe sarayının olduğu konumu görüntüleyecektir. Şimdi mevcut konum bilgisini görüntüleyelim.

GPS ile mevcut konum bilgisinin alınması

GPS ile konum bilgisinin alınabilmesi için react-native-geolocation kütüphanesinin yüklenmesi ve native tarafta birkaç ayar yapılması gereklidir:

yarn add @react-native-community/geolocation
pod install --project-directory=ios/

iOS tarafında yapılandırma

Info.plist dosyasında aşağıdaki key’leri ekleyelim:

  • NSLocationWhenInUseUsageDescription: Kullanım esnasında konum bilgisinin alınması için gereklidir.
  • NSLocationAlwaysAndWhenInUseUsageDescription: Kullanım esnasında ve arka planda konum verisinin alınabilmesi için gereklidir.
  • NSLocationAlwaysUsageDescription: iOS 10 ve altı platformlarda kullanım için gereklidir.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Harita üzerinde konumunuzun görüntülenebilmesi için konum izni vermeniz gereklidir.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Size uygun kampanyalar sunabilmemiz için konum izni vermeniz gereklidir</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Size uygun kampanyalar sunabilmemiz için konum izni vermeniz gereklidir</string>

Ayrıca arka planda konum bilgisinin alınmasını istiyorsanız, Info.plist’e NSLocationAlwaysUsageDescription key’ini eklemeniz ve XCode’da Signing and Capabilities kısmında + Capability ‘e tıklayarak çıkan menüde Background Modes kısmını seçip Location Updates’i işaretlemeniz gereklidir.

Android tarafında yapılandırma

AndroidManifest.xml’e aşağıdaki satırı eklemeniz yeterlidir:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Ayrıca kod tarafında PermissionsAndroid API kullanılarak izin almanız gereklidir.

Şimdi GPS verisini JS tarafında nasıl alıyoruz ona değinelim:

GPS verisinin alınması

App.tsx’i aşağıdaki gibi değiştirelim:

import React from 'react';
import Geolocation from '@react-native-community/geolocation';
const App = () => {
Geolocation.getCurrentPosition(
(info) => console.log(info),
(error) => console.log(error),
{
enableHighAccuracy: true,
},
);
return <></>;
};
export default App;

Uygulamayı çalıştırdığınızda izin diyaloğu görüntülenecek ve izni verdiğinizde konumunuz aşağıdaki gibi terminal çıktısı olarak aktarılacaktır:

Not: Eğer IOS Simulator’de çalışıyorsanız “Unable to retrieve location.” hatasını alabilirsiniz. Bunu düzeltmek için MacOS’teki Maps uygulamasına giderek, herhangi bir noktaya pin atıp Simulator ile share etmeniz gerekiyor:

Şimdi GPS’ten gelen konum bilgisini haritada marker olarak ekleyelim.

Marker ekleme

Marker ekleme için Marker component’ini kullanacağız. Bir marker en basit haliyle, coordinate parametresine istenen değer atanarak aşağıdaki gibi eklenebilir.

import React from 'react';
import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
const App = () => {
const coordDolmabahce = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={coordDolmabahce}>
<Marker coordinate={coordDolmabahce} />
</MapView>
);
};
export default App;

GPS’ten veriyi çekerek mevcut konumu haritada görüntülemek için kodu aşağıdaki gibi değiştirebiliriz:

import React, {useState} from 'react';
import Geolocation from '@react-native-community/geolocation';
import MapView, {PROVIDER_GOOGLE, Marker, LatLng} from 'react-native-maps';
const App = () => {
const [coord, setCoord] = useState<LatLng>();
const initialRegion = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.02,
longitudeDelta: 0.02,
};
Geolocation.getCurrentPosition(
(c) =>
setCoord({
latitude: c.coords.latitude,
longitude: c.coords.longitude,
}),
(error) => console.log(error),
{
enableHighAccuracy: true,
},
);
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={initialRegion}>
{coord !== undefined && <Marker coordinate={coord} />}
</MapView>
);
};
export default App;

Anlık koordinat takibi (watchPosition)

Uygulamanızda anlık olarak koordinat bilgisini görüntülemek isteyebilirsiniz. Bunun için Geolocation API’de yer alan watchPosition fonksiyonunu kullanabilirsiniz. Gelen pozisyon bilgisini state’e yazarak harita üzerinde anlık olarak görüntüleyebilirsiniz:

import React, {useState} from 'react';
import Geolocation from '@react-native-community/geolocation';
import MapView, {PROVIDER_GOOGLE, Marker, LatLng} from 'react-native-maps';
const App = () => {
const [coord, setCoord] = useState<LatLng>();
const initialRegion = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.02,
longitudeDelta: 0.02,
};
Geolocation.watchPosition(
(position) => {
console.log(position);
setCoord(position.coords);
},
(error) => {
console.log(error);
},
);
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={initialRegion}>
{coord !== undefined && <Marker coordinate={coord} />}
</MapView>
);
};
export default App;

Simulator üzerinde mevcut konumu değiştirmek için, Apple Maps’ten ilgili konumu share etmeniz yeterli olacaktır.

Not: Daha az enerji tüketimi için watchPosition fonksiyonunda 3.parametre olan options kısmında useSignificantChanges parametresini true geçebilirsiniz.

Marker mesajı (Callout/InfoWindow) ekleme

Marker’a tıklandıktan sonra bir mesaj kutucuğu çıkarmak için marker içerisine Callout bileşenini ekleyebiliriz:

/* eslint-disable react-native/no-inline-styles */
import React from 'react';
import MapView, {PROVIDER_GOOGLE, Marker, Callout} from 'react-native-maps';
import {View, Text} from 'react-native';
const App = () => {
const coordDolmabahce = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={coordDolmabahce}>
<Marker coordinate={coordDolmabahce}>
<Callout>
<View>
<Text>Dolmabahçe sarayı</Text>
</View>
</Callout>
</Marker>
<Marker coordinate={coordDolmabahce} />
</MapView>
);
};
export default App;

Kodu çalıştırıp marker’ın üzerine tıkladığınızda aşağıdaki gibi bir mesaj metni görüntülenecektir:

Animasyonlu Marker ekleme

Marker’ı animasyonlu olacak şekilde değiştirmek için React Native Animated API’den yararlanabiliriz:

/* eslint-disable react-native/no-inline-styles */
import React, {useState, useEffect} from 'react';
import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
import {Animated, Easing, View} from 'react-native';
const App = () => {
const coordDolmabahce = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
const [growValue] = useState(new Animated.Value(0));
const grow = growValue.interpolate({
inputRange: [0, 1],
outputRange: ['0', '10'],
});
useEffect(() => {
Animated.loop(
Animated.timing(growValue, {
toValue: 1,
duration: 1500,
easing: Easing.linear,
useNativeDriver: true,
}),
).start();
}, [growValue]);
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={coordDolmabahce}>
<Marker coordinate={coordDolmabahce} anchor={{x: 0.5, y: 0.5}}>
<View
style={{
width: 100,
height: 100,
justifyContent: 'center',
alignItems: 'center',
}}>
<Animated.View
style={{
width: 10,
height: 10,
borderRadius: 10,
transform: [{scale: grow}],
backgroundColor: '#1976d299',
}}
/>
</View>
</Marker>

<Marker coordinate={coordDolmabahce} />
</MapView>
);
};
export default App;

Üst üste iki marker ekleyerek, biri animasyonlu diğeri ise normal marker pin’i olacak şekilde görüntülenmesini sağladık.

Dilerseniz daha kompleks animasyonlar için lottie’yi kullanabilirsiniz. Onunla ilgili de bir örnek yapalım. Kütüphaneyi projemize ekleyelim:

yarn add lottie-react-native lottie-ios@3.1.3
pod install --project-directory=ios/

Daha sonra https://lottiefiles.com/15439-blue-ripple JSON olarak indirerek projemize dahil edelim ve aşağıdaki gibi kullanalım:

/* eslint-disable react-native/no-inline-styles */
import React from 'react';
import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
import LottieView from 'lottie-react-native';
import {View} from 'react-native';
const App = () => {
const coordDolmabahce = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={coordDolmabahce}>
<Marker coordinate={coordDolmabahce} anchor={{x: 0.5, y: 0.5}}>
<View
style={{
width: 100,
height: 100,
justifyContent: 'center',
alignItems: 'center',
}}>
<LottieView
source={require('./15439-blue-ripple.json')}
autoPlay
loop
/>

</View>
</Marker>
<Marker coordinate={coordDolmabahce} />
</MapView>
);
};
export default App;

Polygon ekleme

İstediğiniz ilçenin polygon koordinatlarını bulabilmek için https://nominatim.openstreetmap.org/ ‘a giderek ilçenin adını arattırmanız gerekiyor. Daha sonra aşağıdaki gibi çıkan kısımda details butonuna basarak bölgenin detaylarını açmanız lazım.

Devamında çıkan ekranda OSM ID’sini kopyalamanız gerekiyor.

Kopyaladığınız OSM ID’yi http://polygons.openstreetmap.fr/ adresine giderek Id of relation kısmına yazın ve Submit ettikten sonra çıkan sonuçlar kısmında GeoJSON’a tıklayın. Ekrandaki JSON’ı kopyalayın ve projede besiktas.json adında bir dosya oluşturarak JSON içeriğini buraya yapıştırın. Devamında App.tsx’i aşağıdaki gibi değiştirin:

/* eslint-disable react-native/no-inline-styles */
import React from 'react';
import MapView, {PROVIDER_GOOGLE, Polygon} from 'react-native-maps';
import besiktas from './besiktas.json';
const App = () => {
const coordDolmabahce = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.15,
longitudeDelta: 0.15,
};
const polygon = besiktas.geometries[0].coordinates[0][0].map((c) => {
return {longitude: c[0], latitude: c[1]};
});

return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
initialRegion={coordDolmabahce}>
<Polygon coordinates={polygon} />
</MapView>
);
};
export default App;

Uygulamayı çalıştırdığınızda beşiktaş ilçesinin sınırları aşağıdaki gibi görüntülenecektir:

Cluster ekleme

Çok fazla marker’ınız varsa, görüntüleme esnasında uygulamanın performansı düşer. Bu nedenle marker’ların cluster’lanması (kümelenmesi) gereklidir. Bunun için öncelikle aşağıdaki kütüphaneyi eklememiz gerekiyor:

yarn add react-native-map-clustering

Şimdi beşiktaş sınırlarına marker koyarak ve cluster haline getirmek amacıyla App.tsx’i aşağıdaki gibi değiştirelim:

/* eslint-disable react-native/no-inline-styles */
import React from 'react';
import {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
import MapView from 'react-native-map-clustering';
import besiktas from './besiktas.json';
const App = () => {
const coordDolmabahce = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.15,
longitudeDelta: 0.15,
};
const polygon = besiktas.geometries[0].coordinates[0][0].map((c) => {
return {longitude: c[0], latitude: c[1]};
});
return (
<MapView
provider={PROVIDER_GOOGLE}
initialRegion={coordDolmabahce}
style={{flex: 1}}>
{polygon.map((c) => (
<Marker coordinate={c} />
))}

</MapView>
);
};
export default App;

Kodu çalıştırdığınızda, sınırlara eklenen koordinatlar aşağıdaki gibi görüntülenecektir:

Harita kontrolleri ekleme (zoomIn/zoomOut)

Harita kontrolleri eklemek için MapView’ı useRef hook’u ile bir map değişkenine atamamız gerekiyor. Ardından ekranda görüntülenecek alanın (region değişkeninin) state’te tutulması ve harita üzerinde parmakla sürükleme işlemlerinde onRegionChange() fonksiyonu çağrılarak region değişkeninin güncellenmesi gereklidir. Ayrıca zoom miktarının ne kadar olacağını zoomDelta değeri ile belirlememiz gerekiyor. Devamında ise zoomIn/zoomOut durumlarına göre latitudeDelta ve longitudeDelta değerlerine ekleme ve çıkarma yaparak ekrana sığan harita region’ını değiştirmemiz yeterli olacaktır. Bu değişikliklerden sonra App.tsx aşağıdaki hale gelir:

import React, {useRef, useState} from 'react';
import MapView, {PROVIDER_GOOGLE, Region} from 'react-native-maps';
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
const App = () => {
const map = useRef<MapView>(null);
const [region, setRegion] = useState({
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
const onRegionChange = (changedRegion: Region) => {
setRegion(changedRegion);
};
const zoomDelta = 0.005; const onZoom = (zoomSign: number) => {
const zoomedRegion = {
latitude: region.latitude,
longitude: region.longitude,
latitudeDelta: region.latitudeDelta - zoomDelta * zoomSign,
longitudeDelta: region.longitudeDelta - zoomDelta * zoomSign,
};
setRegion(zoomedRegion);
map.current!.animateToRegion(zoomedRegion);
};
const onZoomIn = () => onZoom(1);
const onZoomOut = () => onZoom(-1);
return (
<>
<MapView
ref={map}
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={region}
onRegionChange={onRegionChange}
/>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={onZoomIn}>
<Text style={styles.text}>+</Text>
</TouchableOpacity>
<View style={styles.spacer} />
<TouchableOpacity style={styles.button} onPress={onZoomOut}>
<Text style={styles.text}>-</Text>
</TouchableOpacity>
</View>
</>
);
};
const styles = StyleSheet.create({
map: {
flex: 1,
},
buttonContainer: {
position: 'absolute',
bottom: 30,
end: 20,
borderRadius: 5,
backgroundColor: '#fff',
padding: 12,
},
button: {},
text: {
textAlign: 'center',
},
spacer: {
marginVertical: 7,
},
});
export default App;

+ ve - butonlarına basıldığında zoomIn/zoomOut durumu aşağıdaki gibi gözlemlenebilir:

Harita stilini özelleştirme

Uygulamanızı karanlık temaya uygun hale getirmek veya harita üzerinde daha az detay görüntülemek amacıyla haritanızı özelleştirmeniz gerekebilir. Bunun için öncelikle özelleştirmek istediğiniz haritayı https://mapstyle.withgoogle.com/ adresine giderek oluşturmanız gereklidir.

Harita stilini oluşturup finish’e tıkladıktan sonra üstteki JSON metnini COPY JSON’a basarak kopyalayalım. Ardından proje içerisinde customMapStyle.json dosyasını oluşturalım ve içine yapıştıralım. App.tsx’i JSON dosyasını kullanacak şekilde aşağıdaki gibi değiştirelim:

import React from 'react';
import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
import {StyleSheet, StatusBar} from 'react-native';
import React from 'react';
import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
import {StyleSheet, StatusBar} from 'react-native';
import customMapStyle from './customMapStyle.json';
const App = () => {
const region = {
latitude: 41.0391683,
longitude: 28.9982707,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
return (
<>
<StatusBar barStyle="light-content" />
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={region}
customMapStyle={customMapStyle}>
<Marker coordinate={region} pinColor={'#1976d2'} />
</MapView>
</>
);
};
const styles = StyleSheet.create({
map: {
flex: 1,
},
});
export default App;

Oluşturduğunuz harita stilini snazzymaps.com üzerinden paylaşabilir, hazır harita stillerini edinebilirsiniz.

Sonuç olarak

Harita özelliği birçok uygulamayı renklendiren ve farklı bir hava katan bir eklenti haline geldiği için, siz de buradaki yazıdan faydalanarak kendi haritanızı oluşturabilir ve uygulamanıza yeni bir işlev katabilirsiniz.

Projenin bitmiş halini react-native-map-sample reposunda bulabilirsiniz. Bu yazı hakkında soru ve görüşlerinizi aşağıdaki yorumlar kısmından yazabilirsiniz. Bana destek vermek için alkış simgesine tıklayabilirsiniz. Sonraki yazımda görüşmek üzere…

--

--

Responses (2)