Google Maps Ground Overlay malfunctioning when zooming, moving or panning

48 views Asked by At

I created a Ground Overlay on top of the UK with some markers and reestrictions but when i zoom, move or select a marker that pans to the marker positions the overlay breaks, the image it's quite large but i managed to reduce it's size by using a webp file instead of a png, anyways this keeps happening in both mobile and desktop, you can check it here

https://ots-frontend.vercel.app/tickets

Should i use another type of overlay to prevent it from breaking? I'm using @vis.gl/react-google-maps library to build this in Next.js 14. Here's my code (it's not fully completed so bear with me)

"use client"

import { useEffect, useState, useMemo, useCallback, Fragment } from "react";
import Image from "next/image";
import Link from "next/link";
import {
  useApiIsLoaded,
  Map,
  useMap,
  useMapsLibrary,
  Marker,
  useMarkerRef,
} from "@vis.gl/react-google-maps";
import windowLogo from "../../public/assets/logo-badge.svg";
import CustomPopup from "./CustomPopup";
import { CgClose } from "react-icons/cg";

const Maps = ({ markersList }) => {

  const overlayData = useMemo(() => ({
    mobile: {
      imageUrl: "/assets/map-mobile.webp",
      sw: { lat: 48.15101962663997, lng: -14.624391892254629 }, // SW coordinates A
      ne: { lat: 63.41423162171008, lng: 7.880460478192122 }, // NE coordinates A
    },
    desktop: {
      imageUrl: "/assets/map-desktop.webp",
      sw: { lat: 48.54530012368014 , lng: -25.21521220475463 }, // SW coordinates B
      ne: { lat: 63.433892030361925, lng:  17.88900540006712 }, // NE coordinates B
    },
  }), []);

  const [currentOverlay, setCurrentOverlay] = useState(overlayData.desktop)
  const [overlayLoaded, setOverlayLoaded] = useState(false);
  const [activeMarkerId, setActiveMarkerId] = useState(null);
  const [isTransitioning, setIsTransitioning] = useState(false);

  const coreLibrary = useMapsLibrary("core");
  const mapsLibrary = useMapsLibrary("maps");
  const map = useMap("MapOTS");
  const apiIsLoaded = useApiIsLoaded();

  const [mapCenter, setMapCenter] = useState({ lat: 55.97, lng: -3.699966 });
  const [mapZoom, setMapZoom] = useState(6);
  const [markerIconScale, setMarkerIconScale] = useState(6);
  const southWest = { lat: 49.68145071046583, lng: -11.130739548504629 };
  const northEast = { lat: 63.335455022149226, lng: 3.2662026656921217 };
 
 

  const restrictions = useMemo(() => ({
    latLngBounds: {
      north: currentOverlay.ne.lat,
      south: currentOverlay.sw.lat,
      west: currentOverlay.sw.lng,
      east: currentOverlay.ne.lng,
    },
    strictBounds: true,
  }), [currentOverlay]);


  const breakpoints = useMemo(() => ([
    {
      min: 0,
      max: 369,
      action: () => {
        setMapCenter({ lat: 54.75, lng: -2.199966 });
        setMapZoom(4);
        setMarkerIconScale(4);
      },
    },
    {
      min: 370,
      max: 379,
      action: () => {
        setMapCenter({ lat: 55, lng: -2.499966 });
        setMapZoom(4);
        setMarkerIconScale(4);
      },
    },
    {
      min: 380,
      max: 389,
      action: () => {
        setMapCenter({ lat: 56.35, lng: -3.499966 });
        setMapZoom(5.8);
        setMarkerIconScale(4);
      },
    },
    {
      min: 390,
      max: 399,
      action: () => {
        setMapCenter({ lat: 56.15, lng: -2.899966 });
        setMapZoom(5.8);
        setMarkerIconScale(5);
      },
    },
    {
      min: 400,
      max: 419,
      action: () => {
        setMapCenter({ lat: 55.7, lng: -2.499966 });
        setMapZoom(6);
        setMarkerIconScale(5);
      },
    },
    {
      min: 420,
      max: 559,
      action: () => {
        setMapCenter({ lat: 55.9, lng: -2.499966 });
        setMapZoom(6);
        setMarkerIconScale(5);
      },
    },
    {
      min: 600,
      max: 767,
      action: () => {
        setMapCenter({ lat: 56.3, lng: -3.699966 });
        setMapZoom(6);
        setMarkerIconScale(5);
      },
    },
    {
      min: 768,
      max: 819,
      action: () => {
        setMapCenter({ lat: 55.3, lng: -3.999966 });
        setMapZoom(6.3);
      },
    },
    {
      min: 820,
      max: 1023,
      action: () => {
        setMapCenter({ lat: 55, lng: -3.699966 });
        setMapZoom(6.6);
      },
    },
    {
      min: 1024,
      max: 1279,
      action: () => {
        setMapCenter({ lat: 55, lng: -3.699966 });
        setMapZoom(6.8);
        setMarkerIconScale(8);
      },
    },
    {
      min: 1280,
      max: 1365,
      action: () => {
        setMapCenter({ lat: 54.88, lng: -3.799966 });
        setMapZoom(5.9);
        setMarkerIconScale(4);
      },
    },
    {
      min: 1366,
      max: 1439,
      action: () => {
        setMapCenter({ lat: 54.9, lng: -3.799966 });
        setMapZoom(6);
        setMarkerIconScale(5);
      },
    },
    {
      min: 1440,
      max: 1919,
      action: () => {
        setMapCenter({ lat: 54.93, lng: -3.799966 });
        setMapZoom(6.2);
        setMarkerIconScale(5);
      },
    },
    {
      min: 1920,
      max: 1999,
      action: () => {
        setMapCenter({ lat: 54.8, lng: -3.799966 });
        setMapZoom(6.5);
      },
    },
    {
      min: 2000,
      max: 2999,
      action: () => {
        setMapCenter({ lat: 54.85, lng: -3.799966 });
        setMapZoom(6.9);
        setMarkerIconScale(8);
      },
    },
    {
      min: 3000,
      max: Infinity,
      action: () => {
        setMapCenter({ lat: 54.85, lng: -3.799966 });
        setMapZoom(7.5);
        setMarkerIconScale(13);
      },
    },

    // Add more breakpoints as needed
  ]), []);

  const updateMapSettings = useCallback(() => {
    const width = window.innerWidth;
    const breakpoint = breakpoints.find(
      (bp) => width >= bp.min && width <= bp.max,
    );
    if (breakpoint) breakpoint.action();
  }, []);



  // Set up resize listener and initial settings
  useEffect(() => {
    const checkDeviceAndAdjustMap = () => {
      const width = window.innerWidth;
      const isMobile = window.matchMedia("(max-width: 1280px)").matches;
  
      // Determine current overlay based on device type
      const currentOverlay = isMobile ? overlayData.mobile : overlayData.desktop;
      setCurrentOverlay(currentOverlay); // Assuming setCurrentOverlay updates the overlay state
  
      // Find and apply the appropriate breakpoint action
      const breakpoint = breakpoints.find(bp => width >= bp.min && width <= bp.max);
      if (breakpoint) breakpoint.action();
    };
  
    checkDeviceAndAdjustMap(); // Call once on mount
    window.addEventListener("resize", checkDeviceAndAdjustMap); // Adjust on resize
  
    // Cleanup
    return () => window.removeEventListener("resize", checkDeviceAndAdjustMap);
  }, []);


  //Overlay useEffect
  useEffect(() => {
    if (!apiIsLoaded || !map || !coreLibrary || !mapsLibrary) return;

    const southWestLatLng = new coreLibrary.LatLng(
      currentOverlay.sw.lat,
      currentOverlay.sw.lng,
    );
    const northEastLatLng = new coreLibrary.LatLng(
      currentOverlay.ne.lat,
      currentOverlay.ne.lng,
    );
    const bounds = new coreLibrary.LatLngBounds(
      southWestLatLng,
      northEastLatLng,
    );


    const overlayOptions = { clickable: false };
    console.log("Bounds", southWestLatLng, northEastLatLng);

    const overlay = new mapsLibrary.GroundOverlay(
      currentOverlay.imageUrl,
      bounds,
      overlayOptions,
    );
    overlay.setMap(map);

    overlay.setOpacity(1);
    setOverlayLoaded(true);
  }, [apiIsLoaded, map, coreLibrary, mapsLibrary, southWest, northEast, currentOverlay]);

  const handleMarkerClick = (id, markerPosition) => {
    if (activeMarkerId && activeMarkerId !== id) {
      setIsTransitioning(true);
      setActiveMarkerId(id);
      setTimeout(() => map.panTo(markerPosition), 200)
    } else {
      setIsTransitioning(false);
      setActiveMarkerId(id);
      map.panTo(markerPosition)
    }
  };

  const createPopupContent = useCallback((markerData) => (
    <div className="popup-bubble">
      <div className="flex items-center justify-center px-2 border-r-3 border-dashed border-[#354557]">
        <Image
          src={windowLogo}
          height={50}
          width={50}
          alt="Old Time Sailors Tickets Logo"
          className="md:w-[70px]"
        />
      </div>

      <div className="flex flex-col mt-2.5 md1:mt-3 mb-2 px-2 md1:px-4 items-center gap-1.5 md1:gap-3 w-full">
        <button
          className="absolute top-0 right-0 pt-0.5 pr-0.5"
          onClick={() => setActiveMarkerId(null)}
        >
          <CgClose className=" text-[15px] md1:text-[20px] text-[#232f3f] " />
        </button>

        <ul className="self-start -space-y-1">
          <li className="text-xl md:text-3xl text-lightRed font-titles font-medium flex items-end">
            event:
            <p className="text-[19px] md:text-[28px] text-darkBlue font-txt pl-1 whitespace-nowrap">
              {markerData.event}
            </p>
          </li>
          <li className="text-xl md:text-3xl text-lightRed font-titles font-medium flex items-end">
            location:
            <p className="text-[19px] md:text-[28px] text-darkBlue font-txt pl-1 whitespace-nowrap">
              {markerData.location}
            </p>
          </li>
          <li className="text-xl md:text-3xl text-lightRed font-titles font-medium flex items-end">
            date:
            <p className="text-[19px] md:text-[28px] text-darkBlue font-txt pl-1 whitespace-nowrap">
              {markerData.date}
            </p>
          </li>
        </ul>
        <Link
          className="octagon-tickets flex items-center justify-center bg-darkBlue"
          href={markerData.ticketsURL} target="_blank"
        >
          <p className="text-center text-3xl md:text-[42px] font-titles text-lightRed">
            tickets
          </p>
        </Link>
      </div>
    </div>
  ),[]);

  return (
    <div className="h-dvh">
      <Map
        zoom={mapZoom}
        center={mapCenter}
        gestureHandling={"greedy"}
        disableDefaultUI={true}
        id={"MapOTS"}
        restriction={restrictions}
      >
        {coreLibrary &&
          markersList.map((m) => (
            <Fragment key={m.id}>
              <Marker
                position={m.markerPosition}
                onClick={() => handleMarkerClick(m.id, m.markerPosition)}
                icon={{
                  path: coreLibrary.SymbolPath.CIRCLE,
                  fillColor: "#dd3254",
                  fillOpacity: 1.0,
                  scale: markerIconScale,
                  strokeColor: "#dd3254",
                  strokeWeight: 0.5,
                }}
              />

              <CustomPopup
                position={m.markerPosition}
                isVisible={activeMarkerId === m.id}
                isTransitioning={isTransitioning}
              >
                {createPopupContent(m)}
              </CustomPopup>
            </Fragment>
          ))}
      </Map>
    </div>
  );
};

export default Maps;
0

There are 0 answers