如何在Vue.js中使用Mapbox构建一个地理编码应用程序

546 阅读9分钟

精确性和模块化是使地理编码成为寻找特定地点的完美手段的好处之一。

在本指南中,我们将使用Vue.js和Mapbox从头开始构建一个[简单的地理编码应用]。我们将涵盖从建立前端脚手架到建立一个地理编码器来处理前向地理编码和反向地理编码的过程。为了充分利用本指南,你需要对JavaScript和Vue.js以及如何进行API调用有一个基本的了解。

什么是地理编码?

地理编码是将基于文本的位置转化为地理坐标(通常是经度和纬度),表明世界上的一个位置。

地理编码有两种类型:正向和反向。正向地理编码将位置文本转换为地理坐标,而反向地理编码将坐标转换为位置文本。

换句话说,反向地理编码将40.714224, -73.961452变成 "277 Bedford Ave, Brooklyn",而正向地理编码则相反,将 "277 Bedford Ave, Brooklyn "变成40.714224, -73.961452。

为了让大家有更多的了解,我们将建立一个迷你网络应用程序,使用带有自定义标记的交互式网络地图来显示位置坐标,随后我们将把这些坐标解码为位置文本。

我们的应用程序将具有以下基本功能。

  • 让用户访问带有标记的交互式地图显示。
  • 允许用户随意移动标记,同时显示坐标。
  • 根据用户的要求,返回一个基于文本的位置或位置坐标。

使用Vue CLI设置项目

我们将利用[这个资源库中的模板]。它包含一个带有Vue CLI和yarn 作为包管理器的新项目。你需要克隆这个资源库。确保你是从geocoder/boilerplate 分支工作的。

设置应用程序的文件结构

接下来,我们将需要设置我们项目的文件结构。将组件文件夹中的Helloworld.vue 文件重命名为Index.vue ,并暂时留空。继续往前走,将以下内容复制到App.vue 文件中。

<template>
  <div id="app">
    <!--Navbar Here -->
    <div>
      <nav>
        <div class="header">
          <h3>Geocoder</h3>
        </div>
      </nav>
    </div>
    <!--Index Page Here -->
    <index />
  </div>
</template>
<script>
import index from "./components/index.vue";
export default {
  name: "App",
  components: {
    index,
  },
};
</script>

在这里,我们已经导入并在本地注册了最近重命名的组件。我们还添加了一个导航条来提升我们的应用程序的美感。

我们需要一个.env 文件来加载环境变量。继续,在你的项目文件夹根部添加一个。

安装必要的软件包和库

为了启动开发过程,我们将需要安装所需的库。下面是我们将在这个项目中使用的列表。

  1. Mapbox GL JS
    这个JavaScript库使用WebGL来渲染来自矢量图Mapbox的互动地图。
  2. Mapbox-gl-geocoder
    这个Mapbox GL的geocoder控件将帮助我们进行前向地理编码。
  3. Dotenv
    我们不需要安装这个,因为它已经预装在Vue CLI中了。它可以帮助我们将环境变量从.env 文件加载到 process.env.这样,我们就可以把我们的配置与我们的代码分开。
  4. Axios
    这个库将帮助我们进行HTTP请求。

根据你喜欢的软件包管理器,在你的CLI中安装这些软件包。如果你使用Yarn,运行下面的命令。

cd geocoder && yarn add mapbox-gl @mapbox/mapbox-gl-geocoder axios

如果你使用npm,运行这个。

cd geocoder && npm i mapbox-gl @mapbox/mapbox-gl-geocoder axios --save

在运行安装命令之前,我们首先要进入geocoder 文件夹。

用Vue.js搭建前端的脚手架

让我们继续前进,为我们的应用程序创建一个布局。我们将需要一个元素来容纳我们的地图,一个区域来显示坐标,同时监听地图上标记的移动,以及当我们调用反向地理编码API时显示位置的东西。我们可以把所有这些都放在一个卡片组件中。

将以下内容复制到你的Index.vue 文件中。

<template>
  <div class="main">
    <div class="flex">
      <!-- Map Display here -->
      <div class="map-holder">
        <div id="map"></div>
      </div>
      <!-- Coordinates Display here -->
      <div class="dislpay-arena">
        <div class="coordinates-header">
          <h3>Current Coordinates</h3>
          <p>Latitude:</p>
          <p>Longitude:</p>
        </div>
        <div class="coordinates-header">
          <h3>Current Location</h3>
          <div class="form-group">
            <input
              type="text"
              class="location-control"
              :value="location"
              readonly
            />
            <button type="button" class="copy-btn">Copy</button>
          </div>
          <button type="button" class="location-btn">Get Location</button>
        </div>
      </div>
    </div>
  </div>
</template>

要看我们目前有什么,启动你的开发服务器。对于Yarn来说。

yarn serve

或npm。

npm run serve

我们的应用程序现在应该看起来像这样。

左边的空白处看起来不对。它应该是我们的地图显示。让我们来添加它。

使用Mapbox的交互式地图显示

我们需要做的第一件事是获得对Mapbox GL和Geocoder库的访问。我们将首先在Index.vue 文件中导入Mapbox GL和Geocoder库。

import axios from "axios";
import mapboxgl from "mapbox-gl";
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
import "@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css";

Mapbox需要一个独特的访问令牌来计算地图矢量瓦片。获取你的,并将其作为环境变量添加到你的.env 文件中。

.env
VUE_APP_MAP_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

我们还需要定义一些属性,以帮助将我们的地图瓦片放在我们的数据实例中。在我们导入库的地方下面添加以下内容。

export default {
  data() {
    return {
      loading: false,
      location: "",
      access_token: process.env.VUE_APP_MAP_ACCESS_TOKEN,
      center: [0, 0],
      map: {},
    };
  },
}
  • location 属性将被建模到我们的脚手架中的输入。我们将用它来处理反向地理编码(即从坐标显示位置)。
  • center 属性包含我们的坐标(经度和纬度)。这对于把我们的地图瓦片放在一起至关重要,我们很快就会看到。
  • access_token 属性指的是我们的环境变量,这是我们之前添加的。
  • map 属性作为我们地图组件的构造函数。

让我们继续创建一个方法来绘制我们的交互式地图,并将我们的前向地理编码器嵌入其中。这个方法是我们的基础函数,作为我们的组件和Mapbox GL之间的中介;我们将把这个方法称为createMap 。将其添加到数据对象的下面。

mounted() {
  this.createMap()
},

methods: {
  async createMap() {
    try {
      mapboxgl.accessToken = this.access_token;
      this.map = new mapboxgl.Map({
        container: "map",
        style: "mapbox://styles/mapbox/streets-v11",
        center: this.center,
        zoom: 11,
      });

    } catch (err) {
      console.log("map error", err);
    }
  },
},

为了创建我们的地图,我们已经指定了一个容纳地图的container ,一个用于我们地图显示格式的style 属性,以及一个容纳我们的坐标的center 属性。center 属性是一个数组类型,用于保存经度和纬度。

Mapbox GL JS根据页面上的这些参数来初始化我们的地图,并向我们返回一个Map 对象。Map 对象指的是我们页面上的地图,同时暴露了使我们能够与地图互动的方法和属性。我们将这个返回的对象存储在我们的数据实例中,this.map

使用Mapbox Geocoder进行前向地理编码

现在,我们将添加地理编码器和自定义标记。地理编码器通过将基于文本的位置转换为坐标来处理前向地理编码。这将以一个搜索输入框的形式出现在我们的地图上。

在我们上面的this.map 的初始化下面添加以下内容。

let geocoder =  new MapboxGeocoder({
    accessToken: this.access_token,
    mapboxgl: mapboxgl,
    marker: false,
  });

this.map.addControl(geocoder);

geocoder.on("result", (e) => {
  const marker = new mapboxgl.Marker({
    draggable: true,
    color: "#D80739",
  })
    .setLngLat(e.result.center)
    .addTo(this.map);
  this.center = e.result.center;
  marker.on("dragend", (e) => {
    this.center = Object.values(e.target.getLngLat());
  });
});

这里,我们首先使用`MapboxGeocoder`构造函数创建了一个新的地理编码器实例。这将根据所提供的参数初始化一个地理编码器,并返回一个对象,暴露给方法和事件。`accessToken`属性指的是我们的Mapbox访问令牌,`mapboxgl`指的是当前使用的[地图库](docs.mapbox.com/#maps)。 我们的应用程序的核心是自定义标记;地理编码器默认带有一个。然而,这并不能满足我们所有的自定义需求;因此,我们禁用了它。 继续,我们将新创建的地理编码器作为参数传递给`addControl`方法,该方法由我们的地图对象暴露给我们。`addControl'接受一个`control'作为参数。 为了创建我们的自定义标记,我们利用了地理编码器对象暴露给我们的一个事件。`on`事件监听器使我们能够订阅发生在地理编码器中的事件。我们还必须跟踪我们的自定义标记的移动。我们通过使用`dragend`事件监听器实现了这一点,并且我们用当前的坐标更新了`center`属性。 让我们更新模板,以显示我们的交互式地图和前向地理编码器。在我们的模板中更新坐标显示部分,内容如下。

<div class="coordinates-header">
  <h3>Current Coordinates</h3>
  <p>Latitude: {{ center[0] }}</p>
  <p>Longitude: {{ center[1] }}</p>
</div>

还记得我们是如何在事件发生后更新我们的center 属性吗?我们在这里显示基于当前值的坐标。

为了提升我们应用程序的美感,在index.html 文件的head 部分添加以下CSS文件。把这个文件放在公共文件夹中。

<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.css" rel="stylesheet" />

我们的应用程序现在看起来应该是这样的。

使用Mapbox API进行反向地理编码定位

现在,我们将处理反向地理编码我们的坐标到基于文本的位置。让我们写一个方法来处理这个问题,并通过我们模板中的Get Location 按钮来触发它。

Mapbox的反向地理编码是由反向地理编码API处理的。它接受longitude,latitude, 和access token 作为请求参数。这个调用会返回一个响应的有效载荷--通常,有各种细节。我们关注的是features 数组中的第一个对象,即反向地理编码的位置。

我们需要创建一个函数,将我们想要得到的位置的longitudelatitudeaccess_token 发送给Mapbox API。我们需要发送它们,以便获得该位置的详细信息。

最后,我们需要用对象中place_name 的键值来更新我们实例中的location 属性。

createMap() 函数的下面,让我们添加一个新的函数来处理我们想要的东西。这就是它应该有的样子。

async getLocation() {
  try {
    this.loading = true;
    const response = await axios.get(
      https://api.mapbox.com/geocoding/v5/mapbox.places/${this.center[0]},${this.center[1]}.json?access_token=${this.access_token}
    );
    this.loading = false;
    this.location = response.data.features[0].place_name;
  } catch (err) {
    this.loading = false;
    console.log(err);
  }
},

这个函数向Mapbox API发出一个GET 请求。响应包含place_name ,即所选位置的名称。我们从响应中获得这一信息,然后将其设置为this.location 的值。

完成这些后,我们需要编辑和设置将调用我们所创建的这个函数的按钮。我们将利用一个click 事件监听器--当用户点击它时,它将调用getLocation 方法。继续编辑这个按钮组件。

<button
  type="button"
  :disabled="loading"
  :class="{ disabled: loading }"
  class="location-btn"
  @click="getLocation"
>
  Get Location
</button>

作为锦上添花,让我们附加一个函数将显示的位置复制到剪贴板。把这个函数添加到getLocation 的下面。

copyLocation() {
  if (this.location) {
    navigator.clipboard.writeText(this.location);
    alert("Location Copied")
  }
  return;
},

更新Copy 按钮组件来触发这个。

<button type="button" class="copy-btn" @click="copyLocation">

总结

在本指南中,我们已经了解了使用Mapbox进行地理编码的情况。我们建立了一个地理编码应用程序,将基于文本的位置转化为坐标,在交互式地图上显示位置,并根据用户的要求,将坐标转化为基于文本的位置。这个指南只是一个开始。利用地理编码API可以实现更多的功能,比如利用Mapbox提供的各种地图样式改变地图的表现形式。