I'm having a weird issue with the React Native Maps library. At the moment when I follow all the documentation correctly, every time I move the map, it appears to stutter and move back to the original location. Or sporadically it will move to the location I want to (With stutter)
App.js
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import MapView from "react-native-maps";
import Geolocation from 'react-native-geolocation-service';
import {YellowBox} from 'react-native';
type Props = {};
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
region: {
latitude: 53.41058,
longitude: -2.97794,
latitudeDelta: 0.1,
longitudeDelta: 0,
}
}
}
componentDidMount() {
Geolocation.getCurrentPosition(
(position) => {
console.warn(position.coords.latitude);
console.warn(position.coords.longitude);
this.setState({
region: {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: 0.02,
longitudeDelta: 0,
}
});
},
(error) => {
console.warn(error.code, error.message);
},
{enableHighAccuracy: true, timeout: 20000, maximumAge: 1000},
)
}
onRegionChange(region) {
this.setState({
region: region
});
}
render() {
return (
<MapView
style={styles.map}
region={this.state.region}
showsUserLocation={true}
onRegionChange={region => {
this.setState({region});
}}
/>
);
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'flex-end',
alignItems: 'center',
},
map: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
});
However, if I change onRegionChange to onRegionChangeCompleted, I can move around the map just fine. But then I cannot tilt and when I turn the map using my fingers it will sometimes snap back to the original location.
Has anyone else had this weird issue or is there something I'm doing wrong?
Change onRegionChange to onRegionChangeComplete and it should work smoothly as expected now.
:)
Removing region = {this.state.region} from MapView solved this for me.
for anyone that couldn't solve the problem with the above answers, this answer on https://github.com/react-native-maps/react-native-maps/issues/3639 from #jalasem worked for me, here is a condensed version:
import React, { useEffect, useRef } from 'react'
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps'
const INITIAL_REGION = {
latitude: 44.49317207749917,
longitude: 20.896348971873522,
latitudeDelta: 4.136923536294034,
longitudeDelta: 5.68705391138792,
}
const Map = ({ location }) => {
const mapRef = useRef(null)
useEffect(() => {
// receive a point on the map through props
if (location) {
console.log('change location, location: ', location)
mapRef.current.animateToRegion({
latitude: location.latitude,
longitude: location.longitude,
latitudeDelta: 0.2,
longitudeDelta: 0.2,
})
}
}, [location])
return (
<MapView
provider={PROVIDER_GOOGLE}
initialRegion={INITIAL_REGION}
ref={mapRef}
/>
)
}
export default Map
I needed a way to change the location if a user clicked on a button outside the map, while also being able to move around the map freely, so this solution worked best for me.
Related
i'm trying to get Latitude and longitude values from a GPS Module Tracker which I have made myself. I'm fetching values from it (Lat, Lng). it does give me the current location but does not move smoothly like a uber car instead it changes location at a fixed point after some time. I have used navigator.geolocation.watchPosition to fetch updated location. Is this not the right way?
App.js
import React, { Component } from 'react';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';
import Display from './src/Display';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true
};
}
componentDidMount() {
fetch('https://api.thingspeak.com/channels/1323137/feeds.json?results=1')
.then((response) => response.json())
.then((json) => {
this.setState({ data: json.feeds });
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
render() {
const { data, isLoading } = this.state;
return (
<View style={{ flex: 1, padding: 24 }}>
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={data}
keyExtractor={( id, index ) => index.toString()}
renderItem={({ item }) =>
<Display
value1 = {item.field1}
value2 = {item.field2}
value3 = {item.field3}
/>
}
/>
)}
</View>
);
}
};
Display.js
import React, { Component } from "react";
import { StyleSheet, View, Image, Animated, Text, image, Dimensions } from "react-native";
import MapView, { Marker,PROVIDER_GOOGLE} from "react-native-maps";
const { width, height } = Dimensions.get("window");
export default class App extends React.Component {
constructor(props) {
super(props);
this.State = {
region:
{
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
latitudeDelta: 0.01,
longitudeDelta: 0.0
}
}
}
componentDidMount() {
this.watchID = navigator.geolocation.watchPosition((position) => {
var lat = parseFloat(position.this.props.value1)
var long = parseFloat(position.this.props.value2)
var latestRegion = {
latitude: lat,
longitude: long,
latitudeDelta: 0.01,
longitudeDelta: 0.0
}
this.setState({region: latestRegion})
})
}
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watchID)
}
render() {
return (
<View style={styles.container}>
<MapView
initialRegion={this.State.region}
provider={PROVIDER_GOOGLE}
showsUserLocation={false}
showsCompass={true}
rotateEnabled={false}
showsBuildings={true}
showsTraffic={true}
showsIndoors={true}
style={{
width,
height,
}}
>
<MapView.Marker
style={{ width: 100, height: 15 }}
title="Uni Bus"
description="Route-1"
resizeMode="contain"
image={require('D:/React Native apps/track-app/assets/Bus_image_2.png')}
coordinate={{
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2)
}}
/>
</MapView>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: 25,
padding: -15,
},
});
I'm trying to do this in react native.
I am very new to learning React Native and would appreciate any help that can be provided with my code. I am currently working off of tutorials and have been stuck for hours. Hopefully this is enough info to get some help. Thanks in advance!
My goal
I am trying to do three things (in this order):
Get the user's current location
Fetch the items data from an API (example outlined below). It should be noted that eventually the contents of the fetch will be dependent on the user's current location.
Parse the items data to create markers and a unique list of categories and place those on the map with the user's current location at center to begin with.
I am looking to be able to watch the user's location and move and update the map accordingly. I also don't want to show the map until all of the markers are in place.
Here are my react versions: react-native-cli: 2.0.1 react-native: 0.63.2
My Bugs
I am using both the Android Studio emulator as well as the Xcode emulator and am currently running into the following problems:
The iOS emulator on Xcode renders fine the first time, but on subsequent refreshes I see 1 or two of my 5 markers missing.
On Android, the map loads perfectly then appears to immediately redraw itself and center on the Googleplex in California instead of the user's current location.
Emulator location is set to London, UK
I suspect I might have some race condition with fetching the items and current location before the map renders but I can't be sure.
My Code
//my API response structure
{
"id": "96845",
"title": "Item_title_goes_here",
"image": "https://someURL/image.JPG",
"stories": 46,
"lat": some_lat_number,
"lon": some_lon_number,
"category": "category_name",
"description": "long_description"
},
//ajax.js
export default {
async fetchInitialItems() {
try {
const response = await fetch(apiHost + '/api/v1/items/');
const responseJson = await response.json();
return responseJson;
} catch (error) {
console.error(error);
}
},
async fetchItemDetail(itemId) {
try {
const response = await fetch(apiHost + '/api/items/' + itemId);
const responseJson = await response.json();
return responseJson;
} catch (error) {
console.error(error);
}
},
};
//ExploreScreen.js (my map component)
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
Image,
Animated,
Dimensions,
TouchableOpacity,
PermissionsAndroid,
ScrollView,
Platform,
StatusBar,
} from 'react-native';
import MapView, {
PROVIDER_GOOGLE,
Marker,
Callout,
Polygon,
} from 'react-native-maps';
import PropTypes from 'prop-types';
import Geolocation from '#react-native-community/geolocation';
import { mapDarkStyle, mapStandardStyle } from '../model/mapData';
import ajax from '../utils/ajax';
import MapCarousel from './MapCarousel';
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Fontisto from 'react-native-vector-icons/Fontisto';
import StarRating from '../components/StarRating';
/**
|--------------------------------------------------
| Variables
|--------------------------------------------------
*/
const { width, height } = Dimensions.get('window');
const SCREEN_HEIGHT = height;
const SCREEN_WIDTH = width;
const ASPECT_RATIO = width / height;
const LATITUDE_DELTA = 0.0422;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const darkTheme = false;
/**
|--------------------------------------------------
| Component
|--------------------------------------------------
*/
class ExploreScreen extends React.Component {
/****** Props & States ******/
state = {
region: {
latitude: 0,
longitude: 0,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
},
items: [],
markers: [],
categories: [],
currentMapRegion: null,
};
/****** Functions ******/
///when carousel item selected
onCarouselItemSelected() {
alert('carousel item selected');
}
//when the carousel in scrolled
onCarouselIndexChange(itemID) {
const item = this.state.items.find(item => item.id == itemID);
const marker = this.state.markers.find(marker => marker.id == itemID);
const coordinates = { lat: item.lat, lon: item.lon };
this.goToLocation(coordinates);
}
//get current position
getLocation(that) {
Geolocation.getCurrentPosition(
//get the current location
position => {
const region = {
latitude: parseFloat(position.coords.latitude),
longitude: parseFloat(position.coords.longitude),
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
that.setState({ region });
},
error => alert(error.message),
{ enableHighAccuracy: true, timeout: 20000 },
);
//get location on location change
that.watchID = Geolocation.watchPosition(position => {
const currentRegion = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
this.setState({ region: currentRegion });
});
}
//move map to a lat/lon
goToLocation(coordinates) {
if (this.map) {
this.map.animateToRegion({
latitude: coordinates.lat,
longitude: coordinates.lon,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
});
}
}
//when the region changes for the map
onRegionChangeComplete(region) {
//I dont know what to do here
}
//move map to center of current location
gotToCenter() {
if (this.map) {
this.map.animateToRegion({
latitude: this.state.region.latitude,
longitude: this.state.region.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
});
}
}
//map the categories store in the state
mapCategories = () => {
const uniqueCategories = [];
this.state.items.map(item => {
if (uniqueCategories.indexOf(item.category) === -1) {
uniqueCategories.push(item.category);
}
});
this.setState({ categories: uniqueCategories });
};
//map the items to markers and store in the state
mapMarkers = () => {
const markers = this.state.items.map(item => (
<Marker
key={item.id}
coordinate={{ latitude: item.lat, longitude: item.lon }}
image={require('../assets/map_marker.png')}
tracksViewChanges={false}
title={item.title}
description={item.description}
/>
));
this.setState({ markers: markers });
};
/****** Lifecycle Functions ******/
async componentDidMount() {
var that = this;
//Checking for the permission just after component loaded
if (Platform.OS === 'ios') {
//for ios
this.getLocation(that);
} else {
//for android
async function requestLocationPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Access Required',
message: 'This App needs to Access your location',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
//To Check, If Permission is granted
that.getLocation(that);
} else {
alert('Permission Denied');
}
} catch (err) {
alert('err', err);
console.warn(err);
}
}
requestLocationPermission();
}
//get location on location change
that.watchID = Geolocation.watchPosition(position => {
const currentRegion = {
latitude: parseFloat(position.coords.latitude),
longitude: parseFloat(position.coords.longitude),
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
this.setState({ region: currentRegion });
});
console.log(this.state.region);
const items = await ajax.fetchInitialItems();
this.setState({ items });
this.mapMarkers();
this.mapCategories();
}
componentWillUnmount = () => {
Geolocation.clearWatch(this.watchID);
};
render() {
const lat = this.state.region.latitude;
const lon = this.state.region.longitude;
if (this.state.items && lat && lon) {
return (
<View>
<MapView
ref={map => {
this.map = map;
}}
onRegionChangeComplete={this.onRegionChangeComplete.bind(this)}
toolbarEnabled={false}
showsMyLocationButton={false}
provider={PROVIDER_GOOGLE}
style={styles.map}
customMapStyle={darkTheme ? mapDarkStyle : mapStandardStyle}
showsUserLocation={true}
followsUserLocation={true}
region={this.state.region}>
{this.state.markers}
</MapView>
{/* center button */}
<TouchableOpacity
onPress={this.gotToCenter.bind(this)}
style={styles.centerButtonContainer}>
<Ionicons name="md-locate" size={20} />
</TouchableOpacity>
<View style={styles.carousel}>
<MapCarousel
data={this.state.items}
onPressItem={() => {
this.onCarouselItemSelected.bind(this);
}}
onUpdateLocation={this.onCarouselIndexChange.bind(this)}
/>
</View>
</View>
);
} else {
return (
<View style={styles.container}>
<Text style={styles.header}>Loading...</Text>
</View>
);
}
}
}
/**
|--------------------------------------------------
| Styles
|--------------------------------------------------
*/
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
height: '100%',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
map: {
height: '100%',
},
carousel: {
position: 'absolute',
bottom: 25,
},
header: {
fontSize: 50,
},
//character name
name: {
fontSize: 15,
marginBottom: 5,
},
//character image
image: {
width: 170,
height: 80,
},
//center button
centerButtonContainer: {
width: 40,
height: 40,
position: 'absolute',
bottom: 260,
right: 10,
borderColor: '#191919',
borderWidth: 0,
borderRadius: 30,
backgroundColor: '#d2d2d2',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 9,
},
shadowOpacity: 0.48,
shadowRadius: 11.95,
elevation: 18,
opacity: 0.9,
},
});
export default ExploreScreen;
I'm getting current user latitude and longitude and showing them on the map. But when I start tracking the user it shows error sometimes as displayed in image.
But on emulator is working totally fine. This behavior is happening only on Real device.
but some times it works properly as displayed in the image
.
I am unable to figure out, why this is happening. My code is below
import React, { Component } from "react";
import {
StyleSheet,
PermissionsAndroid,
View,
Text,
Image
} from "react-native";
import { AskPermission } from "../../components/AskPermissions";
import MapView, { PROVIDER_GOOGLE, Marker, Polyline } from "react-native-maps";
import haversine from "haversine";
class TrackCurrentUser extends Component {
state = {
region: {
latitude: 0,
longitude: 0,
latitudeDelta: 0.0922, // must give some valid value
longitudeDelta: 0.0421 // must give some valid value
},
error: "",
routeCoordinates: [],
distanceTravelled: 0, // contain live distance
prevLatLng: {} // contain pass lat and lang value
};
// getLocation Permission and call getCurrentLocation method
componentDidMount() {
const permission = PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION;
AskPermission(permission);
this.getCurrentLocation();
}
// getting the current Location of a user...
getCurrentLocation = () => {
navigator.geolocation.watchPosition(
position => {
const { latitude, longitude } = position.coords;
const { routeCoordinates } = this.state;
const newCoordinate = { latitude, longitude };
let region = {
latitude: parseFloat(position.coords.latitude),
longitude: parseFloat(position.coords.longitude),
latitudeDelta: 5,
longitudeDelta: 5
};
this.setState({
initialRegion: region,
region: region,
routeCoordinates: routeCoordinates.concat([newCoordinate]),
distanceTravelled:
this.state.distanceTravelled + this.calcDistance(newCoordinate),
prevLatLng: newCoordinate
});
},
error => console.log(error),
{
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 1000,
distanceFilter: 1
}
);
};
// animate to current user Location
goToInitialLocation = () => {
let initialRegion = Object.assign({}, this.state.initialRegion);
initialRegion["latitudeDelta"] = 0.005;
initialRegion["longitudeDelta"] = 0.005;
this.mapView.animateToRegion(initialRegion, 2000);
};
// lat & lng for Marker
getMapRegion = () => ({
latitude: this.state.region.latitude,
longitude: this.state.region.longitude
});
// calculate the total distance
calcDistance = newLatLng => {
// console.warn("Method Called");
const { prevLatLng } = this.state;
return haversine(prevLatLng, newLatLng) || 0;
};
render() {
return (
<View style={{ flex: 1 }}>
<MapView
style={{ flex: 0.9 }}
provider={PROVIDER_GOOGLE}
region={this.state.mapRegion}
followUserLocation={true}
ref={ref => (this.mapView = ref)}
zoomEnabled={true}
showsUserLocation={true}
onMapReady={this.goToInitialLocation}
initialRegion={this.state.initialRegion}
>
<Polyline coordinates={this.state.routeCoordinates} strokeWidth={5} />
<Marker coordinate={this.getMapRegion()} title={"Current Location"}>
<Image
source={require("../../images/car.png")}
style={{ height: 35, width: 35 }}
/>
</Marker>
</MapView>
<View style={styles.distanceContainer}>
<Text>{parseFloat(this.state.distanceTravelled).toFixed(2)} km</Text>
</View>
</View>
);
}
}
export default TrackCurrentUser;
const styles = StyleSheet.create({
distanceContainer: {
flex: 0.1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "transparent"
}
});
waiting for your solution to solve this problem.
After few months I again started to work on maps and started looking on my old code to find the issue again and luckily this time I found the problem.
Problem
Problem was with onMapReady
onMapReady={this.goToInitialLocation}
// animate to current user Location
goToInitialLocation = () => {
let initialRegion = Object.assign({}, this.state.initialRegion);
initialRegion["latitudeDelta"] = 0.005;
initialRegion["longitudeDelta"] = 0.005;
this.mapView.animateToRegion(initialRegion, 2000);
};
Solution
I removed this code and following line in getCurrentLocation function.
this.map.animateToRegion(region, 1000);
Now the getCurrentLocation function looks like
getCurrentLocation = () => {
navigator.geolocation.watchPosition(
position => {
const { latitude, longitude } = position.coords;
const { routeCoordinates } = this.state;
const newCoordinate = { latitude, longitude };
let region = {
latitude: parseFloat(position.coords.latitude),
longitude: parseFloat(position.coords.longitude),
latitudeDelta: 5,
longitudeDelta: 5
};
this.setState({
initialRegion: region,
region: region,
routeCoordinates: routeCoordinates.concat([newCoordinate]),
distanceTravelled:
this.state.distanceTravelled + this.calcDistance(newCoordinate),
prevLatLng: newCoordinate
});
},
//animate to user's current location
this.map.animateToRegion(region, 1000);
error => console.log(error),
{
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 1000,
distanceFilter: 1
}
);
};
I'm very new to react native and somehow I was able to display a map and some markers on it. But I need to read set of locations (coordinates) from a remote server and display on map. In other words, makers need to be change their locations.
I tried a few difference ways, but any of those didn't help. If anyone has previous experience please help.
Following is my existing code.
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import MapView, {PROVIDER_GOOGLE} from 'react-native-maps';
import {Container, Header, Content, Footer, FooterTab, Title, Button, Icon} from 'native-base';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
latitude: 6.9212768,
longitude: 79.9610316,
error: null,
friends: [],
};
}
componentDidMount() {
navigator.geolocation.watchPosition(
(position) => {
console.log("wokeeey");
console.log(position);
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
//TODO: send user location to server
},
(error) => this.setState({error: error.message}),
{enableHighAccuracy: false, timeout: 200000, maximumAge: 1000},
);
//API call to get friends
this.setState({
friends: [
{
latitude: 6.9243768,
longitude: 79.9612316,
key: "friend 1"
},
{
latitude: 6.9213768,
longitude: 79.9641316,
key: "friend 2"
}
],
});
}
render() {
contents = this.state.friends.map((item) => {
return (
<MapView.Marker
key={item.key}
coordinate={{"latitude": item.latitude, "longitude": item.longitude}}
title={item.key}/>
);
});
return (
<Container>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.container}
showsUserLocation={true}
showsMyLocationButton={true}
zoomEnabled={true}
followsUserLocation={true}
initialRegion={{
latitude: this.state.latitude,
longitude: this.state.longitude,
latitudeDelta: 0.0158,
longitudeDelta: 0.0070
}}
>
{!!this.state.latitude && !!this.state.longitude && <MapView.Marker
coordinate={{"latitude": this.state.latitude, "longitude": this.state.longitude}}
title={"You're here"} pinColor={'#3498db'}
/>}
<View>{contents}</View>
</MapView>
</Container>
);
}
}
You can use the following code to update the user location received from a remote server periodically:
componentDidMount() {
setTimeout(function () {
// add your code for get and update makers every second
}, 1000);
}
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import MapView, {PROVIDER_GOOGLE} from 'react-native-maps';
import {Container, Header, Content, Footer, FooterTab, Title, Button, Icon} from 'native-base';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
latitude: 6.9212768,
longitude: 79.9610316,
error: null,
friends: [],
contents: null
};
}
componentDidMount() {
navigator.geolocation.watchPosition(
(position) => {
console.log("wokeeey");
console.log(position);
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
//TODO: send user location to server
},
(error) => this.setState({error: error.message}),
{enableHighAccuracy: false, timeout: 200000, maximumAge: 1000},
);
//API call to get friends
this.setState({
friends: [
{
latitude: 6.9243768,
longitude: 79.9612316,
key: "friend 1"
},
{
latitude: 6.9213768,
longitude: 79.9641316,
key: "friend 2"
}
],
}, () => this._renderFriends());
}
_renderFriends() {
const contents = this.state.friends.map((item) => {
return (
<MapView.Marker
key={item.key}
coordinate={{"latitude": item.latitude, "longitude": item.longitude}}
title={item.key}/>
);
});
this.setState({contents})
}
render() {
return (
<Container>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.container}
showsUserLocation={true}
showsMyLocationButton={true}
zoomEnabled={true}
followsUserLocation={true}
initialRegion={{
latitude: this.state.latitude,
longitude: this.state.longitude,
latitudeDelta: 0.0158,
longitudeDelta: 0.0070
}}
>
{!!this.state.latitude && !!this.state.longitude && <MapView.Marker
coordinate={{"latitude": this.state.latitude, "longitude": this.state.longitude}}
title={"You're here"} pinColor={'#3498db'}
/>}
<View>{this.state.contents}</View>
</MapView>
</Container>
);
}
}
I get an error:
"Error using newLatLngBounds(LatLngBounds, int): Map size can't be zero. Most likely layout has not yet occured for the map view. Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions".
But I set up an alert for getCurrentPosition and I'm receiving coordinates from getCurrentPosition().
import React, { Component } from 'react';
import { View, Dimensions } from 'react-native';
import MapView from 'react-native-maps';
const {width, height} = Dimensions.get('window')
const SCREEN_HEIGHT = height
const SCREEN_WIDTH = width
const ASPECT_RATIO = width / height
const LATITUDE_DELTA = 0.0922
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO
class Map extends Component {
constructor(props) {
super(props)
this.state = {
isMapReady: false,
initialPosition: {
longitude: 0,
latitude: 0,
longitudeDelta: 0,
latitudeDelta: 0
},
markerPosition: {
longitude: 0,
latitude: 0
}
}
}
watchID: ?number = null
componentDidMount() {
navigator.geolocation.getCurrentPosition((position) => {
alert(JSON.stringify(position))
var lat = parseFloat(position.coords.latitude)
var long = parseFloat(position.coords.longitude)
var initialRegion = {
latitude: lat,
longitude: long,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA
}
this.setState({initialPosition: initialRegion})
this.setState({markerPosition: initialRegion})
},
(error) => alert(JSON.stringify(error)))
this.watchID = navigator.geolocation.watchPosition((position) => {
var lat = parseFloat(position.coords.latitude)
var long = parseFloat(position.coords.longitude)
var lastRegion = {
latitude: lat,
longitude: long,
longitudeDelta: LONGITUDE_DELTA,
latitudeDelta: LATITUDE_DELTA
}
this.setState({initialPosition: lastRegion})
this.setState({markerPosition: lastRegion})
})
}
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watchID)
}
onMapLayout = () => {
this.setState({ isMapReady: true });
}
render() {
return (
<View style={styles.containerStyle}>
<MapView style={styles.mapStyle} initialRegion={this.state.initialPosition} onLayout={this.onMapLayout}>
{ this.state.isMapReady &&
<MapView.Marker coordinate={this.state.markerPosition}>
</MapView.Marker>
}
</MapView>
</View>
)
}
}
const styles = {
containerStyle: {
flex:1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'lightblue'
},
mapStyle: {
left: 0,
right: 0,
top: 0,
bottom: 0,
position: 'absolute'
}
}
export default Map;
I have no idea what's going wrong to be honest... would really appreciate some help! Thank you!!
this happens because the map view was not initialized yet.
move the call to within the onMapLoaded overriden event.
within your
#Override
public void onMapReady(GoogleMap googleMap)
add :
googleMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
#Override
public void onMapLoaded() {
//Your code where the exception occurred goes here
}
});
I fixed it! So i tried setting mapStyle's width and height but it wasn't working, changed API key, and it still wasn't showing up, tried adding 'flex:1' to containerStyle but it still didn't work until I passed actual height & width values to the container containing my map!
In you Map Styles you Should Provide Screen Width and Height or Flex :1
mapStyle: {
width : SCREEN_WIDTH | SomeValue ,
height : SCREEN_HEIGHT | SomeValue
}
I fixed it using onMapReady strategy , whenever you render a polyline or markers make sure you MapView is loaded.
Reason :
Once your MapView get ready render your Markers.
const { width, height } = Dimensions.get("window");
class Map extends Component {
constructor(props) {
super(props);
this.state = {
isMapReady: false
};
}
onMapLayout = () => {
this.setState({ isMapReady: true });
};
render(){
return(
<MapView
initialRegion={{
latitude:"your lat" ,
longitude:"your lng" ,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}}
onMapReady={this.onMapLayout}
provider={PROVIDER_GOOGLE}
loadingIndicatorColor="#e21d1d"
ref={map => (this.map = map)}
style={{
width,
height,
}}
loadingEnabled={true}
>
{this.state.isMapReady && <MapView.Marker
key="your key"
identifier="marker"
coordinate={{
latitude: "lat",
longitude: "lng"
}}
flat={true}
anchor={anchor}
image={image}
/>
}
</MapView>
);
}
}
I researched a lot looking for a solution and why this error is happening, but I didn't find any correct answer as to why this happens and neither i tested at a low level to understand what really happens, but with some tests i realized that the component needs an absolute size to be able to receive animations and to be manipulated.
So, in order to keep the size of the map relative to the size of the View (my home page) I created a parent container for MapView that is flexible, fills the available size and provides, through the onLayout property, an absolute size for MapView.
here's an example of how it works:
const [mapViewContainerLayout, setMapViewContainerLayout] = useState<LayoutRectangle>();
<View>
<MapContainer
onLayout={(e: LayoutChangeEvent) => {
setMapViewContainerLayout(e?.nativeEvent?.layout);
}}>
{mapVieContainerLayout && (
<MapView
...
style={{
width: mapViewContainerLayout?.width,
height: mapViewContainerLayout?.height
}}
...
</MapView>
)}
</MapContainer>
</View>
I had same problem, with flex: 1 I was getting error so I set fixed width: and height:. But still this wasn't ideal I really needed flexibility of flex: 1. Finaly I made it work and preserved use of flex by using minHeight: instead of height:
{
minHeight: Dimensions.get("window").height - APPROX_HEIGHT_OF_OTHER_ELEMENTS,
flex: 1,
}
Change Api Key. That only the reason to show functional map.
In Kotlin this seems to have worked:
map.setOnMapLoadedCallback {
map.moveCamera(CameraUpdateFactory.newLatLngBounds(cameraBounds, 0))
}
i had a scrollView as parent component and that was giving issue and wouldnt work unless i gave height, but removing parent scrollView it now works perfectly with flex:1
If you are using RawBottomSheet then you need to have minHeight equal to the Raw bottom sheet in MapView style i.e
<MapView
onPress={e => handleMapMarker(e.nativeEvent.coordinate)}
showsUserLocation={true}
showsIndoorLevelPicker={false}
showsBuildings={false}
ref={mapView}
provider={PROVIDER_GOOGLE}
customMapStyle={mapStyle}
initialRegion={region}
region={region}
onMapReady={handleMap}
loadingEnabled={true}
loadingIndicatorColor="#e21d1d"
// showsMyLocationButton={true}
style={{
flex: 1,
minHeight: height * 0.8,
}}>
{isMapReady && (
<MarkerComp
setMarker={setMarker}
coordinate={{...marker}}
handleMapMarker={handleMapMarker}
/>
)}
</MapView>