You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mapbox地图标记位置偏移且缩放时趋近真实位置的问题求助

Mapbox地图标记位置偏移且缩放时趋近真实位置的问题求助

我在用Mapbox开发地图组件时遇到了一个棘手的问题,想请大家帮忙分析一下原因:

  • 我通过props传入的boundaryCoordinates绘制的多边形完全正常,精度很高,和预期位置完全吻合;
  • 但同样作为props传入的标记点(markers)却总是偏离实际位置,而且当我放大地图时,标记点会逐渐向真实位置靠近,不过由于地图有最大缩放限制,没法确认最终是否能完全对齐;
  • 还有个有意思的现象:如果不传入boundaryCoordinates,代码会自动取第一个标记点的坐标,加上偏移量生成一个方形多边形,这时候标记点显示完全正常,说明标记点的经纬度数据本身应该是没问题的。

以下是我的完整组件代码,麻烦大家帮忙看看哪里可能出了问题:

"use client";
import { Button } from "@/components/ui/button";
import { LocateIcon } from "lucide-react";
import mapboxgl from "mapbox-gl";
import { useEffect, useRef, useState } from "react";

mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN!;

interface PropertyMarker {
  id: string;
  latitude: number;
  longitude: number;
}

interface Coordinate {
  longitude: number;
  latitude: number;
}

interface MapboxListingsMapProps {
  markers: PropertyMarker[];
  initialZoom?: number;
  boundaryCoordinates?: Coordinate[];
  height:number
}

export default function MapboxListingsMap({
  markers,
  initialZoom = 12,
  boundaryCoordinates = [],
  height
}: MapboxListingsMapProps) {
  const mapContainerRef = useRef<HTMLDivElement>(null);
  const mapRef = useRef<mapboxgl.Map | null>(null);
  const [zoom, setZoom] = useState(initialZoom);
  const initialCenter = useRef<[number, number] | null>(null);

  useEffect(() => {
    if (!markers.length) return;
    initialCenter.current = [markers[0].longitude, markers[0].latitude];
  }, [markers]);

  useEffect(() => {
    if (!markers.length || !initialCenter.current) return;

    const map = new mapboxgl.Map({
      container: mapContainerRef.current!,
      style: "mapbox://styles/mapbox/light-v9", // Start with a light style
      center: initialCenter.current,
      zoom: initialZoom,
    });

    mapRef.current = map;

    map.on("load", () => {
      // Change map background to rgb(246, 246, 244)
      map.setPaintProperty('background', 'background-color', 'rgb(246, 246, 244)');

      // Add boundary polygon if coordinates are provided
      if (boundaryCoordinates.length > 0) {
        const formattedCoordinates = boundaryCoordinates.map(coord => [
          coord.longitude,
          coord.latitude
        ]);
        if (formattedCoordinates.length > 0 && (formattedCoordinates[0][0] !== formattedCoordinates[formattedCoordinates.length - 1][0] || formattedCoordinates[0][1] !== formattedCoordinates[formattedCoordinates.length - 1][1])) {
          formattedCoordinates.push(formattedCoordinates[0]);
        }
        map.addSource('boundary', {
          'type': 'geojson',
          'data': {
            'type': 'Feature',
            'geometry': {
              'type': 'Polygon',
              'coordinates': [formattedCoordinates]
            },
            'properties': {}
          }
        });
      } else if (initialCenter.current) {
        map.addSource('boundary', {
          'type': 'geojson',
          'data': {
            'type': 'Feature',
            'geometry': {
              'type': 'Polygon',
              'coordinates': [[
                [initialCenter.current[0] - 0.02, initialCenter.current[1] - 0.02],
                [initialCenter.current[0] + 0.02, initialCenter.current[1] - 0.02],
                [initialCenter.current[0] + 0.02, initialCenter.current[1] + 0.02],
                [initialCenter.current[0] - 0.02, initialCenter.current[1] + 0.02],
                [initialCenter.current[0] - 0.02, initialCenter.current[1] - 0.02]
              ]]
            },
            'properties': {}
          }
        });
      }

      if (map.getSource('boundary')) {
        map.addLayer({
          'id': 'boundary-line',
          'type': 'line',
          'source': 'boundary',
          'layout': {},
          'paint': {
            'line-color': '#EB5C60',
            'line-width': 1
          }
        });
      }

      markers.forEach(({ id, latitude, longitude }) => {
        const el = document.createElement("div");
        el.className = "marker";
        el.style.width = "20px";
        el.style.height = "20px";
        el.style.backgroundColor = "#2f80ed";
        el.style.borderRadius = "50%";
        el.style.boxShadow = "0 0 0 2px #e0e0e0";
        new mapboxgl.Marker(el).setLngLat([longitude, latitude]).addTo(map);
      });

      if (markers.length > 1) {
        const bounds = new mapboxgl.LngLatBounds();
        markers.forEach(({ longitude, latitude }) => {
          bounds.extend([longitude, latitude]);
        });
        map.fitBounds(bounds, { padding: 50, maxZoom: 14 });
      }
    });

    map.on("zoomend", () => {
      setZoom(map.getZoom());
    });

    return () => map.remove();
  }, [markers, initialZoom]);

  const handleZoomIn = () => {
    if (mapRef.current) {
      mapRef.current.zoomTo(mapRef.current.getZoom() + 1);
    }
  };

  const handleZoomOut = () => {
    if (mapRef.current) {
      mapRef.current.zoomTo(mapRef.current.getZoom() - 1);
    }
  };

  const handleResetView = () => {
    if (mapRef.current && initialCenter.current) {
      mapRef.current.flyTo({
        center: initialCenter.current,
        zoom: initialZoom,
      });
    }
  };

  return (
    <div className={`relative w-full overflow-hidden`} style={{ height: `${height}px` }} >
      <div ref={mapContainerRef} className="absolute inset-0 z-0" />
      <div className="absolute left-4 bottom-4 z-10 bg-[#f9f9f9]">
        {/* 按钮部分代码截断不影响核心问题排查 */}
      </div>
    </div>
  );
}

我已经检查过标记点的经纬度格式,确认是[longitude, latitude]的顺序,和Mapbox要求的一致。多边形在两种场景下都能正确渲染,唯独传入外部边界时标记点偏移,实在想不通问题出在哪,求各位大佬指点!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:49:52