Skip to content

地图(Map)

地图组件 交互方式:左键拖动,右键切换视角

TIP

需要在地图中显示字体时,一定要设置 glyphs 属性

TIP

如果要自定义坐标系,crs参数格式为字符串或者数组:[name, wkt, extent];

  • name:坐标系名称,必填 -或者Proj4表述,必填。
  • extent: 当前坐标系范围,[左,下,右,上]

具体实现可参见:常见问题

基础用法

示例源码
tsx
import React, { useRef, useState } from 'react'
import ReactDOM from 'react-dom'
import { MbMap, MbWmtsLayer } from '@mapbox-react/core'

const App = () => {
  const [mapCenter] = useState([116.3466, 39.8704])
  const [zoom, setZoom] = useState(8)
  const [pitch, setPitch] = useState(0)
  const mapInst = useRef<any>()
  const [mousePosition, setMousePosition] = useState('')

  const easeToRandomPos = () => {
    mapInst.current
      ?.easeTo({
        center: [
          mapCenter[0] + getRandomArbitrary(-1, 1),
          mapCenter[1] + getRandomArbitrary(-1, 1),
        ],
        duration: 2000,
      })
      .then(() => {
        // callback after over
      })
  }
  const flyToRandomPos = () => {
    mapInst.current
      ?.flyTo({
        center: [getRandomArbitrary(-180, 180), getRandomArbitrary(-90, 90)],
        zoom: getRandomArbitrary(0, 18),
      })
      .then(() => {
        // callback after over
      })
  }

  function getRandomArbitrary(min: number, max: number) {
    return Math.random() * (max - min) + min
  }

  const mouseMoveHandler = (movement) => {
    setMousePosition(movement.coordinates)
  }

  return (
    <div className="map-wrapper">
      <MbMap
        ref={mapInst}
        center={mapCenter}
        zoom={zoom}
        pitch={pitch}
        crs="EPSG:3857"
        extendProps={{
          accessToken:
            'pk.eyJ1IjoiYml0ampqIiwiYSI6ImNtMnNubnlyNTFvNnoycHB5M2M2ZjVpeHEifQ.kk7T4f-CuUA_K6AuPwbvMg',
        }}
        onMouseMove={mouseMoveHandler}
        onZoomChange={(z) => setZoom(z)}
        onPitchChange={(p) => setPitch(p)}
      >
        <div style={{ position: 'absolute', top: '10px', left: '10px' }}>
          <button className="primary mr10" onClick={easeToRandomPos}>
            ease to some position
          </button>
          <button className="primary" onClick={flyToRandomPos}>
            fly to some position
          </button>
        </div>
        <MbWmtsLayer
          url="https://t{s}.tianditu.gov.cn/img_w/wmts?tk=b8ed92ff9b64aebcb0110acca15e478f"
          layerName="img"
          layerStyle="default"
          tileMatrixSetID="w"
          subdomains="01234567"
          maxzoom={18}
        />
      </MbMap>
      <div
        style={{
          position: 'absolute',
          bottom: '0px',
          right: '0px',
          backgroundColor: 'white',
          padding: '10px',
        }}
      >
        <p className="text-muted">
          center:{`${mapCenter}`} zoom:{zoom}
        </p>
        <p className="text-muted">pitch:{`${pitch}`}</p>
        <p className="text-muted">mouse position:{`${mousePosition}`}</p>
      </div>
    </div>
  )
}

ReactDOM.render(<App />, document.querySelector('#root'))

图层重排

示例源码
tsx
import React, { useState, useRef } from 'react'
import ReactDOM from 'react-dom'
import { MbMap, MbTiandituLayer, MbImageLoader, MbImageFrameLoader, MbSymbolLayer, MbBufferEllipseLayer, MbBufferPolygonLayer } from '@mapbox-react/core'

const App = () => {
  const [mapCenter] = useState([116.3466, 39.8704])
  const [zoom, setZoom] = useState(10)

  const mapInst = useRef<any>()
  const symbolDataSource = [
    {
      coordinates: [116.45346681596038, 39.90085772830014],
      properties: {
        icon: 'event',
      },
    },
    {
      coordinates: [...mapCenter],
      properties: {
        icon: 'event',
      },
    },
  ]
  const symbolDataSource2 = [
    {
      coordinates: [116.46346681596, 39.90085772830014],
      properties: {
        icon: 'event2',
      },
    },
    {
      coordinates: [...mapCenter],
      properties: {
        icon: 'event2',
      },
    },
  ]
  const symbolDataSource3 = [
    {
      coordinates: [116.47346681596038, 39.94085772830014],
      properties: {
        icon: 'event3',
        iconSize: 0.1,
      },
    },
    {
      coordinates: [...mapCenter],
      properties: {
        icon: 'event3',
        iconSize: 0.1,
      },
    },
  ]
  const polygon = [
    [116.17381, 39.92155464],
    [116.25105956968257, 40.02398993628292],
    [116.31591012802686, 39.96769599504311],
  ]
  const images = [
    {
      name: 'event',
      type: 'link',
      url: `https://mapbox-web.github.io/mapbox-react/images/common_toget.png`,
    },
    {
      name: 'event2',
      type: 'link',
      url: `https://mapbox-web.github.io/mapbox-react/images/eme_team_soc_toget.png`,
    },
  ]
  const images2 = [
    {
      name: 'event3',
      dir: 'v' as const,
      steps: 3,
      duration: 300,
      url: `https://mapbox-web.github.io/mapbox-react/images/v-steps.png`,
    },
  ]

  const reorder = (layerIds: string[]) => {
    mapInst.current?.reorderLayers(layerIds)
  }

  return (
    <div className="map-wrapper">
      <MbMap
        ref={mapInst}
        center={mapCenter}
        zoom={zoom}
      >
        <div style={{ position: 'absolute', top: '10px', left: '10px' }}>
          <button onClick={() => reorder(['buffer2', 's3', 'buffer1', 's2', 's1'])}>Order:buffer2, s3, buffer1, s2, s1(top)</button>
          <button onClick={() => reorder(['s2', 's3', 's1', 'buffer2', 'buffer1'])}>Order:s2, s3, s1, buffer2, buffer1(top)</button>
        </div>
        <MbTiandituLayer types={['vec', 'cva']} />
        <MbImageLoader images={images} />
        <MbImageFrameLoader images={images2} />
        <MbSymbolLayer
          id="s1"
          data={symbolDataSource}
          textField="label"
          iconImageField="icon"
          iconSize={0.5}
          textOffset={[0, -1]}
          iconAllowOverlap={true}
          textAllowOverlap={true}
        />
        <MbBufferEllipseLayer
          id="buffer1"
          center={mapCenter}
          xSemiAxis={10}
          ySemiAxis={5}
          angle={0}
        />
        <MbSymbolLayer
          id="s2"
          data={symbolDataSource2}
          textField="label"
          iconImageField="icon"
          iconSize={0.5}
          textOffset={[0, -1]}
          cluster={true}
          spiderify={true}
          clusterMaxZoom={18}
          iconAllowOverlap={true}
          textAllowOverlap={true}
        />
        <MbSymbolLayer
          id="s3"
          data={symbolDataSource3}
          textField="label"
          iconImageField="icon"
          iconSize={0.5}
          textOffset={[0, -1]}
          iconAllowOverlap={true}
          textAllowOverlap={true}
        />
        <MbBufferPolygonLayer
          id="buffer2"
          polygon={polygon}
          radius={7}
          center={mapCenter}
        />
      </MbMap>
    </div>
  );
}

ReactDOM.render(<App />,
  document.getElementById("root"))

获取地图实例方式

通常来讲,获取 MbMap 实例可以通过 ref 方式实现,获取封装的 mapbox 实例可以通过监听 MbMaponCreated 事件来获取。

  • 如果您的组件可以放到 MbMap 组件里面,那么可以使用组件库提供的 MapContext 上下文来获取地图实例
demo3.tsx
tsx
import { MbMap } from '@mapbox-react/core'
import Instance1 from './instance1'

function Demo3() {
  return (
    <div style={{ height: '400px' }} className="vw-full vh-full">
      <MbMap>
        <Instance1 />
      </MbMap>
    </div>
  )
}

export default Demo3
instance1.tsx
tsx
import { useContext } from 'react'
import { MapContext } from '@mapbox-react/core'

function Instance1() {
  const mapContext = useContext(MapContext)

  console.log(mapContext.mapInstance, mapContext.mapboxInstance)
}

export default Instance1
  • 如果想在其他组件中和地图进行交互,例如操作ThreeJS等,您可以声明全局变量,然后监听onCreated事件,并存储Mapbox地图实例,在需要的时候使用此实例操作地图。下面给出部分代码示例:
ts
// use-map.ts
import type { MapboxInstance } from '@mapbox-react/core'

let mapbox: MapboxInstance

function useMap() {
  const onCreated = (mb: MapboxInstance) => {
    console.log('map created', mb)
    mapbox = mb
  }

  // 不需要的时候记住销毁
  const destroy = () => {
    mapbox = null
  }

  return {
    onCreated,
    destroy,
    // 这里一定要写成方法,直接返回mapbox得到的结果将一直是undefined
    getMapbox: () => mapbox
  }
}

export { useMap }
tsx
import { MbMap } from '@mapbox-react/core'
import { useMap } from './use-map'

function App() {
  const { onCreated } = useMap()

  return <MbMap onCreated={onCreated}> ... </MbMap>
}

export default App

在需要使用的地方:

tsx
import { useMap } from './use-map'

const { getMapbox } = useMap()

上述方式有个问题,只能在地图实例创建以后才能使用getMapbox方法获取实例,否则得到的实例为undefined

  • 如果您的代码逻辑在操作地图时不能确定地图是否已经完成创建,那么需要使用稍微复杂的方式进行地图操作,下面给出代码示例:

您需要安装 ts-deferred 工具库

ts
// map-deferred.ts
import { Deferred } from 'ts-deferred';

const defferedMap = new Deferred<void>();

const waitingForMap = async() => {
  return await defferedMap.promise;
};
const resolveMap = () => {
  defferedMap.resolve();
};

export { waitingForMap, resolveMap };
ts
// use-map.ts
import { resolveMap } from './map-deferred'
import type { MapboxInstance } from '@mapbox-react/core'

let mapbox: MapboxInstance

function useMap() {
  const onCreated = (mb: MapboxInstance) => {
    console.log('map created', mb)
    mapbox = mb
    resolveMap()
  }

  // 不需要的时候记住销毁
  const destroy = () => {
    mapbox = null
  }

  return {
    onCreated,
    destroy,
    getMapbox: () => mapbox
  }
}

export { useMap }
tsx
// instance.tsx
import { useEffect } from 'react'
import { useMap } from './use-map'
import { waitingForMap } from './map-deferred'

function Instance() {
  const { getMapbox } = useMap()

  useEffect(() => {
    const operateMap = async() => {
      await waitingForMap()
      // 下面可以使用getMapbox()获取地图实例进行操作了
      console.log('operate', getMapbox())
    }
    operateMap()
  }, [])

  return <></>
}

export default Instance
tsx
import { MbMap } from '@mapbox-react/core'
import { useMap } from './use-map'
import Instance from './instance'

function App() {
  const { onCreated } = useMap()

  return <>
    <MbMap onCreated={onCreated}> ... </MbMap>
    <Instance />
  </>
}

export default App

API

PROPS

名称描述类型默认值
antialias是否开启抗锯齿模式; 关闭后性能将有所提升booleantrue
bearing地图初始化时的方位角(旋转角度)number0
bounds地图初始化时的四至范围。如果设置了 bounds 将会覆盖掉 center 和 zoom 的设置 其值为rectangle的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]]IndexAny / AnyArrundefined
center地图地理中心点坐标,采用经度、纬度的顺序number[][116.38745, 39.91266]
mapStyle地图样式信息 包括字体文件路径以及精灵图资源路径IndexAny / string{ version: 8, sources: {}, layers: [], glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', sprite: "mapbox://sprites/mapbox/bright-v8" }
glyphs字体文件路径,可单独设置,会覆盖mapStyle中值string-
sprite雪碧图路径,可单独设置,会覆盖mapStyle中值string-
maxBounds设置之后,地图将限制在给定的最大范围内IndexAny / AnyArrundefined
maxzoom地图最大缩放级别number22
minzoom地图最小缩放级别number0
pitch地图初始化时的倾角number0
zoom地图缩放级别number2
hash地图的位置信息是否同步到浏览器URL的hash上boolean / stringfalse
interactive地图是否可交互booleantrue
crs坐标系,优先级低于extendPropsstring / AnyArr-
extendProps创建地图扩展属性,可参见Mapbox文档IndexAny{}
children子节点any-

extendProps说明

Mapbox官方文档中创建地图接口的所有参数都可以通过此属性传入,优先级高于Map组件中其他所有属性

TIP

除了可用于 bearingcenterpitchzoom 的四个属性,其他所有属性都是非响应式。 如需修改,可通过onCreated事件参数获取map对象,然后调用Mapbox原生API实现。

EVENTS

名称描述参数
onCreated地图初始化完成事件map: MapboxInstance
onZoomChange地图缩放级别变化事件number
onCenterChange地图中心点变化事件number[]
onBearingChange地图方位角变化事件number
onPitchChange地图倾角变化事件number
onClick鼠标点击事件 每次点击都会触发object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent
onDoubleClick鼠标双击事件object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent
onSingleClick鼠标单击事件object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent
onMouseMove鼠标移动事件object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent
onMouseOut鼠标离开地图事件MapMouseEvent & EventData

METHODS

名称描述定义
moveLayer将图层移动到beforeId所代表的图层前,若beforeId为空,则将图层移动至数组列表末尾(地图最顶层)id: string, beforeId: string | undefined
resize根据其 container 元素的尺寸调整地图的大小-
setCenter设置地图中心点centerValue: number[] — 经纬度数组
jumpTo不用动态转换的情况下改变中心点、 缩放级别、方位角和倾斜度。地图将保留 options 没有指定的当前值。options: object{center, zoom, bearing, pitch}
easeTo使用动态转换,将中心点、缩放级别、方位角和倾斜度组合的原有数值改为新数值。地图将保留 options 没有指定的当前值。options: object{center, zoom, bearing, pitch, duration = 1000, easing, offset} => promise
flyTo按照飞行曲线到移动到目标位置。options: object{center, zoom, bearing, pitch, duration, easing, offset, animate, curve=1.42, speed=1.2, maxDuration} => promise
stop停止所有地图平移/飞行。-
cameraForBounds根据目标四至范围计算返回对应的镜头参数。bounds: number[] — 四至范围,其值的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]], options: object{padding,offset=[0,0]}
fitBounds在指定的地理边界内平移和缩放地图,以包含其可见区域。bounds: number[] — 四至范围,其值的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]], options: object{padding,linear=false,offset=[0,0]}
project返回一个表示像素坐标的Point,相对于地图的container,与指定的地理位置相对应。lnglatLike
reorderLayers重排图层,参数为图层Id数组string[]