使用GeoJson和Javascript构建一个地点定位器
GeoJson是一种使用JavaScript Object Notation格式在数据库中存储地理点的传统方法。由于基于JSON,GeoJson很容易与JavaScript集成。此外,GeoJson允许对地理特征进行编码,如纬度、经度、线型字符串和多边形。
本文将引导读者使用GeoJson来构建一个使用Javascript的地点定位器应用程序。在文章中,我们将开发一个应用程序,将一个地点添加到MongoDB数据库中,对其进行地理编码以获得该地点的纬度、经度和可读地址,然后在地图上显示该地点的经纬度图。
这个项目探索了几个与GeoJson一起工作的API。使用的API是Mapbox,用于在网页上渲染地图,mapquest用于发送位置请求,并将地点的名称地理编码为匹配的经纬度。
前提条件
要跟上这篇文章,你需要具备以下条件。
- 一个代码编辑器。我更喜欢[VS Code],因为它提供了对网络开发有帮助的扩展。
- Mapquest帐户的API密钥。
- 拥有mapquest API密钥的[Mapbox账户]。
- 对Javascript的理解。
- Node.js的工作安装。
项目环境设置
运行npm init -y,创建一个空的package.json
文件来开始。该文件包含开发和运行应用程序所需的所有依赖项。在这个文件中包含依赖项是非常重要的,这样当项目在本地开发环境之外的任何地方执行时,用户只需执行一条命令就可以让项目运行。
接下来,执行下面的命令来安装所需的包。
npm install express cors node-geocoder mongoose dotenv
在成功安装了依赖项之后,我们需要为项目设置一个mongo DB数据库。人们可以按照这些步骤来设置数据库,并获得一个他将用于项目的连接URL。
在下一步,登录或创建一个Mapbox的新账户,以获得一个API密钥,在项目网站上生成地图。然后,应与Mapquest进行同样的程序。
然而,密钥需要在mapquest中是全局性的,而Mapbox的API密钥则带有一个启动代码,我们将在网站文件夹中的JavaScript文件中使用。
Mapbox和Mapquest都是用来绘制位置数据,并分别对地名的纬度和经度进行地理编码。Mapquest确保给定一个地名,就会返回与该地相关的所有信息,包括格式化的地址、纬度、经度、街道名称、邮政编码和国家。然而,我们的项目只需要经纬度,以便在Mapbox提供的地图上定位该地。
文件夹组织
我们将使用MVC开发模式。然而,项目的规模将与之有一些偏差。因此,我们将把我们的模型、控制器和路由分离到不同的文件夹中。此外,我们将有一个网站文件夹,包含在用户界面上渲染数据所需的文件。最后,我们将有一个实用文件夹,包含项目所需的实用文件。
项目的最终文件夹组织如下所示。
developer-locator
┣ config
┣ controllers
┣ models
┣ routes
┣ utilities
┣ website
┗ package.json
上面的每个文件夹都会有我们稍后要讨论的文件。
设置项目服务器
在设置服务器文件之前,我们需要将全局变量存储在一个配置文件中。因此,创建一个名为config.env
的新文件,并在配置文件夹中添加以下代码段。
PORT=5000
MODE=development
MONGO_URI='YOUR MONGO URI'
PROVIDER=mapquest
GEOCODER_API_KEY='YOUR GEOCODER API KEY'
在应用程序的根文件夹中创建一个名为server.js
的新文件。该文件将包含一个项目的所有服务器配置,包括运行的端口、所需的依赖性和全局变量。在server.js
文件中添加以下片段。
const path = require('path');
const express = require('express')
const dotenv = require('dotenv')
const cors = require('cors')
///load environmental variables into the project
dotenv.config({ path: './config/config.env' });
//initialize the application
const app = express()
//uloading the bosy parser middleware
app.use(express.json())
//using cors
app.use(cors())
const PORT = process.env.PORT || 5000;
app.listen(PORT, () =>{
console.log(`Server running in ${process.env.MODE} mode on port ${PORT}`);
})
设置数据库连接
我们需要数据库来存储开发者的信息,包括姓名、地点和在系统中注册的日期。首先,我们需要设置一个数据库连接到我们之前创建的远程mongo DB数据库。在config
文件夹中,创建一个名为database.js
的新文件,并在下面的片段。
const mongoose = require('mongoose');
const connectDatabase = async() => {
try {
const connection = await mongoose.connect(YOUR MONGO URI, {
useUnifiedTopology: true,
});
console.log(`Database connected to ${connection.connection.host}`)
} catch (error) {
console.log(error);
process.exit(1);
}
}
module.exports = connectDatabase;
下一步是在服务器文件中加载数据库连接函数。然后,在server.js
文件中添加以下代码段,以方便数据库连接。
const connectDatabase = require('./config/database')
connectDatabase();
创建开发者模型
一个模型就像一个脚手架,它提供了一种在数据库中表示数据的方式。它指定了一个给定数据元素的所有属性和每个属性的数据类型。
我们将为我们的案例存储开发者的名字、地址、地点和他的注册日期。在models
文件夹中创建一个名为Developer.js
的文件。下面这段代码代表了一个开发商的模式。
const mongoose = require('mongoose');
const DeveloperSchema = new mongoose.Schema(
{
name:{
type: String,
required: [true, 'Please add developer name']
},
address: {
type: String,
required: [true, 'Please add developer address']
},
location: {
type: {
type: String,
enum: ['Point']
},
coordinates: {
type: [Number],
index: '2dsphere'
},
readableAddress: String
},
dateRegistered: {
type: Date,
default: Date.now
}
}
);
在创建了开发者模式之后,我们需要添加一个中间件,该中间件可以获取用户提供的位置字符串,并将其地理编码为纬度、经度和可读地址,然后将其存储在位置对象的相应属性中。
所有这些都是在开发者对象被保存到数据库之前完成的。我们还将this.location
,以防止地理编码的位置数据被保存到数据库中。
//geocode and create location
DeveloperSchema.pre('save', async function(next){
const locate = await geocoder.geocode(this.address);
this.location = {
type: 'Point',
coordinates: [locate[0].latitude, locate[0].longitude],
readableAddress: locate[0].formattedAddress
}
this.address = undefined;
next();
});
为了使上述中间件工作,我们需要添加地理编码工具,包括服务提供者和API密钥。这个工具从我们下载的node-geocoder
依赖关系中获得。
在utilities
文件夹中,创建一个名为geocoder.js
的新文件,然后添加以下代码片段。
const nodeGeocoder = require('node-geocoder');
const options = {
provider: process.env.PROVIDER,
httpAdapter: 'https',
apiKey: process.env.GEOCODER_API_KEY,
formatter: null
};
const geocoder = nodeGeocoder(options);
module.exports = geocoder;
在控制器上工作
控制器决定了如何获得数据并将其输入数据库。我们将有两个方法,分别完成这些功能。首先,在controllers
文件夹中,创建一个名为developer.js
的新文件,然后添加下面的代码片断。
const Developer = require('../models/Developer')
exports.getDevelopers = async (req, res, next) =>{
try {
const developers = await Developer.find();
return res.status(200).json({
data: developers
})
} catch (error) {
res.status(500).json({
error: error,
})
}
}
exports.addDeveloper = async (req, res, next) =>{
try {
const developer = await Developer.create(req.body);
return res.status(200).json({
data: developer
})
} catch (error) {
res.status(500).json({
error: error.message,
})
}
}
将数据传递给路由器
路由是必要的,它可以指定在哪里引导请求,以及URL做获取数据的地方。我们的路由文件从控制器中获取功能,并根据请求应用必要的功能。
我们将实例化一个路由器,并从控制器中传递功能,然后将文件导出到服务器文件中,这是应用程序的入口点。
const express = require('express')
const {getDevelopers, addDeveloper} = require('../controllers/developers')
const router = express.Router()
router.route('/').get(getDevelopers).post(addDeveloper);
module.exports = router
在网站上工作
我们的网站将有一个标题和一个地图区域,显示在一个给定的地方有哪些开发商。
此外,我们将有一个表格,用于将开发人员添加到系统中。该表格将提交数据以调用controller
文件中的addDeveloper
函数。
接下来,我们有一个JavaScript文件,在浏览器上渲染地图。一旦你在Mapbox创建了API密钥,就会提供这个代码片段。
mapboxgl.accessToken = 'yourmapboxapikey;
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11', //,ap style
zoom: 9, //zoom level
center: [ 36.82194619999996, -1.2920659] //the center of the map
})
该代码段指定了缩放级别、地图风格和中心,中心的选择是基于你想在哪里映射出开发者。
获取开发人员并将其显示在地图上
在这一步,我们要收集数据库中的所有开发人员,然后根据具体的经纬度在地图上显示他们。
我们首先需要一个函数来从数据库中获取所有的开发者。接下来,我们将把开发人员转换为一个数组,并对其进行修改以适应Mapbox指定的风格。
async function getDevelopers() {
const res = await fetch('/api/developers');
const data = await res.json();
const developers = data.data.map(developer => {
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
developer.location.coordinates[0], //latitude
developer.location.coordinates[1] // longitude
]
},
properties: {
name: developer.name, //developer name
icon: 'house' //icon to display
}
};
});
loadMap(developers); //load map with developers
}
下面的函数加载地图,将开发者映射到各自的经纬度上。接下来,我们将上述函数中的开发者数组传递给loadMap()
函数。我们还设置了图标的大小,并添加了开发者的名字。
最后,我们调用getDevelopers
函数,该函数调用loadMap
函数。
//Load map with devs
function loadMap(developers) {
map.on('load', function() {
map.addLayer({
id: 'points',
type: 'symbol',
source: {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: developers //add the develpers array
}
},
layout: {
'icon-image': '{icon}-15',
'icon-size': 1.5,
'text-field': '{name}',
'text-anchor': 'top'
}
});
});
}
getDevelopers()
测试应用程序
要运行该应用程序,使用npm start
来启动你的服务器,然后前往你在config.env
文件中指定的端口上的localhost来访问你的网站。
在添加开发人员之前,你的网站应该如下图所示。
然而,在添加了开发人员之后,地图应该如下所示。
结论
本文介绍了如何使用Mapbox与GeoJson来构建一个在地图上定位开发人员的应用程序。我们探讨了如何将位置名称地理编码为经纬度,然后存储在MongoDB数据库中。
然后用Mapbox将存储的开发者的位置钉在地图上。在这个项目中探索GeoJson、Mapbox和Mapquest可以成为使用地图和地理编码的一个很好的起点。