当前位置: 首页 > news >正文

react+Mapbox GL实现标记地点、区域的功能

准备工作

首先,确保你已经:

  1. 注册了 Mapbox 账号并获取了访问令牌(access token)

  2. 创建了 React 项目

安装必要的依赖:

npm install mapbox-gl react-map-gl
# 或者
yarn add mapbox-gl react-map-gl

基础地图组件

首先创建一个基础地图组件:

import React, { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';// 设置你的 Mapbox 访问令牌
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';const MapboxMap = () => {const mapContainer = useRef(null);const map = useRef(null);const [lng, setLng] = useState(-70.9);const [lat, setLat] = useState(42.35);const [zoom, setZoom] = useState(9);useEffect(() => {if (map.current) return; // 如果地图已经初始化,则不再重复初始化map.current = new mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v11',center: [lng, lat],zoom: zoom});map.current.on('move', () => {setLng(map.current.getCenter().lng.toFixed(4));setLat(map.current.getCenter().lat.toFixed(4));setZoom(map.current.getZoom().toFixed(2));});}, []);return (<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}</div><div ref={mapContainer} className="map-container" /></div>);
};export default MapboxMap;

添加点标记

要在地图上添加点标记,可以使用 mapboxgl.Marker

useEffect(() => {if (!map.current) return;// 添加一个点标记new mapboxgl.Marker().setLngLat([-70.9, 42.35]).addTo(map.current);// 可以添加多个标记const locations = [{ lng: -70.92, lat: 42.36, title: '地点1' },{ lng: -70.88, lat: 42.34, title: '地点2' },];locations.forEach(loc => {new mapboxgl.Marker().setLngLat([loc.lng, loc.lat]).setPopup(new mapboxgl.Popup().setHTML(`<h3>${loc.title}</h3>`)).addTo(map.current);});
}, []);

绘制区域(多边形)

要绘制多边形区域,我们需要使用 GeoJSON 数据:

useEffect(() => {if (!map.current) return;// 等待地图加载完成map.current.on('load', () => {// 添加一个多边形区域map.current.addLayer({id: 'polygon',type: 'fill',source: {type: 'geojson',data: {type: 'Feature',geometry: {type: 'Polygon',coordinates: [[[-70.92, 42.36],[-70.88, 42.36],[-70.88, 42.34],[-70.92, 42.34],[-70.92, 42.36]]]},properties: {}}},paint: {'fill-color': '#088','fill-opacity': 0.4,'fill-outline-color': '#000'}});// 添加可交互的多边形map.current.addLayer({id: 'interactive-polygon',type: 'fill',source: {type: 'geojson',data: {type: 'Feature',geometry: {type: 'Polygon',coordinates: [[[-70.95, 42.38],[-70.90, 42.38],[-70.90, 42.33],[-70.95, 42.33],[-70.95, 42.38]]]},properties: {name: '可交互区域'}}},paint: {'fill-color': '#800','fill-opacity': 0.4,'fill-outline-color': '#000'}});// 添加点击事件map.current.on('click', 'interactive-polygon', (e) => {new mapboxgl.Popup().setLngLat(e.lngLat).setHTML(`<h3>${e.features[0].properties.name}</h3>`).addTo(map.current);});// 鼠标悬停效果map.current.on('mouseenter', 'interactive-polygon', () => {map.current.getCanvas().style.cursor = 'pointer';});map.current.on('mouseleave', 'interactive-polygon', () => {map.current.getCanvas().style.cursor = '';});});
}, []);

完整示例

下面是一个完整的组件,结合了标记地点和绘制区域的功能:

import React, { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';const MapWithMarkersAndPolygons = () => {const mapContainer = useRef(null);const map = useRef(null);const [lng, setLng] = useState(-70.9);const [lat, setLat] = useState(42.35);const [zoom, setZoom] = useState(9);useEffect(() => {if (map.current) return;map.current = new mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v11',center: [lng, lat],zoom: zoom});map.current.on('move', () => {setLng(map.current.getCenter().lng.toFixed(4));setLat(map.current.getCenter().lat.toFixed(4));setZoom(map.current.getZoom().toFixed(2));});// 添加标记map.current.on('load', () => {// 点标记const locations = [{ lng: -70.92, lat: 42.36, title: '地点1' },{ lng: -70.88, lat: 42.34, title: '地点2' },{ lng: -70.95, lat: 42.35, title: '地点3' }];locations.forEach(loc => {new mapboxgl.Marker().setLngLat([loc.lng, loc.lat]).setPopup(new mapboxgl.Popup().setHTML(`<h3>${loc.title}</h3>`)).addTo(map.current);});// 多边形区域map.current.addLayer({id: 'polygon',type: 'fill',source: {type: 'geojson',data: {type: 'Feature',geometry: {type: 'Polygon',coordinates: [[[-70.92, 42.36],[-70.88, 42.36],[-70.88, 42.34],[-70.92, 42.34],[-70.92, 42.36]]]},properties: {description: '静态区域'}}},paint: {'fill-color': '#088','fill-opacity': 0.4,'fill-outline-color': '#000'}});// 可交互多边形map.current.addLayer({id: 'interactive-polygon',type: 'fill',source: {type: 'geojson',data: {type: 'Feature',geometry: {type: 'Polygon',coordinates: [[[-70.95, 42.38],[-70.90, 42.38],[-70.90, 42.33],[-70.95, 42.33],[-70.95, 42.38]]]},properties: {name: '可交互区域',description: '点击我可以看到更多信息'}}},paint: {'fill-color': '#800','fill-opacity': 0.4,'fill-outline-color': '#000'}});// 添加点击事件map.current.on('click', 'interactive-polygon', (e) => {new mapboxgl.Popup().setLngLat(e.lngLat).setHTML(`<h3>${e.features[0].properties.name}</h3><p>${e.features[0].properties.description}</p>`).addTo(map.current);});// 鼠标悬停效果map.current.on('mouseenter', 'interactive-polygon', () => {map.current.getCanvas().style.cursor = 'pointer';});map.current.on('mouseleave', 'interactive-polygon', () => {map.current.getCanvas().style.cursor = '';});});}, []);return (<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}</div><div ref={mapContainer} className="map-container" /></div>);
};export default MapWithMarkersAndPolygons;

样式

添加一些基本样式到你的 CSS 文件中:

.map-container {position: absolute;top: 0;bottom: 0;left: 0;right: 0;
}.sidebar {position: absolute;top: 0;left: 0;margin: 12px;background-color: #404040;color: #ffffff;z-index: 1;padding: 6px;font-weight: bold;
}

使用 react-map-gl (官方 React 封装)

如果你更喜欢使用官方 React 封装,可以这样实现:

import React, { useState, useCallback } from 'react';
import ReactMapGL, { Marker, Popup, Source, Layer } from 'react-map-gl';const MapWithReactMapGL = () => {const [viewport, setViewport] = useState({latitude: 42.35,longitude: -70.9,zoom: 9,width: '100%',height: '100%'});const [selectedPoint, setSelectedPoint] = useState(null);const [selectedPolygon, setSelectedPolygon] = useState(null);const points = [{ id: 1, longitude: -70.92, latitude: 42.36, name: '地点1' },{ id: 2, longitude: -70.88, latitude: 42.34, name: '地点2' },];const polygonData = {type: 'Feature',geometry: {type: 'Polygon',coordinates: [[[-70.92, 42.36],[-70.88, 42.36],[-70.88, 42.34],[-70.92, 42.34],[-70.92, 42.36]]]},properties: {}};const handlePolygonClick = useCallback((event) => {setSelectedPolygon({lngLat: event.lngLat,feature: event.features[0]});}, []);return (<ReactMapGL{...viewport}mapboxApiAccessToken="YOUR_MAPBOX_ACCESS_TOKEN"onViewportChange={setViewport}onClick={handlePolygonClick}interactiveLayerIds={['polygon-layer']}>{/* 点标记 */}{points.map(point => (<Markerkey={point.id}longitude={point.longitude}latitude={point.latitude}><buttonstyle={{ background: 'none', border: 'none', cursor: 'pointer' }}onClick={e => {e.preventDefault();setSelectedPoint(point);}}><div style={{ color: 'red', fontSize: '24px' }}>📍</div></button></Marker>))}{/* 点标记弹出框 */}{selectedPoint && (<Popuplongitude={selectedPoint.longitude}latitude={selectedPoint.latitude}onClose={() => setSelectedPoint(null)}><div><h3>{selectedPoint.name}</h3><p>详细信息...</p></div></Popup>)}{/* 多边形区域 */}<Source id="polygon-source" type="geojson" data={polygonData}><Layerid="polygon-layer"type="fill"paint={{'fill-color': '#088','fill-opacity': 0.4,'fill-outline-color': '#000'}}/></Source>{/* 多边形弹出框 */}{selectedPolygon && (<Popuplongitude={selectedPolygon.lngLat[0]}latitude={selectedPolygon.lngLat[1]}onClose={() => setSelectedPolygon(null)}><div><h3>多边形区域</h3><p>点击了多边形</p></div></Popup>)}</ReactMapGL>);
};export default MapWithReactMapGL;

相关文章:

  • PostGIS实现栅格数据导出TIFF应用实践【ST_AsTiff】
  • RK3399 Android13设备插拔无线鼠标键盘设备出现APP或系统界面刷新现象
  • RISC-V 开发板 MUSE Pi Pro OpenCV结合Gstreamer实时显示CSI摄像头
  • 从负债到上岸:湖北理元理律师事务所的专业债务规划之道
  • 开源工具自建AI大模型底座:打造你的专属智能助理
  • 数据分析概述and环境配置
  • 驱动开发学习20250523
  • Unity 打包程序全屏置顶无边框
  • Docker端口映射与容器互联
  • 从 Docker 到 runC
  • 微信小程序:列表项上同样的css样式在IOS上字体大小不一样
  • uniapp uts 插件开发指南
  • 电路图识图基础知识-电气符号(二)
  • Nginx-配置详解
  • 【论文精读】2023 CVPRW--EAVSR现实世界视频超分辨率(RealWorld VSR)
  • 响应面法(Response Surface Methodology ,RSM)
  • 鸿蒙Ability对比Android的Fragment
  • CSS2相关知识点
  • IvorySQL-WASM:免安装的数据库探索之旅
  • 【沉浸式求职学习day47】【JSP详解】
  • 住房和城乡建设部网站施工员/阿里指数查询入口
  • 高校专业建设五大要素/如何进行搜索引擎优化
  • 深圳网站建设 东莞网站建设/软文营销代理
  • 福建省人民政府网站/网络广告营销的典型案例
  • 网站平台怎么建设/网站优化排名工具
  • 网站建设售后培训/免费网站搭建