currently appears with the Leaflet map always the error message:
Invalid LatLng object: (undefined, undefined)
This is due to the fact that my variables are not yet available when the map is retrieved.
My code:
import React from "react";
import { TileLayer } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
import StyledMapContainer from "./styled.js";
import { Marker, Popup } from "react-leaflet";
import MarkerIcon from "../mapmarker/index.jsx";
const Map = ({ data }) => {
console.log(data?.latitude);
const position = [data?.latitude, data?.longitude];
return (
<StyledMapContainer
watch
enableHighAccuracy
zoomControl
center={position}
zoom={[13]}
scrollWheelZoom={true}
>
<TileLayer
url="https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}#2x?access_token="
zoomControl={true}
/>
<Marker position={position} icon={MarkerIcon}>
<Popup>The Ship is here!</Popup>
</Marker>
</StyledMapContainer>
);
};
export default Map;
Now I want to build something so that first a default value is taken and then, once latitude and longitude are available, they are exchanged
I have tried it with:
if (position === "undefined") {
const positon = [37.84151, -6.041185];
}
Unfortunately, this does not work. Do any of you have an idea? Thanks for help!
position may never be equal to "undefined" as this is s a string.
what you want is,
if (position === undefined) {
const positon = [39.86101, -0.069185];
}
But this is still tricky because position can be null.
so i will do this instead.
if (!position) {
const positon = [39.86101, -0.069185];
}
Read more about falsy values of Javascript here.
But in the context of your case, here is how the full code can look like.
import React from "react";
import { TileLayer } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
import StyledMapContainer from "./styled.js";
import { Marker, Popup } from "react-leaflet";
import MarkerIcon from "../mapmarker/index.jsx";
const Map = ({ data }) => {
let position = [39.86101, -0.069185];
if (data?.latitude && data?.longitude) {
const lat = parseFloat(data?.latitude);
const lng = parseFloat(data?.longitude);
if (Number.isFinite(lat) && Number.isFinite(lng)) {
position = [lat, lng];
}
}
return (
<StyledMapContainer
watch
enableHighAccuracy
zoomControl
center={position}
zoom={[13]}
scrollWheelZoom={true}
>
<TileLayer
url="https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}#2x?access_token="
zoomControl={true}
/>
<Marker position={position} icon={MarkerIcon}>
<Popup>The Ship is here!</Popup>
</Marker>
</StyledMapContainer>
);
};
export default Map;
Related
Anyone know why I am getting the type error below? I am following along this tutorial (about 59 mins in): https://www.youtube.com/watch?v=G5mmrBkCaP8
Here is my code:
import React, { useState } from 'react';
import ReactMapGL from 'react-map-gl';
import { getCenter } from 'geolib';
function Map({ searchResults }) {
const coordinates = searchResults.map((result) => ({
longitude: result.long,
latitude: result.lat,
}));
// The latitude and longitude of the center of the coordinates
const center = getCenter(coordinates);
const [viewport, setViewport] = useState({
width: "100%",
height: "100%",
latitude: center.latitude,
longitude: center.longitude,
zoom: 10,
});
return (
<ReactMapGL
mapStyle='mapbox://styles/shesonanadventure/cl063x0yl000r15n4frjpm9in'
mapboxAccessToken={process.env.mapbox_key}
{...viewport}
onViewportChange={(nextViewport) => setViewport(nextViewport)}
></ReactMapGL>
);
}
export default Map;
SearchResults is undefined.
Do searchResults && searchResults.map
Also make sure you’re passing the prop
I have the following map.jsx file in my react app, which displays a map on screen. I am trying to add a marker to this map (in a separate component called 'MyGreatPlace') which changes location every 2 seconds. It should just update the marker rather than refreshing the whole map, however i am getting the following error:
Uncaught TypeError: setState is not a function
Below is my code:
import GoogleMapReact from 'google-map-react';
import './map.css';
import MyGreatPlace from './my_great_place.jsx';
import React, { useEffect, useRef, useState, setState } from 'react';
const Map = ({ location, zoomLevel, markerLat, markerLong }) => {
useEffect(() => {
const interval = setInterval(() => {
changeMarkerLatitude();
}, 2000);
return () => clearInterval(interval);
}, []);
const changeMarkerLatitude = () => {
setState({
markerLat: markerLat + 50,
});
};
return (
<div className='map'>
<div className='google-map'>
<GoogleMapReact
bootstrapURLKeys={{ key: 'KeyID' }}
defaultCenter={location}
defaultZoom={zoomLevel}>
<MyGreatPlace lat={markerLat} lng={markerLong} text={'A'} />
</GoogleMapReact>
</div>
</div>
);
};
export default Map;
Does anyone know how i can fix this error, or is there an alternative way of updating the marker location?
That's not how you use the state objects. useState is for functional components, and you invoke it by providing two values. First, the reference to the actual value, and second - the reference to the setter. So const [stateVal, setStateVal] = useState() gives you an undefined reference to a state object, and a reference to a function for updating it. You NEVER mutate the state directly (e.g. stateVal = newVal). You ALWAYS use the setter to mutate the state (which triggers a rerender). You can always initialize the value by passing a value into the useState() call. Like this: setStateVal(newVal)
import GoogleMapReact from 'google-map-react';
import './map.css';
import MyGreatPlace from './my_great_place.jsx';
import React, { useEffect, useRef, useState, setState } from 'react';
const Map = ({ location, zoomLevel, markerLat, markerLong }) => {
const [markerLatVal, setMarkerLatVal] = useState(markerLat) // You can put value in here to initialize
useEffect(() => {
const interval = setInterval(() => {
changeMarkerLatitude();
}, 2000);
return () => clearInterval(interval);
}, []);
const changeMarkerLatitude = () => {
// 'prev' gives you access to the previous state value
setMarkerLatVal(prev => prev + 50);
};
return (
<div className='map'>
<div className='google-map'>
<GoogleMapReact
bootstrapURLKeys={{ key: 'KeyID' }}
defaultCenter={location}
defaultZoom={zoomLevel}>
<MyGreatPlace lat={markerLat} lng={markerLong} text={'A'} />
</GoogleMapReact>
</div>
</div>
);
};
export default Map;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I am trying to implement a searchbox feature in my react app. But getting this error "Attempted import error: 'MapControl' is not exported from 'react-leaflet'" in the new version of react-leaflet
import { MapContainer, TileLayer, Polygon, Marker, Popup } from 'react-leaflet';
import "./index.css";
// Cordinates of Marcillac
const center = [45.269169177925754, -0.5231516014256281]
const purpleOptions = { color: 'white' }
class MapWrapper extends React.Component {
render() {
return (
<div id="mapid">
<MapContainer center={center} zoom={13} scrollWheelZoom={true}>
<TileLayer
attribution='© OpenStreetMap © CartoDB'
url='https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png'
/>
</MapContainer>
</div>
)
}
}
export default MapWrapper;
The implementation given here https://stackoverflow.com/questions/48290555/react-leaflet-search-box-implementation doesnt work as MapControl is depricted.
Tried 2nd solution as well.
import { Map, useLeaflet } from 'react-leaflet'
import { OpenStreetMapProvider, GeoSearchControl } from 'leaflet-geosearch'
// make new leaflet element
const Search = (props) => {
const { map } = useLeaflet() // access to leaflet map
const { provider } = props
useEffect(() => {
const searchControl = new GeoSearchControl({
provider,
})
map.addControl(searchControl) // this is how you add a control in vanilla leaflet
return () => map.removeControl(searchControl)
}, [props])
return null // don't want anything to show up from this comp
}
export default function Map() {
return (
<Map {...otherProps}>
{...otherChildren}
<Search provider={new OpenStreetMapProvider()} />
</Map>
)
}
Here I get map.addControl is not defined
Your approach is correct. You have just confused react-leaflet versions.
The way you are doing it would be correct in react-leaflet version 2.x
For react-leaflet v.3.x your custom comp should look like this:
function LeafletgeoSearch() {
const map = useMap(); //here use useMap hook
useEffect(() => {
const provider = new OpenStreetMapProvider();
const searchControl = new GeoSearchControl({
provider,
marker: {
icon
}
});
map.addControl(searchControl);
return () => map.removeControl(searchControl)
}, []);
return null;
}
You can take the map reference from useMap hook instead of useLeaflet.
Demo
I have seen solutions where they use the Map component, but I read that this has been updated to the MapContainer which does not have an onClick Method. Right now, my code (below) allows me to add a marker when I click anywhere on the map. As my title states, how would I store the new marker in some kind of usable variable. My end goal is to store new markers in MongoDB. Thanks in advance.
import React, { Component } from 'react';
import {
MapContainer,
TileLayer,
MapConsumer,
} from "react-leaflet";
import Leaflet from "leaflet";
import { connect } from 'react-redux';
import { Icon } from "../Leaflet/Configurations";
import NavBar from '../components/NavBar';
import Footer from '../components/Footer';
import { registerHouse } from '../actions/houseActions';
import { clearErrors } from "../actions/errorActions";
import "leaflet/dist/leaflet.css";
class MyMap extends Component{
constructor(props){
super(props);
this.state = {
markers: [[40.7, -74]],
data: []
};
this.addMarker = this.addMarker.bind(this);
}
render(){
return(
<div>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossOrigin=""/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossOrigin=""></script>
<NavBar/>
{/* <MapContainer className="Map" center={{ lat: 40.7 , lng: -74 }} zoom={15} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<MapConsumer>
{(map) => {
console.log("map center:", map.getCenter());
map.on("click", function (e) {
const { lat, lng } = e.latlng;
Leaflet.marker([lat, lng], { Icon }).addTo(map);
let tmp = this.state.data;
tmp.append([lat,lng]);
this.setState({data:tmp});
});
return null;
}}
</MapConsumer>
</MapContainer> */}
<Footer/>
</div>
)
}
}
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated,
error: state.error
});
export default connect(mapStateToProps, { registerHouse, clearErrors })(MyMap);
I am not sure this part of your code works:
let tmp = this.state.data;
tmp.append([lat,lng]);
this.setState({data:tmp});
why? Because this.state is undefined inside map.on('click) block scope. It loses its reference there.
So what you could do is create a custom child component of MapContainer and take advantage of a concept in react called raising an event from the child to the parent component. The parent will send an event to the child and the latter calls that function when something happens.
function MyComponent({ saveMarkers }) {
const map = useMapEvents({
click: (e) => {
const { lat, lng } = e.latlng;
L.marker([lat, lng], { icon }).addTo(map);
saveMarkers([lat, lng]);
}
});
return null;
}
and in your MyMap comp define the event
saveMarkers = (newMarkerCoords) => {
const data = [...this.state.data, newMarkerCoords];
this.setState((prevState) => ({ ...prevState, data }));
};
which will be passed as a prop to the custom comp:
<MapContainer
...
<MyComponent saveMarkers={this.saveMarkers} />
</MapContainer>
Demo
Im using google-maps-react, and it works pretty good, but i just cant understand how can i work with Google Maps API's methods inside my component. Now i need to getBounds of rendered map, but cant find any way to do this. Here is the code, any help would be appreciated.
import React from 'react';
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
const GOOGLE_MAPS_JS_API_KEY='AIzaSyB6whuBhj_notrealkey';
class GoogleMap extends React.Component {
constructor() {
this.state = {
zoom: 13
}
this.onMapClicked = this.onMapClicked.bind(this);
this.test = this.test.bind(this);
}
onMapClicked (props) {
if (this.state.showingInfoWindow) {
this.setState({
showingInfoWindow: false,
activeMarker: null
})
}
}
test(google) {
// Here i tried a lot of ways to get coords somehow
console.log(google.maps.Map.getBounds())
}
render() {
const {google} = this.props;
if (!this.props.loaded) {
return <div>Loading...</div>
}
return (
<Map className='google-map'
google={google}
onClick={this.onMapClicked}
zoom={this.state.zoom}
onReady={() => this.test(google)}
>
</Map>
);
}
}
export default GoogleApiWrapper({
apiKey: (GOOGLE_MAPS_JS_API_KEY)
})(GoogleMap);
Google Maps Api v 3.30.4
You could try and adapt your requirements to the following example here.
From what i can see a reference is returned using the onReady prop.
For example :
import React from 'react';
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
const GOOGLE_MAPS_JS_API_KEY='AIzaSyB6whuBhj_notrealkey';
class GoogleMap extends React.Component {
constructor() {
this.state = {
zoom: 13
}
this.onMapClicked = this.onMapClicked.bind(this);
this.handleMapMount = this.handleMapMount.bind(this);
}
onMapClicked (props) {
if (this.state.showingInfoWindow) {
this.setState({
showingInfoWindow: false,
activeMarker: null
})
}
}
handleMapMount(mapProps, map) {
this.map = map;
//log map bounds
console.log(this.map.getBounds());
}
render() {
const {google} = this.props;
if (!this.props.loaded) {
return <div>Loading...</div>
}
return (
<Map className='google-map'
google={google}
onClick={this.onMapClicked}
zoom={this.state.zoom}
onReady={this.handleMapMount}
>
</Map>
);
}
}
export default GoogleApiWrapper({
apiKey: (GOOGLE_MAPS_JS_API_KEY)
})(GoogleMap);