react-native-tpl-maps-view
v2.2.5
Published
TPL Maps React Native Package
Readme
React Native TPL Maps
react-native-tpl-maps-view lets you use the TPL Maps SDK as a native map view in React Native apps on Android and iOS.
The package connects to TPL Maps servers, draws the map, and handles gestures and camera movement. From JavaScript, you can add markers, draw routes, search for places, and more.
Table of Contents
- Requirements
- Installation
- Keep Your API Key Safe
- Android Setup
- iOS Setup
- Events
- Usage
- Common Recipes
- API Reference
- Known Platform Differences
- Maintainers
- Contributing
- License
Requirements
| Item | Supported version |
|---|---|
| Android | minSdkVersion 21 or higher |
| iOS | 12.0 or higher (15.1+ recommended, this is the version we tested) |
| React | 18.2.0 |
| React Native | 0.71.7 or higher |
| New Architecture (Fabric) | Supported, tested with newArchEnabled=true |
Installation
npm install react-native-tpl-maps-viewIf you want to follow the navigation example in this guide, also install:
npm install @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context[!NOTE] If Metro cannot find
prop-types, install it yourself withnpm install prop-types. The package uses it internally but does not list it as a dependency yet.
Keep Your API Key Safe
Never write your real API key directly in your source code or commit it to Git. The examples in this guide read the key from a separate config file:
// config.ts (add this file to .gitignore)
export const TPL_MAPS_API_KEY = 'YOUR_TPL_MAPS_API_KEY';You can also use a tool like react-native-config to load the key from a .env file.
You can get your API key from api.tplmaps.com.
Android Setup
1. Add the TPL Maps Maven repository
Open android/build.gradle (the root one) and add the repositories inside the existing buildscript { } block:
buildscript {
ext {
// ...
}
repositories {
google()
mavenCentral()
}
dependencies {
// ...
}
allprojects {
repositories {
jcenter()
google()
maven { url 'https://jitpack.io' }
maven { url 'https://api1.tplmaps.com/artifactory/example-repo-local/' }
}
}
}[!TIP] This setup is tested with React Native 0.77 and the New Architecture, using:
- Android Gradle Plugin
8.5.1- NDK
27.2.12479018- Kotlin
1.9.24- Gradle
8.10.2If you are updating your tools for the New Architecture, use these versions.
2. Fix the native library conflict
The TPL Maps SDK comes with its own copy of libc++_shared.so. React Native has one too, and the two can clash during the build.
To fix this, open android/app/build.gradle and add the following inside the android { } block:
android {
// ...
packaging {
jniLibs {
useLegacyPackaging = false
keepDebugSymbols += "**/libc++_shared.so"
}
pickFirsts += [
"lib/arm64-v8a/libc++_shared.so",
"lib/armeabi-v7a/libc++_shared.so",
"lib/x86_64/libc++_shared.so",
"lib/x86/libc++_shared.so"
]
}
}3. Add your API key
Open android/app/src/main/AndroidManifest.xml and add these tags inside <application>:
<meta-data
android:name="@string/metadata_name_api_key"
android:value="YOUR_TPL_MAPS_API_KEY" />
<meta-data
android:name="@string/metadata_name_origin"
android:value="https://yourapp.com/" />[!NOTE] You do not need to create the
@string/metadata_name_api_keyand@string/metadata_name_originresources yourself. They come from the TPL Maps native SDK (com.tpl.maps.sdk:maps) once Gradle downloads it from the repository in step 1. Just replace theandroid:valuewith your own key and origin.
You also do not need to add any permissions. This package already declares INTERNET, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_NETWORK_STATE, and the OpenGL ES 2.0 feature. Android merges them into your app automatically.
4. Register the native package
In android/app/src/main/java/.../MainApplication.kt:
import com.awesomeproject3.MyPackage
// ...
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(MyPackage())
}5. Turn on the New Architecture
In android/gradle.properties:
newArchEnabled=true6. Ask for location permission at runtime
The package only declares the location permissions. On Android 6 and above, you must still ask the user for permission while the app is running. Until the user says yes, the "my location" button, setZoomOnCurrentLocation(), and EnableLocationButton() will not work.
The native SDK does not ask for this permission for you. Ask for it yourself, usually right before you need it:
import { PermissionsAndroid, Platform } from 'react-native';
async function requestLocationPermission() {
if (Platform.OS !== 'android') return true;
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}[!NOTE] On iOS you don't need to do this. The native SDK asks for permission by itself as soon as the map appears, using the text you set in the iOS setup below.
iOS Setup
1. Set the deployment target
In ios/Podfile:
platform :ios, '15.1'2. Install pods
Autolinking adds react-native-tpl-maps-view, which depends on the native TPLMaps pod (version 4.3.4). If CocoaPods cannot find it, add it yourself above use_native_modules!:
target 'YourApp' do
pod 'TPLMaps'
config = use_native_modules!
# ...
endThen run:
cd ios && pod install3. Add your TPL Maps credentials file
- Download
tplservices.plistfrom api.tplmaps.com. - Drag it into your app target in Xcode.
- Tick Copy items if needed.
- Make sure your app target is ticked under Target Membership, so the file is included in the app.
[!WARNING]
tplservices.plistcontains your credentials. Do not commit it to a public repository.
4. Add a location usage message
In ios/<YourApp>/Info.plist, give NSLocationWhenInUseUsageDescription a real message. It's easy to leave this empty while testing, but Apple will reject your app if it is empty.
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses your location to show nearby places on the map.</string>Events
On both platforms, every event is sent through one JavaScript event name: onReady.
- On Android it comes from
CustomEventEmitterModule. - On iOS it comes from
CustomEventEmitter.
Listen once, then check event.type to see what happened:
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
const emitter =
Platform.OS === 'android'
? new NativeEventEmitter(NativeModules.CustomEventEmitterModule)
: new NativeEventEmitter(NativeModules.CustomEventEmitter);
const listener = emitter.addListener('onReady', (event) => {
console.log(event.type, event);
});
// later: listener.remove();| event.type | When it fires | Data you get | Platforms |
|---|---|---|---|
| onMapReady | The map has loaded and is ready to use. It fires once right away with empty data, then again a moment later with real coordinates. | { type, data: "lat;lng" } | Android, iOS |
| onMapChange | The camera stops moving (after a pan, a zoom, or a SetCameraAnimation / setZoomOnCurrentLocation call). | { type, data: "lat;lng" } | Android, iOS |
| onMarkerClick | The user taps any marker. | { type, lat, lng, title, desc } (fields are at the top level, there is no data field) | Android, iOS |
| onLocationClick | The user taps the native "my location" button. | { type } (Android sends nothing extra, iOS also sends a data label) | Android, iOS |
| onMapClick | The user taps the map or a point of interest. | { type, data: "lat;lng" } | Android, iOS |
| onPolyLineClick | The user taps a drawn route. | { type, data: "id;order;properties" } | Android only |
[!IMPORTANT] Watch the coordinate order.
Event
dataalways comes as"lat;lng". Every setter function in the API Reference below (AddMarker,AddCustomMarkerWithBase64,SetCameraAnimation,SetPolyLine, ...) expects the opposite order,"lng;lat".When you take a coordinate from an event and pass it to one of these functions, swap it. This is the most common mistake with this package. A marker with swapped coordinates usually lands somewhere in the ocean, so it's not always easy to spot.
Usage
The example below is a complete, working app that puts all the setup above together. It covers the things most map apps need:
- Show the user's location with a marker. The position is read from
onMapReady/onMapChangeand placed withSetMarkerAtPositionWithBase64, so the same pin moves instead of adding a new one every time. - Show several branches with custom markers. A
branchesarray is drawn withAddCustomMarkerWithBase64. - React to a marker tap.
onMarkerClickis matched to a branch, a route is drawn to it, and its details are shown. - Go to another screen and come back. Tapping the bottom bar opens
SecondScreenwith the selected branch. Going back returns to the map. - Let the user pick a spot by moving the map. A second screen (
AddressPicker) finds the address under the center pin once the map stops moving.
The example is split into these files:
index.js
App.tsx
MainScreen.tsx
BranchLocator.tsx
AddressPicker.tsx
SecondScreen.tsx
config.tsindex.js
import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';
AppRegistry.registerComponent(appName, () => App);App.tsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import MainScreen from './MainScreen';
import BranchLocator from './BranchLocator';
import AddressPicker from './AddressPicker';
import SecondScreen from './SecondScreen';
const Stack = createNativeStackNavigator();
const App: React.FC = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Main" component={MainScreen} />
<Stack.Screen name="BranchLocator" component={BranchLocator} />
<Stack.Screen name="AddressPicker" component={AddressPicker} />
<Stack.Screen name="SecondScreen" component={SecondScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;MainScreen.tsx
Two buttons that open the two demo screens.
import React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import { NavigationProp, ParamListBase, useNavigation } from '@react-navigation/native';
const MainScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<ParamListBase>>();
return (
<View style={styles.container}>
<Button title="Open Map" onPress={() => navigation.navigate('BranchLocator')} />
<View style={styles.spacer} />
<Button title="Pick My Location" onPress={() => navigation.navigate('AddressPicker')} />
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
spacer: { height: 12 },
});
export default MainScreen;BranchLocator.tsx
This screen:
- Shows the map.
- Asks for location permission.
- Places a marker that follows the user's position.
- Shows branches as custom markers.
- Draws a route to the branch the user taps.
It also includes a sizing fix for Fabric. Without it, the native map sometimes doesn't get its real size on the first layout, and stays blank.
import React, { useEffect, useState } from 'react';
import {
View,
Text,
StyleSheet,
Platform,
PermissionsAndroid,
TouchableOpacity,
NativeModules,
NativeEventEmitter,
PixelRatio,
useWindowDimensions,
} from 'react-native';
import { NavigationProp, ParamListBase, useNavigation } from '@react-navigation/native';
import MyViewManager, {
AddCustomMarkerWithBase64,
SetMarkerAtPositionWithBase64,
RemoveAllMarkers,
RemoveAllPolyLines,
SetPolyLine,
EnableLocationButton,
setZoomOnCurrentLocation,
} from 'react-native-tpl-maps-view';
import { TPL_MAPS_API_KEY } from './config';
type Branch = { id: string; title: string; desc: string; latitude: number; longitude: number };
const branches: Branch[] = [
{ id: '1', title: 'Branch 1', desc: 'Main Boulevard', latitude: 24.899987, longitude: 67.163350 },
{ id: '2', title: 'Branch 2', desc: 'Airport Road', latitude: 24.860360, longitude: 67.001331 },
{ id: '3', title: 'Branch 3', desc: 'Highway Exit 4', latitude: 29.344599, longitude: 71.695158 },
];
// Small pin icons as base64 images.
// Replace these with your own (for example, a different icon for the user and for branches).
const branchMarkerBase64 =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFxSURBVEiJnZUvbkJBEIe/JSQgOEErwZIaisPVVCAwHKKOhGNQzwG4QQUnAAUhRZEKkLU4DDAVDMljuv9gk0nevvnN79uX3Z3nRITYcM61gHegpQGw0JiKyCJqICLeAKrAJ3ACJBAn1VSDPgHzBrCJGNvYAI0sAFAC5neYX2MOlHIAQ0/xEZgAHxoTfWd1wygAqAEHU/QLtD0LaWuuqD0AtRig41lVN3IQuh59p6gpmUP1auZbEfkiMDS3jXlYwIuZf4fMI5obDwvYm/lzBsBqbjws4Meuxjn3FHLWnP3qWw+zaU3+b9oUKHs2uKw5q2+m7sHaU7QE3rgc45o+Lz26dc5FG3gKi70n1psGOYAKsIuYhGIHVHKbXf8BQD+7mypkdof57K52Xeg15wzzM55elQQoZJwBGEc9EoAqsIqYr4j8zZIAhdS5XH9rvgfqyfqUQCE9D6CXVZsjUsioYD7KrXNanBzOuTJw/Td0ReSYU/cHfjZLwpHI7BkAAAAASUVORK5CYII=';
const userMarkerBase64 = branchMarkerBase64;
const BranchLocator: React.FC = () => {
const navigation = useNavigation<NavigationProp<ParamListBase>>();
const { width, height } = useWindowDimensions();
// Start with no size, then set a real size a moment after the screen appears.
// This makes Fabric lay out the native view again with real dimensions,
// which is what makes the map start drawing.
const [mapStyle, setMapStyle] = useState({});
const [currentLocation, setCurrentLocation] = useState<{ lat: string; lng: string } | null>(null);
const [selectedBranch, setSelectedBranch] = useState<Branch | null>(null);
useEffect(() => {
const timer = setTimeout(() => setMapStyle({ flex: 1, width: '100%', height: '100%' }), 300);
return () => clearTimeout(timer);
}, []);
const requestLocationPermission = async () => {
if (Platform.OS !== 'android') return true;
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
return granted === PermissionsAndroid.RESULTS.GRANTED;
};
const showBranchMarkers = () => {
branches.forEach((branch) => {
AddCustomMarkerWithBase64(
`${branch.longitude};${branch.latitude};0;"${branch.title}";"${branch.desc}"`,
branchMarkerBase64
);
});
};
// Event data comes as "lat;lng", so we swap it to "lng;lat" for the setter functions.
const trackUserMarker = (lat: string, lng: string) => {
setCurrentLocation({ lat, lng });
SetMarkerAtPositionWithBase64(`${lng};${lat};0;"You are here";""`, userMarkerBase64);
};
const routeToBranch = (branch: Branch, from: { lat: string; lng: string }) => {
RemoveAllPolyLines();
SetPolyLine(`${from.lat};${from.lng}`, [`${branch.latitude};${branch.longitude}`], '#1D8548', '2', TPL_MAPS_API_KEY);
};
// The map is created again every time this screen opens (including when you
// come back to it), so onMapReady fires again each time. That's why markers
// and routes are drawn here, and not only once when the screen first loads.
const handleEvent = (event: any, hasLocationPermission: boolean) => {
if (event.type === 'onMapReady') {
RemoveAllMarkers();
RemoveAllPolyLines();
showBranchMarkers();
if (event.data) {
const [lat, lng] = event.data.split(';');
trackUserMarker(lat, lng);
}
if (hasLocationPermission) {
EnableLocationButton(true);
setZoomOnCurrentLocation();
}
} else if (event.type === 'onMapChange') {
if (event.data) {
const [lat, lng] = event.data.split(';');
trackUserMarker(lat, lng);
}
} else if (event.type === 'onMarkerClick') {
const branch = branches.find((b) => b.title === event.title);
if (!branch) return; // The user tapped their own "You are here" marker.
setSelectedBranch(branch);
if (currentLocation) routeToBranch(branch, currentLocation);
}
};
useEffect(() => {
let listener: { remove: () => void } | undefined;
requestLocationPermission().then((hasLocationPermission) => {
const emitter =
Platform.OS === 'android'
? new NativeEventEmitter(NativeModules.CustomEventEmitterModule)
: new NativeEventEmitter(NativeModules.CustomEventEmitter);
listener = emitter.addListener('onReady', (event) => handleEvent(event, hasLocationPermission));
});
return () => listener?.remove();
}, []);
return (
<View style={{ flex: 1 }}>
{Platform.OS === 'android' && (
<MyViewManager
style={mapStyle}
customWidth={PixelRatio.getPixelSizeForLayoutSize(width)}
customHeight={PixelRatio.getPixelSizeForLayoutSize(height)}
/>
)}
{Platform.OS === 'ios' && <MyViewManager style={{ flex: 1 }} />}
<TouchableOpacity
style={styles.footer}
onPress={() => navigation.navigate('SecondScreen', { branch: selectedBranch })}
>
<Text style={styles.footerTitle}>{selectedBranch ? selectedBranch.title : 'Tap a branch marker'}</Text>
<Text style={styles.footerDescription}>
{selectedBranch ? selectedBranch.desc : 'Then tap here to see full details'}
</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
footer: {
position: 'absolute',
bottom: 10,
left: 10,
right: 10,
backgroundColor: 'white',
borderRadius: 10,
padding: 10,
},
footerTitle: { fontWeight: 'bold', color: 'black' },
footerDescription: { color: 'black' },
});
export default BranchLocator;How this example works
- User location marker: This package has no
getCurrentPosition()function. The only way to get the user's position in JavaScript is from thedatafield ofonMapReadyoronMapChange, because the native map is the one tracking GPS. We useSetMarkerAtPositionWithBase64(notAddCustomMarkerWithBase64) so the same pin moves around instead of adding a new pin on every update. - Why we ask for permission first:
EnableLocationButtonandsetZoomOnCurrentLocationonly work after permission is given. On iOS the SDK asks by itself, so thehasLocationPermissioncheck only matters inside theonMapReadypart above. - Marker tap to route: Tapping a branch marker fires
onMarkerClick. Itstitleis matched to thebranchesarray (it's the same text used to create the marker), thenSetPolyLinedraws a route from the user's last known location to that branch. - Tapping a second branch on iOS:
routeToBranchcallsRemoveAllPolyLines()before drawing the new route. On Android this clears the old route. On iOS it does nothing (see Known Platform Differences), so older routes stay on the map next to the new one. - Leaving the screen and coming back: On both Android and iOS, the native code resets its "map ready" flag when the map is removed (which happens when you leave this screen). So
onMapReadyfires again when you come back, and the markers are drawn again.
AddressPicker.tsx
A "move the map to pick a location" screen. The selected spot is always whatever is under the pin in the center of the screen. When the user stops moving the map, the app looks up the address for that spot and shows it in a text field.
import React, { useEffect, useRef, useState } from 'react';
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Platform,
NativeModules,
NativeEventEmitter,
PixelRatio,
useWindowDimensions,
} from 'react-native';
import { NavigationProp, ParamListBase, useNavigation } from '@react-navigation/native';
import MyViewManager, { getSearchPointData } from 'react-native-tpl-maps-view';
import { TPL_MAPS_API_KEY } from './config';
const AddressPicker: React.FC = () => {
const navigation = useNavigation<NavigationProp<ParamListBase>>();
const { width, height } = useWindowDimensions();
const [mapStyle, setMapStyle] = useState({});
const [address, setAddress] = useState('Drag the map to choose a location');
const [isLoading, setIsLoading] = useState(false);
// onMapChange can fire many times while the user is still dragging,
// not only once when the map stops. Without this timer, every small
// movement would send a new network request to look up the address.
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
const timer = setTimeout(() => setMapStyle({ flex: 1, width: '100%', height: '100%' }), 300);
return () => clearTimeout(timer);
}, []);
const reverseGeocode = async (lat: string, lng: string) => {
setIsLoading(true);
try {
const result = await getSearchPointData('', lat, lng, TPL_MAPS_API_KEY);
const place = result?.data?.[0];
setAddress(place ? place.compound_address_parents ?? place.name : 'No address found here');
} catch (error) {
setAddress('Could not look up this address');
} finally {
setIsLoading(false);
}
};
// Event data comes as "lat;lng". getSearchPointData takes lat and lng as
// separate values, so no swapping is needed here (unlike the marker
// functions, which expect one combined "lng;lat" string).
const handleCenterChanged = (data: string) => {
const [lat, lng] = data.split(';');
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => reverseGeocode(lat, lng), 600);
};
useEffect(() => {
const emitter =
Platform.OS === 'android'
? new NativeEventEmitter(NativeModules.CustomEventEmitterModule)
: new NativeEventEmitter(NativeModules.CustomEventEmitter);
const listener = emitter.addListener('onReady', (event) => {
if ((event.type === 'onMapReady' || event.type === 'onMapChange') && event.data) {
handleCenterChanged(event.data);
}
});
return () => {
listener.remove();
if (debounceRef.current) clearTimeout(debounceRef.current);
};
}, []);
return (
<View style={{ flex: 1 }}>
{Platform.OS === 'android' && (
<MyViewManager
style={mapStyle}
customWidth={PixelRatio.getPixelSizeForLayoutSize(width)}
customHeight={PixelRatio.getPixelSizeForLayoutSize(height)}
/>
)}
{Platform.OS === 'ios' && <MyViewManager style={{ flex: 1 }} />}
{/* Fixed center pin. The map moves under it, the pin never moves. */}
<View style={styles.centerPin} pointerEvents="none">
<Text style={styles.centerPinText}>📍</Text>
</View>
<View style={styles.panel}>
<TextInput style={styles.addressInput} editable={false} value={isLoading ? 'Looking up address…' : address} />
<Button title="Use This Location" onPress={() => navigation.goBack()} />
</View>
</View>
);
};
const styles = StyleSheet.create({
centerPin: {
position: 'absolute',
top: '50%',
left: '50%',
marginLeft: -12,
marginTop: -24,
},
centerPinText: { fontSize: 24 },
panel: {
position: 'absolute',
bottom: 10,
left: 10,
right: 10,
backgroundColor: 'white',
borderRadius: 10,
padding: 10,
},
addressInput: { color: 'black', marginBottom: 8 },
});
export default AddressPicker;How this example works
- Why the pin doesn't move: The pin is a normal
Viewplaced in the center of the screen. It has nothing to do with the map functions. The map moves underneath it, and the selected spot is always the point under the pin. This is the same point thatonMapChangegives you indata. - Why we use a timer: This package has no "drag ended" event (see Events).
onMapChangeis the only camera event, and it can fire more than once per drag. The short timer in JavaScript waits until the map stops before looking up the address.
SecondScreen.tsx
This screen reads the branch sent from BranchLocator with navigation.navigate('SecondScreen', { branch }). It shows a full round trip: tap a marker, open a details screen with that marker's data, then go back. Going back opens the map again and redraws its markers, as explained above.
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { useNavigation, useRoute, NavigationProp, ParamListBase, RouteProp } from '@react-navigation/native';
type SecondScreenParams = { branch?: { title: string; desc: string } | null };
const SecondScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<ParamListBase>>();
const route = useRoute<RouteProp<Record<string, SecondScreenParams>, string>>();
const branch = route.params?.branch;
return (
<View style={styles.container}>
<Text style={styles.title}>{branch ? branch.title : 'Second Screen'}</Text>
<Text style={styles.description}>
{branch ? branch.desc : 'You navigated here from the map screen without selecting a branch.'}
</Text>
<TouchableOpacity style={styles.backButton} onPress={() => navigation.goBack()}>
<Text style={styles.backButtonText}>← Go Back</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 12 },
description: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 30 },
backButton: { backgroundColor: '#D40000', paddingHorizontal: 24, paddingVertical: 12, borderRadius: 8 },
backButtonText: { color: 'white', fontSize: 16, fontWeight: '600' },
});
export default SecondScreen;Common Recipes
Two things almost every map app needs, taken out of the full example above so they are easy to find and copy.
Draw a route from one point to another
SetPolyLine(currentLatLng, destinationLatLng, color, width, apiKey)This sends your starting point and one or more destinations to the TPL Maps routing service. It gets back the best route and draws it on the map.
- Both
currentLatLngand every item indestinationLatLnguse"lat;lng"order. This is the opposite of most other setter functions in this package (see the coordinate order note). destinationLatLngcan have more than one stop. The route will be planned through all of them.
import { SetPolyLine, RemoveAllPolyLines } from 'react-native-tpl-maps-view';
import { TPL_MAPS_API_KEY } from './config';
// A route from one point to a single destination.
RemoveAllPolyLines(); // Clear any old route first (see the iOS note below).
SetPolyLine('24.820186;67.103118', ['24.801976;67.031951'], '#1D8548', '2', TPL_MAPS_API_KEY);
// Or a route through several stops in one call.
SetPolyLine(
'24.820186;67.103118',
['24.801976;67.031951', '24.883838;67.177270', '24.846263;67.055640'],
'#1D8548',
'2',
TPL_MAPS_API_KEY
);[!WARNING]
RemoveAllPolyLines()does nothing on iOS (see Known Platform Differences). On iOS, each newSetPolyLinecall draws a new route on top of the old one instead of replacing it.
Move the camera to any point
SetCameraAnimation(lngLat, zoom)This smoothly moves the camera to a point at the zoom level you choose.
- Unlike
SetPolyLineabove, it uses"lng;lat"order (the same as the marker functions). Double check the order whenever you switch between the two. zoomis a string, from about"10"(whole city) to"18"(street level).- On iOS,
zoomis ignored and the camera always zooms to level15.
import { SetCameraAnimation } from 'react-native-tpl-maps-view';
// Center the camera on a point, zoomed in to street level.
SetCameraAnimation('67.125973;24.820649', '15');
// A wider, city level view of the same point.
SetCameraAnimation('67.125973;24.820649', '11');To center on the device's own location instead of a fixed point, use setZoomOnCurrentLocation() (see the user location marker in BranchLocator.tsx above). It takes no arguments because the native SDK already knows where the device is.
API Reference
All coordinates use "lng;lat" order unless a note says otherwise.
Markers
AddMarker(lngLat: string)
Adds a simple marker.
AddMarker('67.125973;24.820649');AddCustomMarker(lngLatWithImageName: string)
Adds a marker with an image from your app's resources.
- Android format:
"lng;lat;imageName;width;height;bearing;title;desc" - iOS: works the same as
AddMarkerfor now (the image name is ignored). UseAddCustomMarkerWithBase64on iOS if you need a custom icon.
AddCustomMarkerWithBase64(lngLatMeta: string, base64Image: string)
Adds a marker with a base64 image.
lngLatMetaformat:"lng;lat;bearing;title;description"
AddCustomMarkerWithBase64('67.1234;24.8765;0;"Title";"Description"', base64String);SetMarkerAtPositionWithBase64(lngLatMeta: string, base64Image: string)
Same as AddCustomMarkerWithBase64, but uses one single marker. Each new call moves that marker instead of adding a new one.
RemoveAllMarkers()
Removes all markers.
[!NOTE] Marked as not working in the current iOS native code.
Camera
SetCameraAnimation(lngLat: string, zoom: string)
Smoothly moves the camera to a point.
SetCameraAnimation('67.125973;24.820649', '15');[!NOTE]
zoomis ignored on iOS (fixed at15.0in the native code).
EnableLocationButton(isEnable: boolean)
Shows or hides the native "my location" button.
setZoomOnCurrentLocation()
Moves the camera to the device's current location.
CameraCallback()
Returns a Promise. Android only. Calling it on iOS throws an error because the native module does not exist there.
Routes
SetPolyLine(currentLatLng: string, destinationLatLng: string[], color: string, width: string, apiKey: string)
Gets the best route from the TPL Maps routing service and draws it.
[!IMPORTANT]
currentLatLngand every destination use"lat;lng"order (the reverse of the other functions).
SetPolyLine('24.820186;67.103118', ['24.801976;67.031951'], '#1D8548', '2', TPL_MAPS_API_KEY);RemoveAllPolyLines()
Removes all drawn routes.
[!NOTE] Marked as not working in the current iOS native code.
Search
getSearchPointData(query: string, lat: string, lng: string, apiKey: string)
Finds the address of a point (reverse geocoding). Returns a Promise with the API response as JSON.
getSearchByQuery(query: string, apiKey: string)
Searches for places by text.
getOptimizedSearch(query: string, lat: string, lng: string, apiKey: string)
Searches for places by text, with results sorted by how close they are to the given point.
Maintainers
TPL Maps
| Name | Role | Email | |---|---|---| | Bilal Sipra | Head of Information Technology (IT) | [email protected] | | Daniyal Ahmed Khan | Manager & Team Lead (Mobile Development) | [email protected] | | Zaeem Ehsanullah | Assistant Manager (Mobile Application Development) | [email protected] |
Contributing
Found a bug or have a problem? Please email [email protected].
License
MIT
