【问题标题】:Category searches in Mapbox GL GeocoderMapbox GL Geocoder 中的类别搜索
【发布时间】:2020-12-09 05:46:19
【问题描述】:

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

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

这是一个基本的 react + 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'));

谢谢。

【问题讨论】:

    标签: mapbox openstreetmap geocoding mapbox-gl-js


    【解决方案1】:

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

    【讨论】:

    • 在哪里插入过滤功能?是您在箭头函数中放入的内容吗? geocoder.on('结果', (e) => {} )
    • 定义过滤器函数过滤在地理编码器下拉建议中显示为建议结果的内容,但它不允许我们同时在所有结果上放置标记。我们似乎可以同时访问多个结果的唯一一次是在 geocoder.on('results', callback) 方法中。但是一旦用户按下回车,它将执行地理编码器中突出显示的结果建议,并丢弃其余的。我们如何在所有结果上添加标记?
    • 对,在results 事件上放置一个监听器,然后将它们全部放在地图上。这有什么问题?
    • 啊,对不起。我误解了问题的重点,被技术细节分散了注意力。您要搜索“该地区的所有咖啡馆”吗?不要使用地理编码器,只需使用mapbox-streets 矢量切片并过滤到咖啡馆。它不会完美,并且在低变焦时根本无法工作。
    【解决方案2】:

    可以做到这一点,但这是人为的。让我们以“咖啡馆”为例进行类别搜索。 Steve Bennett 建议使用 mapbox-streets(在他的答案的 cmets 中)似乎是要走的路,但我在这里记录我的答案作为替代方案,以防它对其他人有用。

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

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

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

    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 步:监听“cafe”结果,如果出现,则返回 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 的建议那么优雅,但我还没有深入研究,这至少有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-02
      • 1970-01-01
      相关资源
      最近更新 更多