Mapbox GL Geocoder 中的类别搜索

Category searches in Mapbox GL Geocoder

如何使用 mapbox gl 地理编码器执行一般搜索? “通用”是指搜索一种地点,而不是特定地点,例如“咖啡店”。我希望能够搜索“咖啡馆”或“杂货店”或任何通用类别,并让地图在当前视口中找到的该类型的每个地方放置一个标记。它看起来不像一般搜索或任何支持 returns 多个结果的搜索。那是对的吗?这有点令人惊讶,因为 OpenStreetMap 似乎拥有支持此类搜索所需的数据。 我已经包含了类型:'poi' 和一个过滤函数,如果它们的 obj.properties.category 字符串中没有“cafe”或“coffee”,则会删除潜在的结果。这减少了结果建议中的噪音,但是当用户按下 ENTER 时,结果仍然只是一个地方(无论哪个建议被突出显示)。文档示例中没有这种通用搜索的示例。

请注意,此示例已通过使用边界框将结果限制在纽约市内。另请注意,您必须插入自己的 mapboxgl.accessToken 才能使此示例正常工作。

这是一个有效的基本反应 + mapbox gl 设置:

import React from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css'
import Geocoder from "@mapbox/mapbox-gl-geocoder";
import "@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css";
import './map.scss';

mapboxgl.accessToken = 'YOUR_ACCESS_TOKEN_HERE';
const nyc_bbox = [-74.04848, 40.54217, -73.72479, 41.15114]

export class Map extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      lng: -73.9392,
      lat: 40.8053,
      zoom: 14
    };
  }

  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,
      zoomAnimation: false
    });

    const geocoder = new Geocoder({
      accessToken: mapboxgl.accessToken,
      mapboxgl: mapboxgl,
      bbox: nyc_bbox,
      types: 'poi'
    })
    
    map.addControl(geocoder);
  }

  render() {
    return <div ref={el => this.mapContainer = el} className='mapContainer'/>
  }
}

以及随附的 scss 文件 (./map.scss):

.mapContainer {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
  }

还有 ./index.js 文件:

import React from 'react';
import ReactDOM from 'react-dom';
import { Map } from './map';

class Application extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <Map />
  }
}

ReactDOM.render(
  <Application />,
  document.getElementById('app'));

谢谢。

当然,这是可能的。您可以将 poi 作为 types 的值传递,并定义一个 filter 函数来检查每个结果的 categories 属性 以决定是否保留它或没有。

这项工作是可行的,但它是人为设计的。我们以“咖啡馆”为例进行类别搜索。 Steve Bennett 建议使用 mapbox-streets(在他的回答的评论中)似乎是可行的方法,但我在这里记录我的回答作为替代方案,以防它对其他人有用。

第 1 步:为我要支持的搜索词(在本例中为“咖啡馆”搜索)添加一个空的搜索结果数组。我还需要经度和纬度。

this.state = {
  cafes: [],
  lng: -74,
  lat: 40.8
}

第 2 步:添加 localGeocoder 函数,并 return 搜索词的结果,以便它出现在建议中,用户可以在地理编码器输入字段中点击“输入”。将那个点的位置设置为当前地图的中心,这样当你选择“咖啡馆”结果时它就不会移动

const geocoder = new Geocoder({
  accessToken: mapboxgl.accessToken,
  mapboxgl: mapboxgl,
  marker: false,
  localGeocoder: this.localGeocodingFunc
})

// ...

  localGeocodingFunc(query) {
    if (query !== 'cafe') {
      return [];
    }
    const cafe_result = {
      type: 'General cafe search',
      properties: {
        title: 'generic cafe',
        description: "Generic cafe search trigger"
      },
      context: [],
      geometry: {
        coordinates: [this.state.lng, this.state.lat],
        type: 'Point'
      },
      place_name: 'cafe',
      text: 'cafe',
      center: [this.state.lng, this.state.lat],
      place_type: ['cafe']
    }
    return [cafe_result];
  }

第 3 步:每次用户移动地图时,更新地理编码器的缩放级别以匹配地图的缩放级别,这样您就不会在选择“咖啡馆”结果时更改缩放级别。我还发现设置边界框很有用,以便显示当前视口中唯一的咖啡馆结果。

map.on('move', () => {
  geocoder.setZoom(map.getZoom());
  geocoder.setBbox(this.viewportToBbox(map));
});

//...

  viewportToBbox(_map) {
    // Return the current viewport in a bbox array format.
    let {_sw, _ne} = _map.getBounds();
    return [_sw.lng, _sw.lat, _ne.lng, _ne.lat];
  }

第 4 步:收听“咖啡馆”结果,如果出现,则 return this.state.cafes 中列出的咖啡馆。我在这里使用 jquery 命令删除地图上当前的任何标记,如果您多次执行此类别搜索,可以防止标记堆积。

geocoder.on('result', (e) => {
  console.log('onResult e value:', e);
  $( ".marker" ).remove();
  if (e.result.text === 'cafe') {
    if (this.state.cafes.length > 0) {
      // Process these cafes however you want to put a marker on each one
    }
  }
}

所以这几乎肯定不如 Steve Bennett 关于使用 mapbox-streets 的建议那么优雅,但我还没有深入研究,这至少有效。