首页 > 解决方案 > 如何从地图外部将对象拖放到 Mapbox 地图中?

问题描述

如何在Mapbox 地图对象之外创建可以拖入其中的元素?例如,假设我想在页面上呈现位置列表。每个位置都是一个带有自定义标记或图标的 React 组件。

这个位置列表旁边是一个 Mapbox 地图。位置列表不会在地图内呈现。虽然我知道可以使这些单独的位置组件可拖动,但是否可以将它们拖放到 Mapbox 地图中并将其识别为地图上具有纬度/经度坐标的实际标记?如果是这样,我该怎么做?

以下是我尝试过的代码中的相关源文件:

index.js

import dynamic from "next/dynamic";
import { useSelector } from "react-redux";
import Plant from "../../components/Plant";

const MapboxMap = dynamic(() => import("../../components/MapboxGLMap"), {
  ssr: false,
});

const Blueprint = () => {
  const plants = useSelector((state) => state.plants);

  const showPlants = () => {
    return (
      <React.Fragment>
        {plants.map((plant) => (
          <Plant plant={plant} />
        ))}
      </React.Fragment>
    );
  };

  return (
    <React.Fragment>
      <div className="ui container centered grid blueprint">
        <div className="three wide column scrollable">
          <div className="ui link one cards">{showPlants()}</div>
        </div>
        <div className="twelve wide column">
          <MapboxMap />
        </div>
      <style jsx>{`
        .scrollable {
          height: calc(100vh);
          overflow-x: auto;
        }
      `}</style>
    </React.Fragment>
  );
};

export default Blueprint;

植物.jsx

import React from "react";
import { useDrag } from "react-dnd";

const ItemTypes = {
  PLANT: "plant",
};

const Plant = ({ plant }) => {
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes.PLANT },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  });
  return (
    <div
      ref={drag}
      style={{
        opacity: isDragging ? 0.1 : 1,
        cursor: "move",
      }}
      key={plant.id}
      className="card"
    >
      <div className="image">
        <img src="/white-image.png" />
      </div>
      <div className="content">
        <div className="center aligned">{plant.common_name}</div>
      </div>
    </div>
  );
};

export default Plant;

MapboxGLMap.jsx

import React, { useEffect, useRef, useState } from "react";
import mapboxgl from "mapbox-gl";
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
import MapboxDraw from "@mapbox/mapbox-gl-draw";

const MAPBOX_TOKEN = "xxx";

const styles = {
  width: "100%",
  height: "100%",
  position: "absolute",
};

const MapboxGLMap = () => {
  const [map, setMap] = useState(null);
  const [lng, setLng] = useState(null);
  const [lat, setLat] = useState(null);
  const [plant, setPlant] = useState(null);

  const mapContainer = useRef(null);

  useEffect(() => {
    mapboxgl.accessToken = MAPBOX_TOKEN;
    const initializeMap = ({ setMap, mapContainer }) => {
      const map = new mapboxgl.Map({
        container: mapContainer.current,
        style: "mapbox://styles/mapbox/satellite-v9", // stylesheet location
        center: [0, 0],
        zoom: 5,
      });

      map.on("load", () => {
        setMap(map);
        map.resize();
      });

      map.on("click", (e) => {});

      map.addControl(
        new MapboxGeocoder({
          accessToken: MAPBOX_TOKEN,
          mapboxgl: mapboxgl,
        })
      );

      const draw = new MapboxDraw({
        displayControlsDefault: false,
        controls: {
          polygon: true,
          trash: true,
        },
      });
      map.addControl(draw);

      map.on("draw.create", (e) => {
        console.log("e =>", e);
        console.log("draw.getAll()", draw.getAll());
      });

      map.on("mousemove", (e) => {
        // console.log(e.point);
        setLng(e.lngLat.wrap().lng);
        setLat(e.lngLat.wrap().lat);
      });
    };

    if (!map) initializeMap({ setMap, mapContainer });
  }, [map]);

  return <div ref={(el) => (mapContainer.current = el)} style={styles} />;
};

export default MapboxGLMap;

标签: javascriptreactjsmapboxnext.jsmapbox-gl-js

解决方案


实际上,根据您的相关标签,我想您想将诸如图钉之类的东西从外部拖放到地图区域。你使用reactjs标签,这意味着你想通过使用 ReactJS 来做到这一点。

为此,您应该通过 npm 或 yarn 安装 Mapbox:

npm install mapbox-gl --save

或者

yarn add mapbox-gl

然后你应该将 Mapbox 区域包裹在一个放置区中。为此,您可以使用react-dropzone. 通过以下命令安装它:

npm install react-dropzone --save

或者

yarn add react-dropzone

将以下行添加到 HTML 模板:

<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.css' rel='stylesheet' />

然后像下面这样使用它:

import React from 'react';
import Dropzone from 'react-dropzone';
import mapboxgl from 'mapbox-gl';

mapboxgl.accessToken = 'MAPBOX_ACCESS_TOKEN';

class MapComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      lng: 5,
      lat: 34,
      zoom: 2,
    };
  }

  componentDidMount() {
    const map = new mapboxgl.Map({
      container: this.mapContainer,
      style: 'mapbox://styles/mapbox/streets-v11',
      center: [this.state.lng, this.state.lat],
      zoom: this.state.zoom,
    });

    map.on('move', () => {
      this.setState({
        lng: map.getCenter().lng.toFixed(4),
        lat: map.getCenter().lat.toFixed(4),
        zoom: map.getZoom().toFixed(2),
      });
    });
  }

  render() {
    return (
      <Dropzone>
        {({ getRootProps, getInputProps }) => (
          <section>
            <div {...getRootProps()}>
              <input {...getInputProps()} />
              <div
                ref={el => {
                  this.mapContainer = el;
                }}
              />
            </div>
          </section>
        )}
      </Dropzone>
    );
  }
}

通过使用此方法,您可以放置​​一些图像并获取它,并根据放置位置将其显示在地图上。

注意将捕获的文件类型减少为图像文件类型,如jpg/png


推荐阅读