2

I am trying to use a Leaflet map in my React application but I am running into a strange problem. I tell my map to pan to the user's current geolocation (if they have allowed access to their location) in the whenReady function but when it does that the map glitches out. What makes this weirder is that it only happens when I put my map in a view where I route to from App.js (I want to display it together with a navigation bar). When it is in App.js itself, this doesn't occur and the map displays correctly with my location. I attached pictures to show what I mean:

When the map is in App.js: The map displays correctly with the pin in the center

When the map is in another component: The map is entirely grey with the pin out of the center

Here is my code for the two situations:

Code when the map is in App.js:

import React, {Component} from 'react';
import L from 'leaflet';
import './MapView.css';
import fence from './assets/fence.png';
import pin from './assets/pin.png';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';

class App extends Component {
  constructor() {
    super();
    this.state = {
      markers: [],
      center: [51.505, -0.09]
    };
    this.addMarker = this.addMarker.bind(this)
  }

  fence = L.icon({
    iconUrl: fence,
    iconSize:     [46.3125, 51.5625], // size of the icon
    iconAnchor:   [22, 25], // point of the icon which will correspond to marker's location
    popupAnchor:  [0, -25]
  });
  
  addMarker = (e) => {
    const {markers} = this.state
    markers.push(this.state.center)
    this.setState({markers})
  }

  render() {
    const self = this;
    return (
      <div className="map-container">
        <button onClick={this.addMarker} className="btn">+</button>
        <div className="center"></div>
      <MapContainer 
        className="map"
        minZoom= {8}
        zoom={20} 
        center = {this.state.center}
        whenReady={(map) => {
          if ('geolocation' in navigator) {
            navigator.geolocation.getCurrentPosition(function (location) {
              console.log(location);
              map.target.panTo(new L.LatLng(location.coords.latitude, location.coords.longitude))

              var marker = L.marker([location.coords.latitude, location.coords.longitude], {icon: L.icon({
                iconUrl: pin,
                iconSize:     [24, 38], // size of the icon
                iconAnchor:   [12.5, 40], // point of the icon which will correspond to marker's location
                popupAnchor:  [0, -50]
              })} ).addTo(map.target);
              marker.bindPopup("Your current location")
              marker.openPopup()
            });
          }

          map.target.on("drag", function (e) {
            self.setState({center: map.target.getCenter()})
          });

          map.target.on("zoom", function (e) {
            self.setState({center: map.target.getCenter()})
          })
        }}
        >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
        ></TileLayer>
        {this.state.markers.map((position, idx) => 
          <Marker key={`marker-${idx}`} icon={this.fence} position={position}>
          <Popup>
            <span>Title<br/></span>
          </Popup>
        </Marker>
        )}
      </MapContainer>
      </div>
    );
  }
}

export default App;

Code when the map is in another view, routed from App.js:

//App.js
import React from "react";
import "./App.css";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import MapView from './MapView';
import AddView from './AddView';
import Navbar from './Navbar';

function App() {
  return (
    <div className="App">
      <Router>
      <Navbar/>
        <Switch>
          <Route exact from="/" component={MapView} />
          <Route exact path="/new" component={AddView} />
        </Switch>
      </Router>
    </div>
  );
}

export default App;

//MapView.js
import React, {Component} from 'react';
import L from 'leaflet';
import './MapView.css';
import fence from './assets/fence.png';
import pin from './assets/pin.png';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';

class MapView extends Component {
  constructor() {
    super();
    this.state = {
      markers: [],
      center: [51.505, -0.09]
    };
    this.addMarker = this.addMarker.bind(this)
  }

  fence = L.icon({
    iconUrl: fence,
    iconSize:     [46.3125, 51.5625], // size of the icon
    iconAnchor:   [22, 25], // point of the icon which will correspond to marker's location
    popupAnchor:  [0, -25]
  });
  
  addMarker = (e) => {
    const {markers} = this.state
    markers.push(this.state.center)
    this.setState({markers})
  }

  render() {
    const self = this;
    return (
      <div className="map-container">
        <button onClick={this.addMarker} className="btn">+</button>
        <div className="center"></div>
      <MapContainer 
        id="map"
        className="map"
        minZoom= {8}
        zoom={20} 
        center = {this.state.center}
        whenReady={(map) => {
            if ('geolocation' in navigator) {
              navigator.geolocation.getCurrentPosition(function (location) {
                localStorage.setItem('latitude', location.coords.latitude)
                localStorage.setItem('longitude', location.coords.longitude)
                let latitude= location.coords.latitude;
                let longitude= location.coords.longitude;
                console.log(location);

                map.target.panTo(new L.LatLng(latitude, longitude))
  
                var marker = L.marker([latitude, longitude], {icon: L.icon({
                  iconUrl: pin,
                  iconSize:     [24, 38], 
                  iconAnchor:   [12.5, 40], 
                  popupAnchor:  [0, -50]
                })} ).addTo(map.target);
                marker.bindPopup("Your current location")
                marker.openPopup()
              });
            } else {
                console.log("Ik kom erin bro wtf")
                let latitude = localStorage.getItem('latitude') ?? 51.505
                let longitude = localStorage.getItem('longitude') ?? -0.09

                map.target.panTo(new L.LatLng(latitude, longitude))
            }
  
            map.target.on("drag", function (e) {
              self.setState({center: map.target.getCenter()})
            });
  
            map.target.on("zoom", function (e) {
              self.setState({center: map.target.getCenter()})
            })
          }}
        >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
        ></TileLayer>
        {this.state.markers.map((position, idx) => 
          <Marker key={`marker-${idx}`} icon={this.fence} position={position}>
          <Popup>
            <span>Title<br/></span>
          </Popup>
        </Marker>
        )}
      </MapContainer>
      </div>
    );
  }
}

export default MapView;

//Navbar.js
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import {
    Link
  } from "react-router-dom";


const useStyles = makeStyles((theme) => ({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    title: {
      flexGrow: 1,
    },
    bar: {
        height: '7.5vh',
    }
  }));

function Navbar() {
    const classes = useStyles();

    return (
        <AppBar position="static" className={classes.bar}>
        <Toolbar>
          <IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu">
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" className={classes.title}>
            Work in progress
          </Typography>
          <Link color="inherit" to="/new">Report construction</Link>
          <Button color="inherit">Login</Button>
        </Toolbar>
      </AppBar>
    );
  }
  
  export default Navbar;

I don't have too much knowledge in Leaflet, because this is the first app I make with it, so please take this into consideration if I am confusing some theory behind this. Does anybody know if there is something wrong with my code, my way of thinking, or if there is some Leaflet theory behind this that I am missing? I look forward to hearing any answers.

2 Answers 2

1

You might want to try setting a delay before panning to your location inside the whenReady(). It is possible that the map is not fully rendered when the function starts, this might fix the issue perhaps

setTimeout(() => { this.goToLocation() }, 1000);
1

The reason for this is because you have assigned map zoom to be 20 while the upper limit is 19. You can find more for the reason on this post

All you have to do is set maxZoom and maxNativeZoom props on TileLayer component

<TileLayer
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
     url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
     maxNativeZoom={19}
     maxZoom={22}
 />

Not the answer you're looking for? Browse other questions tagged or ask your own question.