visgl / react-map-gl

React friendly API wrapper around MapboxGL JS
http://visgl.github.io/react-map-gl/
Other
7.88k stars 1.35k forks source link

When setting maxZoom and dragging map around markers are moving all over the place [Bug] #2359

Closed paoloviselli closed 4 months ago

paoloviselli commented 8 months ago

Description

Normally my map works totally fine, however as soon as I set a maxZoom value, then zoom in and drag around the map, the markers start going all over the map.

This is my code:

`import React, { useEffect, useState, useCallback } from "react"; import Map, { Layer, Marker, NavigationControl, Source } from "react-map-gl"; import MyLocationIcon from "@mui/icons-material/MyLocation"; import { Box, Typography } from "@mui/material"; import SatelliteAltIcon from "@mui/icons-material/SatelliteAlt";

const VesselAisMap = ({ locations }) => { const [isLoading, setIsLoading] = useState(true); const [initialViewport, setInitialViewport] = useState({}); const [viewport, setViewport] = useState({ latitude: 0, longitude: 0, zoom: 4, bearing: 0, pitch: 40, });

useEffect(() => { setIsLoading(true);

if (locations.length === 0) {
  setIsLoading(false);
  return;
}
//set the latitude and longitude of the last locations as the initial view state
setViewport({
  ...viewport,
  latitude: locations[locations.length - 1].latitude,
  longitude: locations[locations.length - 1].longitude,
});

setInitialViewport({
  latitude: locations[locations.length - 1].latitude,
  longitude: locations[locations.length - 1].longitude,
  zoom: 4,
  bearing: 0,
  pitch: 40,
});

setIsLoading(false);

}, [locations]);

const recenterMap = () => { // Set your desired center latitude and longitude setViewport((prevState) => ({ ...prevState, latitude: initialViewport.latitude, longitude: initialViewport.longitude, zoom: 4, bearing: 0, pitch: 40, transitionDuration: 500, // Optional: add transition effect })); };

// Define the line as a GeoJSON object const routeGeoJSON = { type: "Feature", geometry: { type: "LineString", coordinates: locations.map((loc) => [loc.longitude, loc.latitude]), }, };

return ( <> {locations === null && !isLoading && ( <Box height={320} sx={{ minHeight: 320, maxHeight: 320, marginTop: "20px", borderRadius: "8px", display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#f5f5f5", flexDirection: "column", }}

<SatelliteAltIcon sx={{ fontSize: "120px", color: "#1f677a" }} /> <Typography variant="h6" component="span" sx={{ mt: "32px" }}> LOADING )}

  {locations.length === 0 && !isLoading && (
    <Box
      height={320}
      sx={{
        minHeight: 320,
        maxHeight: 320,
        marginTop: "20px",
        borderRadius: "8px",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: "#f5f5f5",
        flexDirection: "column",
      }}
    >
      <SatelliteAltIcon sx={{ fontSize: "120px", color: "#1f677a" }} />
      <Typography variant="h6" component="span" sx={{ mt: "32px" }}>
        No AIS data available for this vessel
      </Typography>
    </Box>
  )}

  {locations.length !== 0 && (
    <Map
      {...viewport}
      reuseMaps
      initialViewState={viewport}
      mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_TOKEN}
      attributionControl={false}
      style={{
        minHeight: 320,
        maxHeight: 320,
        marginTop: "20px",
        borderRadius: "8px",
      }}
      mapStyle="mapbox://styles/mapbox/streets-v12"
      maxZoom={5}
      minZoom={2.5}
      onViewportChange={(newViewport) => setViewport(newViewport)}
      onMove={(evt) => setViewport(evt.viewState)}
    >
      {/*CONTROL PANEL*/}
      <div style={{ position: "absolute", right: 10, top: 10 }}>
        <NavigationControl />
        <button
          onClick={recenterMap}
          style={{
            display: "flex",
            cursor: "pointer",
            padding: "5px",
            alignItems: "center",
            justifyContent: "center",
            borderRadius: "4px",
            background: "white",
            border: "none",
            boxShadow: "0 2px 4px rgba(0,0,0,0.3)",
            marginTop: "100px",
          }}
        >
          <MyLocationIcon sx={{ fontSize: "18px" }} />
        </button>
      </div>

      {locations.map((location, index) => (
        <>
          {index === 0 && (
            <Marker
              key={index}
              latitude={location.latitude}
              longitude={location.longitude}
            >
              <svg
                width="18"
                height="18"
                viewBox="0 0 18 18"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <circle cx="9" cy="9" r="6" fill="#1F677A" />
                <circle
                  cx="9"
                  cy="9"
                  r="7.5"
                  stroke="#1F677A"
                  stroke-opacity="0.3"
                  stroke-width="3"
                />
              </svg>
            </Marker>
          )}

          {index === locations.length - 1 &&
            location.status !== "At anchor" && (
              <Marker
                key={index}
                latitude={location.latitude}
                longitude={location.longitude}
              >
                <div
                  className="marker-container"
                  style={{
                    transform: `rotate(${location.course}deg)`,
                  }}
                >
                  {/* Pulse effect */}
                  <div className="pulse-container"></div>
                  <svg
                    width="28"
                    height="28"
                    viewBox="0 0 28 28"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                    style={{
                      position: "absolute",
                    }}
                  >
                    <circle
                      cx="14"
                      cy="14"
                      r="13"
                      fill="#1F677A"
                      stroke="white"
                      stroke-width="2"
                    />
                    <g clip-path="url(#clip0_2311_2485)">
                      <path
                        d="M14.4646 5.52102C14.4276 5.42803 14.3635 5.34829 14.2807 5.2921C14.1979 5.23592 14.1001 5.20589 14 5.20589C13.8999 5.20589 13.8021 5.23592 13.7193 5.2921C13.6365 5.34829 13.5724 5.42803 13.5354 5.52102L7.87858 19.6632C7.8409 19.7573 7.83275 19.8607 7.85521 19.9596C7.87766 20.0585 7.92966 20.1482 8.0043 20.2169C8.07894 20.2855 8.1727 20.3299 8.27312 20.344C8.37355 20.3582 8.4759 20.3414 8.56659 20.296L14 17.5793L19.4327 20.2967C19.5235 20.3425 19.626 20.3595 19.7267 20.3454C19.8274 20.3314 19.9214 20.287 19.9962 20.2182C20.0711 20.1494 20.1231 20.0594 20.1455 19.9602C20.1679 19.8611 20.1595 19.7574 20.1214 19.6632L14.4646 5.52102Z"
                        fill="white"
                      />
                    </g>
                    <defs>
                      <clipPath id="clip0_2311_2485">
                        <rect
                          width="15"
                          height="15"
                          fill="white"
                          transform="translate(14 5) rotate(45)"
                        />
                      </clipPath>
                    </defs>
                  </svg>
                </div>
              </Marker>
            )}

          {location.type === "latestPosition" &&
            location.status === "At anchor" && (
              <Marker
                key={index}
                latitude={location.latitude}
                longitude={location.longitude}
              >
                <svg
                  width="28"
                  height="28"
                  viewBox="0 0 28 28"
                  fill="none"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <circle
                    cx="14"
                    cy="14"
                    r="13"
                    fill="#1F677A"
                    stroke="white"
                    stroke-width="2"
                  />
                  <path
                    d="M14 20.6667C13.3222 20.6667 12.6278 20.5445 11.9167 20.3C11.2056 20.0556 10.5611 19.7222 9.98333 19.3C9.40556 18.8778 8.93067 18.3833 8.55867 17.8167C8.18667 17.25 8.00044 16.6445 8 16V14L10.6667 16L9.63333 17.0333C9.95556 17.6 10.4667 18.0889 11.1667 18.5C11.8667 18.9111 12.5889 19.1722 13.3333 19.2833V13.3333H11.3333V12H13.3333V11.2167C12.9444 11.0722 12.6251 10.8305 12.3753 10.4913C12.1256 10.1522 12.0004 9.76623 12 9.33334C12 8.77779 12.1944 8.30557 12.5833 7.91668C12.9722 7.52779 13.4444 7.33334 14 7.33334C14.5556 7.33334 15.0278 7.52779 15.4167 7.91668C15.8056 8.30557 16 8.77779 16 9.33334C16 9.76668 15.8751 10.1529 15.6253 10.492C15.3756 10.8311 15.056 11.0727 14.6667 11.2167V12H16.6667V13.3333H14.6667V19.2833C15.4111 19.1722 16.1333 18.9111 16.8333 18.5C17.5333 18.0889 18.0444 17.6 18.3667 17.0333L17.3333 16L20 14V16C20 16.6445 19.814 17.25 19.442 17.8167C19.07 18.3833 18.5949 18.8778 18.0167 19.3C17.4389 19.7222 16.7944 20.0556 16.0833 20.3C15.3722 20.5445 14.6778 20.6667 14 20.6667ZM14 10C14.1889 10 14.3473 9.93623 14.4753 9.80868C14.6033 9.68112 14.6671 9.52268 14.6667 9.33334C14.6667 9.14445 14.6027 8.98623 14.4747 8.85868C14.3467 8.73112 14.1884 8.66712 14 8.66668C13.8111 8.66668 13.6529 8.73068 13.5253 8.85868C13.3978 8.98668 13.3338 9.1449 13.3333 9.33334C13.3333 9.52223 13.3973 9.68068 13.5253 9.80868C13.6533 9.93668 13.8116 10.0005 14 10Z"
                    fill="white"
                  />
                </svg>
              </Marker>
            )}
        </>
      ))}

      <Source id="route" type="geojson" data={routeGeoJSON}>
        <Layer
          id="route"
          type="line"
          source="route"
          layout={{
            "line-join": "round", // This will smooth the corners
            "line-cap": "round",
          }}
          paint={{
            "line-color": "#00b0a9", // Change the color to red, for example
            "line-width": 5,
            "line-opacity": 0.8, // You can also set the opacity of the line
          }}
        />
      </Source>
    </Map>
  )}
</>

); };

export default VesselAisMap; `

https://github.com/visgl/react-map-gl/assets/46694996/71a36f52-aba0-4650-b3e3-3ec8d3f58f65

Expected Behavior

No response

Steps to Reproduce

Set maxZoom, zoom in from default value, then drag the map around.

Environment

Logs

No response

bhutoria commented 7 months ago

Have you imported the css file in your index.html?

or

import 'mapbox-gl/dist/mapbox-gl.css'; -- in your component file?

kagiura commented 2 months ago

If anyone is still suffering with this even after adding the CSS, try turning off 3D terrain 😔