How to add title to Polygon using react-native-maps - react-native

I'm trying to draw specific polygons in MapView, but I'm not sure how to add title to Polygon.
I tried to search official documentation, but only Marker has title property, so I'm not sure if it's supported.
Marker can be used something like this
<Marker
coordinate={latittude: x, longitude: y}
title="I am Marker"
/>
so is it possible to add title property to Polygon like this:
<Polygon
coordinates=[{latittude: x, longitude: y},...]
title="I am Polygon"
/>
???Expecting something like this...

I don't think you have any other option but you can use marker which works pretty nice.
<MapView.Marker key={`${key}Marker`} coordinate={center} anchor={centerAnchor} tracksViewChanges={false}>
<Text key={`${key}Text`} style={styles.clusterText}>
{data.properties.n}
</Text>
</MapView.Marker>
don't forget the tracksViewChanges={false} or the map will go crazy for many markers and put a huge load on the cpu

I also encountered the same problem, I know this is late but here's what I did. I also didn't know if react native maps have that method right now. So for alternative, I used Marker as title. And used this function from github to get the center of polygon.
center_polygon(coordinates) {
let x = coordinates.map(c => c.latitude);
let y = coordinates.map(c => c.longitude);
let minX = Math.min.apply(null, x);
let maxX = Math.max.apply(null, x);
let minY = Math.min.apply(null, y);
let maxY = Math.max.apply(null, y);
return {
latitude: (minX + maxX) / 2,
longitude: (minY + maxY) / 2
};
}
Polygon
return (
<View>
<Marker
coordinate={polygon_center}
>
<Text note style={{color:"#000", fontSize: 9}}>
{name}
</Text>
</Marker>
<Polygon
key={index}
coordinates={fixed_coordinates}
fillColor={this.hexToRgbA(color, 0.4)}
strokeColor={color}
strokeWidth={3}
>
</Polygon>
</View>
);

Proper complete solution for adding any component over polygon in center.
import {
View,
Text,
Button,
StyleSheet,
TouchableOpacity,
InteractionManager,
} from 'react-native';
import React, {useEffect, useRef, useState} from 'react';
import MapView, {Marker, Polygon} from 'react-native-maps';
import {useNavigation} from '#react-navigation/native';
import {COLOR_PLOT_BORDER} from '../../utils/colors';
let polygonsCoordinates = [
[
{latitude: 42.9348443, longitude: -72.287181},
{latitude: 42.9345929, longitude: -72.2832328},
{latitude: 42.9335415, longitude: -72.2834162},
{latitude: 42.9335572, longitude: -72.2852186},
{latitude: 42.9336671, longitude: -72.2869996},
{latitude: 42.934704, longitude: -72.2871498},
],
[
{latitude: 42.9345437, longitude: -72.2811199},
{latitude: 42.9347204, longitude: -72.2795911},
{latitude: 42.9343119, longitude: -72.2792585},
{latitude: 42.9341077, longitude: -72.2810394},
{latitude: 42.9345358, longitude: -72.2811146},
],
[
{latitude: 42.9329962, longitude: -72.2825093},
{latitude: 42.9333065, longitude: -72.2804494},
{latitude: 42.9322775, longitude: -72.2801382},
{latitude: 42.931979, longitude: -72.2824235},
{latitude: 42.9329726, longitude: -72.2824932},
],
];
export default function MapPlots() {
const mapRef = useRef(null);
const [region, setRegion] = useState({});
const [polygonCenters, setPolygonCenters] = useState([]);
useEffect(() => {
InteractionManager.runAfterInteractions(() => {
goToLocation();
getCenterOfAllPolygon();
});
}, []);
const getCenterOfAllPolygon = () => {
let centers = [];
polygonsCoordinates.map((v, i) => {
let c = getCenterPolygon(v);
centers.push(c);
});
setPolygonCenters(centers);
console.log('centers', centers);
};
const keeneRegion = {
latitude: 42.9329962,
longitude: -72.2825093,
latitudeDelta: 0.02,
longitudeDelta: 0.02,
};
const goToLocation = () => {
//complete this animation in 5 seconds
mapRef.current.animateToRegion(keeneRegion, 1 * 1000);
};
let getCenterPolygon = coordinates => {
let x = coordinates.map(c => c.latitude);
let y = coordinates.map(c => c.longitude);
let minX = Math.min.apply(null, x);
let maxX = Math.max.apply(null, x);
let minY = Math.min.apply(null, y);
let maxY = Math.max.apply(null, y);
console.log('adsasdad', minX, maxX, minY, maxY);
return {
latitude: (minX + maxX) / 2,
longitude: (minY + maxY) / 2,
};
};
const onPressPolygon = coordinates => {
let newCoordinates = getCenterPolygon(coordinates);
const goToRegion = {
...newCoordinates,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
mapRef.current.animateToRegion(goToRegion, 1 * 1000);
};
return (
<View style={styles.container}>
<MapView
provider={MapView.PROVIDER_GOOGLE}
ref={mapRef}
style={styles.map}
initialRegion={{
latitude: 0, // This the position data
longitude: 0, // This is also position data
latitudeDelta: 200,
longitudeDelta: 1,
}}
loadingEnabled={true}
onRegionChangeComplete={r => setRegion(r)}>
{polygonsCoordinates.map((polygon, i) => (
<View key={i}>
<Polygon
coordinates={polygon}
strokeColor={COLOR_PLOT_BORDER} // fallback for when `strokeColors` is not supported by the map-provider
fillColor={i === 0 ? 'blue' : i === 2 ? 'green' : 'yellow'}
strokeWidth={0.3}
tappable
geodesic
onPress={() => onPressPolygon(polygon)}
/>
<Marker
anchor={{x: 0.5, y: 0.5}}
centerOffset={{x: 0.5, y: 0.5}}
zIndex={10}
coordinate={polygonCenters[i]}>
<Text note style={{color: '#000', fontSize: 15}}>
1001
</Text>
</Marker>
</View>
))}
</MapView>
<Button onPress={() => goToLocation()} title="Go to Current Location" />
<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',
},
});

Related

I got this error message while trying to get my position in react native:

in the device:
Error while updating property 'corrdinate' of view maneg by: AIRMapMarker
in the console terminal:
ERROR Warning: Failed prop type: The prop coordinate.latitude is marked as required in MapMarker, but its value is null.
my code:
import { useContext, useEffect, useState } from "react";
import Context from "../../global/Context";
import MapView, { Marker } from "react-native-maps";
import * as Position from "expo-location";
import {
View,
StyleSheet,
ImageBackground,
Dimensions,
Text,
TouchableOpacity,
} from "react-native";
const Location = (props) => {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const { states } = useContext(Context);
const place = states.place;
useEffect(() => {
(async () => {
let { status } = await Position.requestBackgroundPermissionsAsync();
if (status !== "granted") {
setErrorMsg("App não tem permissão para acessar localizaçõa");
}
let location = await Position.getCurrentPositionAsync();
setLocation(location);
})();
}, []);
let text = "Carregando...";
if (errorMsg) {
text = errorMsg;
} else if (location) {
text = JSON.stringify(location);
}
return (
<ImageBackground
style={{ flex: 1 }}
source={require("../../img/mypoint-wallpaper.jpg")}
>
<View style={styles.container}>
<MapView
style={{
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
}}
initialRegion={{
latitude: place.latitude,
longitude: place.longitude,
longitudeDelta: 0.01,
latitudeDelta: 0.01,
}}
>
<Marker
coordinate={{
latitude: place.latitude,
longitude: place.longitude,
}}
/>
<Marker
title="Sua localização"
coordinate={{
latitude: location && location.coords.latitude,
longitude: location && location.coords.longitude,
}}
/>
</MapView>
<Text style={styles.txtStyle}>{text}</Text>
</View>
</ImageBackground>
);
};
To Draw a marker longitude and latitude is must and are required props.
your latitude and longitude is null here
<Marker
coordinate={{
latitude: place.latitude,
longitude: place.longitude
}}/>
so draw the marker only when you have coordinates
use something like this :-
{(place.latitude && place.longitude) ? <Marker
coordinate={{
latitude: place.latitude,
longitude: place.longitude
}}/> : null
}

TypeError: null is not an object (evaluating 'this.state.initialPosition') - React Native

I am doing a RN project using Geolocation to get the current location, but I don't know what's wrong in this.
Here is my code:
import React, { Component } from 'react';
// Import required component
import { SafeAreaView, StyleSheet, Text, Alert, TouchableOpacity, Image, View } from 'react-native';
import { request, PERMISSIONS } from 'react-native-permissions';
// Import vector icons
import Icon from 'react-native-vector-icons/FontAwesome';
import MapView, { PROVIDER_GOOGLE, Platform, Marker, Callout, Circle } from 'react-native-maps';
import coordinate from '../../api/binLocationData';
import Geolocation from '#react-native-community/geolocation';
export class Home extends Component {
componentDidMount() {
this.requestLocationPermission();
}
requestLocationPermission = async () => {
if (Platform.OS === 'IOS') {
var response = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
console.log('IOS: ' + response);
if (response === 'granted') {
this.locateCurrentPosition();
}
} else {
var response = await request(PERMISSIONS.ANDROID.LOCATION_WHEN_IN_USE);
console.log('Android: ' + response);
if (response === 'granted') {
this.locateCurrentPosition();
}
}
};
locateCurrentPosition = () => {
Geolocation.getCurrentPosition(
(position) => {
console.log(JSON.stringify(position));
let initialPosition = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
};
this.setState({ initialPosition });
},
(error) => Alert.alert(error.message),
{ enableHighAccuracy: true, timeout: 1000, maximumAge: 1000 },
);
};
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<MapView
provider={PROVIDER_GOOGLE}
ref={(map) => (this._map = map)}
showsUserLocation={true}
style={styles.maps}
initialRegion={this.state.initialPosition}
// initialRegion={{
// latitude: 16.0599,
// longitude: 108.2438,
// latitudeDelta: 0.05,
// longitudeDelta: 0.05,
// }}
>
<Circle
radius={1000}
center={{ latitude: 16.0599, longitude: 108.2438 }}
fillColor={'rgba(51,150,152,0.3)'}
/>
<Marker coordinate={{ latitude: 16.0599, longitude: 108.2438 }}>
<Callout>
<Text>Bin 1</Text>
</Callout>
{/* <Image source={require('./src/assets/icons/bin.png')} /> */}
</Marker>
{coordinate.map((marker) => (
<Marker
key={marker.id}
coordinate={{ latitude: marker.latitude, longitude: marker.longitude }}
title={marker.name}
/>
))}
</MapView>
</View>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
maps: {
...StyleSheet.absoluteFillObject,
},
});
export default Home;
And the debugging show:
TypeError: null is not an object (evaluating 'this.state.initialPosition')
How can I fix it?
You forget to initialise state. You should add this to your class component:
constructor(props) {
this.state = {
latitude: 0,
longitude: 0,
latitudeDelta: 0,
longitudeDelta: 0,
};
}
This should works:
import React, { Component } from "react";
// Import required component
import {
SafeAreaView,
StyleSheet,
Text,
Alert,
TouchableOpacity,
Image,
View,
} from "react-native";
import { request, PERMISSIONS } from "react-native-permissions";
// Import vector icons
import Icon from "react-native-vector-icons/FontAwesome";
import MapView, {
PROVIDER_GOOGLE,
Platform,
Marker,
Callout,
Circle,
} from "react-native-maps";
import coordinate from "../../api/binLocationData";
import Geolocation from "#react-native-community/geolocation";
export class Home extends Component {
constructor(props) {
this.state = {
latitude: 0,
longitude: 0,
latitudeDelta: 0,
longitudeDelta: 0,
};
}
componentDidMount() {
this.requestLocationPermission();
}
requestLocationPermission = async () => {
if (Platform.OS === "IOS") {
var response = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
console.log("IOS: " + response);
if (response === "granted") {
this.locateCurrentPosition();
}
} else {
var response = await request(PERMISSIONS.ANDROID.LOCATION_WHEN_IN_USE);
console.log("Android: " + response);
if (response === "granted") {
this.locateCurrentPosition();
}
}
};
locateCurrentPosition = () => {
Geolocation.getCurrentPosition(
(position) => {
console.log(JSON.stringify(position));
let initialPosition = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
};
this.setState({
latitude: initialPosition.latitude,
longitude: initialPosition.longitude,
latitudeDelta: initialPosition.latitudeDelta,
longitudeDelta: initialPosition.longitudeDelta,
});
},
(error) => Alert.alert(error.message),
{ enableHighAccuracy: true, timeout: 1000, maximumAge: 1000 }
);
};
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<MapView
provider={PROVIDER_GOOGLE}
ref={(map) => (this._map = map)}
showsUserLocation={true}
style={styles.maps}
initialRegion={this.state.initialPosition}
// initialRegion={{
// latitude: 16.0599,
// longitude: 108.2438,
// latitudeDelta: 0.05,
// longitudeDelta: 0.05,
// }}
>
<Circle
radius={1000}
center={{ latitude: 16.0599, longitude: 108.2438 }}
fillColor={"rgba(51,150,152,0.3)"}
/>
<Marker coordinate={{ latitude: 16.0599, longitude: 108.2438 }}>
<Callout>
<Text>Bin 1</Text>
</Callout>
{/* <Image source={require('./src/assets/icons/bin.png')} /> */}
</Marker>
{coordinate.map((marker) => (
<Marker
key={marker.id}
coordinate={{
latitude: marker.latitude,
longitude: marker.longitude,
}}
title={marker.name}
/>
))}
</MapView>
</View>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
maps: {
...StyleSheet.absoluteFillObject,
},
});
export default Home;

How to fit all markers to given coordinates provided dynamically? (react-native-maps)

I have implemented this example where I can zoom in to the given coordinates by clicking on the button.
Below you can read what I aiming to implement and I couldn't:
First, I want to be able to read coordinates out of a dynamic array, I tried by putting the array in the state but it fails.
const ASPECT_RATIO = width / height;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const MARKERS = [
{
latitude: 42.637368,
longitude: 21.148682,
},
{
latitude: 42.604021,
longitude: 21.261292,
},
{
latitude: 42.500833,
longitude: 21.181641,
}
];
const DEFAULT_PADDING = { top: 60, right: 60, bottom: 60, left: 60 };
export default class map_of_patients extends React.Component {
constructor(){
this.state={}
}
fitAllMarkers() {
this.map.fitToCoordinates(MARKERS, {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}
render() {
return (
<View style={styles.container}>
<MapView
ref={ref => {
this.map = ref;
}}
style={styles.map}
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
>
{MARKERS.map((marker, i) => (
<Marker key={i} identifier={`id${i}`} coordinate={marker} />
))}
</MapView>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={() => this.fitAllMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit All Markers</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
Second I would like to call the function fitAllMarkers into coordinates on start, so I don't have to click somewhere to do it. I tried by calling in inside componentDidMount() but didn't work either.
Third, I would like to zoom in to the region by giving the coordinates from the dynamic array.
I managed to fix all the issues mentioned in the question by doing the following:
-To fit all markers on initialization of the map I did as #Marek Lisik suggested using onMapReady={this.fitAllMarkers.bind(this)}.
-And to read from a dynamic array I managed to pass the array beforehand to the state and by the time the map is initialized it already had some data.
Here is the entire code again with the changes:
constructor(props) {
super(props);
this.state = {
region: {
latitude: 42.65847,
longitude: 21.16070,
latitudeDelta: 0.500,
longitudeDelta: 0.500 * width / height,
},
MARKERS: [
{
latitude: 42.637368,
longitude: 21.148682,
description: "dfsdf",
title: "title"
},
{
latitude: 42.604021,
longitude: 21.261292,
description: "sdfsdf",
title: "title"
},
{
latitude: 42.500833,
longitude: 21.181641,
description: "sdfsdfds",
title: "title"
}
]
};
}
fitAllMarkers() {
this.map.fitToCoordinates(this.state.MARKERS, {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}
render() {
return (
<View style={styles.container}>
<MapView
ref={ref => {
this.map = ref;
}}
style={styles.map}
initialRegion={this.state.region}
onMapReady={this.fitAllMarkers.bind(this)}
>
{this.state.MARKERS.map((marker, i) => (
<Marker key={i} identifier={`id${i}`} coordinate={marker}
description={marker.description}>
</Marker>
))}
</MapView>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={() => this.fitAllMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit All Markers</Text>
</TouchableOpacity>
</View>
</View>
);
}

How to use animateToRegion function in react native maps?

I am using MapView of react-native map clustering and Marker and callout of react-native-maps. I am unable to use animateToRegion.
It shows me this.mapView.animateToRegion is not a function
<MapView
ref={map=>{mapView = map}}
provider='google'
clustering={true}
onClusterPress={this.onPressCluster}
region={this.state.region}
onRegionChange={this.onRegionChange}
onRegionChangeComplete={this.onRegionChangeComplete}
style={styles.map}
showsUserLocation={true}
followUserLocation={true}
zoomEnabled={true}
ScrollEnabled={true}
showsBuildings={true}
showsMyLocationButton={false}/>
animate(){
let r = {
latitude: 42.5,
longitude: 15.2,
latitudeDelta: 7.5,
longitudeDelta: 7.5,
};
this.mapView.root.animateToRegion(r, 2000);
}
render(){
return(
<MapView
ref = {(ref)=>this.mapView=ref}
region={{
latitude: 35.688442,
longitude: 51.403753,
latitudeDelta: 0.5,
longitudeDelta: 0.5,
}}
onPress={()=>this.animate()}
>
...markers...
</MapView>
);
}
clickOnSearchedAddress = (placeId, index, dscrpt) => {
getLatLongFromAddress(placeId).then((response) => {
let r = {
[Constants.KEY_LATITUDE]: response.geometry.location.lat,
[Constants.KEY_LONGITUDE]: response.geometry.location.lng,
latitudeDelta: latitudeDelta,
longitudeDelta: longitudeDelta
}
this.mapView.animateToRegion(r, 1000)
Keyboard.dismiss()
isSetTextProgramatically = true;
this.setState({
search: dscrpt,
searchData: [],
})
}).then((error) => { })
}
<MapView
ref={ref => this.mapView = ref}
provider={PROVIDER_GOOGLE}
style={[styles.map, , { width: this.state.width }]}
initialRegion={region}
onRegionChangeComplete={this.onRegionChange}
showsMyLocationButton={true}
showsCompass={true}
showsUserLocation={true}
onMapReady={() => this.setState({ width: width - 1 })}}
/>
I believe the issue is that the prop is not called ref, but instead mapRef. Pass the ref in there, and it should work.
You would then also have to call it with something like this this.mapView.[current/map]animateToRegion. Just check the mapView object you get.

zooming map using latitudeDelta and longitudeDelta

I'm using react-native-maps. I'm showing direction from source to destination using polyline. But it's not aligned to centre of screen. Zooming is also not proper.
How can I zoom map properly to show direction in the centre of screen properly.
This is my code:
const ASPECT_RATIO = width / height;
const LATITUDE = 40.7128;
const LONGITUDE = 74.0060;
const LATITUDE_DELTA = 0.0012;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
this.state = {
region: {
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}
}
<MapView
provider={PROVIDER_GOOGLE}
style={StyleSheet.absoluteFillObject}
region={this.state.region}>
<MapView.Polyline
coordinates={direction}
strokeWidth={4}
strokeColor="#000"/>
</MapView>
Thanks in advance
I'm able to zoom the map to fit the coordinates into the screen.
This is the code:
const { width, height } = Dimensions.get('window');
const ASPECT_RATIO = width / height;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const SPACE = 0.01;
function createMarker(modifier = 1) {
return {
latitude: LATITUDE - (SPACE * modifier),
longitude: LONGITUDE - (SPACE * modifier),
};
}
const MARKERS = [
createMarker(),
createMarker(2),
createMarker(3),
createMarker(4),
];
const DEFAULT_PADDING = { top: 40, right: 40, bottom: 40, left: 40 };
class FitToCoordinates extends Component {
fitPadding() {
this.map.fitToCoordinates([MARKERS[2], MARKERS[3]], {
edgePadding: { top: 100, right: 100, bottom: 100, left: 100 },
animated: true,
});
}
fitBottomTwoMarkers() {
this.map.fitToCoordinates([MARKERS[2], MARKERS[3]], {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}
fitAllMarkers() {
this.map.fitToCoordinates(MARKERS, {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}
render() {
return (
<View style={styles.container}>
<MapView
ref={ref => { this.map = ref; }}
style={styles.map}
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
>
{MARKERS.map((marker, i) => (
<MapView.Marker
key={i}
coordinate={marker}
/>
))}
</MapView>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={() => this.fitPadding()}
style={[styles.bubble, styles.button]}
>
<Text>Fit Bottom Two Markers with Padding</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.fitBottomTwoMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit Bottom Two Markers</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.fitAllMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit All Markers</Text>
</TouchableOpacity>
</View>
</View>
);
}
}