raph
raph

Reputation: 319

"Error using newLatLngBounds(LatLngBounds, int): Map size can't be zero...." using react-native-maps on Android

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!!

Upvotes: 16

Views: 21351

Answers (14)

Shivanand Muddi
Shivanand Muddi

Reputation: 21

Im also facing same error. This error occurs because of the map's layout (width and height) is not yet ready when the newLatLngBounds method is called. Code for display the map and selecting the location

import { View, StyleSheet, Alert, Dimensions } from "react-native";
import MapView, { Marker, PROVIDER_GOOGLE } from "react-native-maps";
import * as Location from "expo-location";
import {
  widthPercentageToDP as wp,
  heightPercentageToDP as hp,
} from "react-native-responsive-screen";
import Button from "../../../../components/Button";

const MapScreen = ({ setLocation, closeMap }) => {
  const [region, setRegion] = useState(null);
  const [selectedLocation, setSelectedLocation] = useState(null);
  const [mapReady, setMapReady] = useState(false);
  const { height } = Dimensions.get("window");

  useEffect(() => {
    (async () => {
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== "granted") {
        Alert.alert(
          "Permission denied",
          "Enable location permissions to use this feature"
        );
        return;
      }

      const currentLocation = await Location.getCurrentPositionAsync({});
      setRegion({
        latitude: currentLocation.coords.latitude,
        longitude: currentLocation.coords.longitude,
        latitudeDelta: 0.01,
        longitudeDelta: 0.01,
      });
    })();
  }, []);

  const handleMapMarker = (coordinate) => {
    setSelectedLocation(coordinate);
  };

  const handleConfirmLocation = async () => {
    if (selectedLocation) {
      const [address] = await Location.reverseGeocodeAsync(selectedLocation);

      if (address) {
        const formattedAddress = `${address.name || ""}, ${
          address.street || ""
        }, ${address.city || ""}, ${address.region || ""}, ${
          address.country || ""
        }`.trim();
        setLocation(formattedAddress);
      } else {
        Alert.alert("Address not found", "Unable to get the full address.");
      }

      closeMap();
    } else {
      Alert.alert(
        "No location selected",
        "Please select a location on the map."
      );
    }
  };

  return (
    <View style={{ height: hp("100%"), width: wp("100%") }}>
      {region && (
        <MapView
          provider={PROVIDER_GOOGLE}
          style={{ flex: 1, minHeight: height * 0.8 }}
          initialRegion={region}
          onPress={(e) => handleMapMarker(e.nativeEvent.coordinate)}
          showsUserLocation={true}
          onMapReady={() => setMapReady(true)}
        >
          {mapReady && selectedLocation && (
            <Marker coordinate={selectedLocation} draggable />
          )}
        </MapView>
      )}

      <View
        style={{ position: "absolute", bottom: hp("10%"), left: 10, right: 10 }}
      >
        <Button
          text={"Confirm Location"}
          onPress={handleConfirmLocation}
          fontSize={16}
          height={hp("5.5%")}
        />
      </View>
    </View>
  );
};

export default MapScreen;

Upvotes: 0

Zain Ali
Zain Ali

Reputation: 66

giving map width and height in numbers solve my problem.

  <MapView
    ref={mapRef}
    provider={Platform.OS === 'android' ? PROVIDER_GOOGLE : PROVIDER_DEFAULT}
    style={{
      ...StyleSheet.absoluteFillObject,
      width: 100,
      height: 100,
    }}
    onMapLoaded={onMapLoaded}
    region={region}
    initialRegion={region}
    // pointerEvents={'none'}
    onRegionChangeComplete={_onRegionChange}
  />

Upvotes: 0

The method isMapReady worked for me! `

<MapView

    style={{ height: 100, width: 120, borderWidth: 1 }}
                zoomEnabled={false}
                scrollEnabled={false}
                onLayout={this.onMapLayout}
                initialRegion={{
                  latitude,
                  longitude,
                  latitudeDelta: 0.04,
                  longitudeDelta: 0.04,
                }}>
                {isMapReady &&
                  <Marker coordinate={{ latitude, longitude }} />
                }
           </MapView>

`

Upvotes: 0

Konstantin Konopko
Konstantin Konopko

Reputation: 5420

Wait until map layout is being ready:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mapView = findViewById(R.id.mapGoogle) as? MapView
        mapView?.onCreate(savedInstanceState)
        mapView?.getMapAsync(this)
}

override fun onMapReady(googleMap: GoogleMap) {
        // wait for map layout ready
        mapView?.post {
            drawRoute(googleMap) //todo make some with your map
        }
}

Upvotes: 0

Ravis Farooq
Ravis Farooq

Reputation: 248

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>

Upvotes: 0

user1462498
user1462498

Reputation: 374

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

Upvotes: 0

Thiago
Thiago

Reputation: 13302

In Kotlin this seems to have worked:

map.setOnMapLoadedCallback {
 map.moveCamera(CameraUpdateFactory.newLatLngBounds(cameraBounds, 0))
}

Upvotes: 0

remo
remo

Reputation: 780

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,
}

Upvotes: 2

Gabriel Felipe
Gabriel Felipe

Reputation: 59

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>

Upvotes: 2

Elad
Elad

Reputation: 1655

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    
                    }
                }); 

Upvotes: 11

Syed Zain Ali
Syed Zain Ali

Reputation: 1226

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>
         );
      }
    }

Upvotes: 2

raph
raph

Reputation: 319

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!

Upvotes: 6

Pawan Soni
Pawan Soni

Reputation: 936

Change Api Key. That only the reason to show functional map.

Upvotes: 0

Syed Zain Ali
Syed Zain Ali

Reputation: 1226

In you Map Styles you Should Provide Screen Width and Height or Flex :1

mapStyle: {
       width : SCREEN_WIDTH | SomeValue ,
       height : SCREEN_HEIGHT | SomeValue 
    }

Upvotes: 5

Related Questions