Node.js、MariaDB 和 GIS

MariaDB 的非阻塞客户端库有了 Node.js 绑定,再加上 MariaDB 的 GIS 功能,这激发了我创建一个示例,演示如何使用 Node.js 和 MariaDB 将所谓的 GPX 轨迹导入 MariaDB 数据库,然后在地图上显示它们。GPX 轨迹是许多 GPS 设备(包括跑步手表和智能手机)存储的内容。

我的项目使用了 MariaDB 的非阻塞客户端库以及 Node.js 平台,并且在此基础上使用了 MariaDB 5.5 和 10.0 中提供的 GIS 功能。

首先,让我们回顾一下我使用的软件和组件

  • Node.js – 基于 Chrome V8 JavaScript 引擎构建的流行 Node.js 平台。一个事件驱动和非阻塞的架构。
  • Express.js – 用于 Node.js 的 Web 应用框架(类似于 Ruby 中的 Sinatra)。它为应用程序提供了良好的结构,并使配置变得容易。
  • Jade – 一个 Node.js 模板引擎,在大多数情况下用于输出 HTML,但也可用于其他 XML 结构化语言。
  • node-mariasql – Brian White 创建的 MariaDB 非阻塞(MySQL 兼容)客户端库的 Node.js 绑定。
  • xml2js – 用于解析 XML 的 Node 模块。
  • Google 地图 – 用于在地图上显示最终结果。

我的应用程序的源代码可以在 Github 上找到: https://github.com/rasmushoj/nodejs-gpx-mariadb
我已根据 MIT 许可证授权,因此请随意获取、复制、重用或分发。如果您在此基础上进行构建,当然很高兴知道,如果您能给我发一封电子邮件或在此添加评论,将不胜感激。

安装平台

我选择使用 Ubuntu 13.10 x64 作为应用程序的操作系统。下面概述了安装此应用程序所需每个组件的步骤。在开始之前,最好创建一个新的 OS 用户而不是以 root 身份运行。adduser 对此非常方便。如果您打算使用此新用户进行安装,请记住将用户添加到 sudo 组。在我的例子中,我选择了“sudo adduser rasmus sudo”。

按照MariaDB 下载仓库中找到的说明安装 MariaDB 10.0。 请记住在该页面上选择正确的 Ubuntu 版本,在我的例子中是 13.10 “saucy”。然后按照页面上的说明进行操作。在进行本教程时,安装的 MariaDB 版本是 10.0.7。应用程序使用 MariaDB 的 root 用户,密码为 password1,因此您可能也想这样做,以便在不修改代码的情况下使此示例正常工作。使用以下命令创建一个名为 running 的数据库

mysql -u root -p
MariaDB [(none)]> create database running;

应用程序的 GitHub 仓库中,您会在根目录下找到 running.sql。在服务器上使用以下命令运行它,以创建 running 数据库的模式

mysql -u root -p running < running.sql

准备环境:
更新 apt 仓库: sudo apt-get update
安装编译器: sudo apt-get install build-essential
安装 curl,安装 Node.js 时需要它: sudo apt-get install curl

安装 Node.js 和 Node 包管理器 (NPM):
sudo apt-get install nodejs
sudo apt-get install nodejs-legacy
sudo apt-get install npm

安装 Express.js 和 Jade:
npm install express
npm install jade

安装 MariaDB 非阻塞客户端:
npm install mariasql
(可能会有一些编译器警告,我至少遇到了几个)

最后安装我们需要的 xml2js 模块:
npm install xml2js

完成以上步骤后,平台就设置好了,下一步是运行命令 express nodegpxmariadb 创建一个 Express.js Web 应用程序,其中 nodegpxmariadb 是 Web 应用程序的名称。我在我的用户主目录中执行了此操作。进入 nodegpxmariadb 目录,并运行 npm install 确保所有必需的模块都已安装。

配置应用程序

在 app.js 中指定了一些不同的内容。

列出了所有模块依赖。对于我的应用程序,除了 Express.js 设置的常规模块外,还添加了 data.js 和 common.js 模块,我在其中处理了所有数据库操作。通过添加以下行定义模块依赖:

var common = require('./routes/common');
var data = require('./data.js');

现在 common 和 data 模块在整个应用程序中都可用。Common 遵循 Express.js 框架的原则,并被实现为一个所谓的路由,其中包括下面提到的 URL 路径映射的逻辑。

正在使用默认的 Express.js 环境配置,例如服务器运行在端口 3000 上。确保 JSON 支持已开启。默认情况下应该是开启的。

app.use(express.json());

另一个需要的配置是告诉 Express.js 将上传的文件存储在哪里

app.use(express.bodyParser({ keepExtensions: true, uploadDir: __dirname + '/public/uploads' }));

此外,app.js 是定义所有 URL 路径映射的地方。需要以下映射:

app.get('/upload', common.fileForm);
app.post('/upload', common.fileUpload);
app.get('/readfile', common.readFile);
app.get('/parsegpx', common.parseGPX);
app.get('/track', common.listPoints);
app.get('/showmap', function (req, res) {
    res.sendfile(__dirname + '/views/maps.html');
});

如您所见,大多数 URL 路径映射指向 common 模块,其中定义了当接收到 URL 路径请求时要做什么。对于 /showmap,不需要服务器端逻辑,因此可以直接读取 HTML 文件并发送到客户端。

启动应用程序

配置完成后,就可以启动应用程序了

rasmus@rasdo1:~$ cd nodegpxmariadb/
rasmus@rasdo1:~/nodegpxmariadb$ node app
connect.multipart() will be removed in connect 3.0
visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives
connect.limit() will be removed in connect 3.0
Express server listening on port 3000

如果您在浏览器中打开 http://[server ip]:3000,您应该会看到“Welcome to Express”消息。

上传 GPX 文件

让我们仔细看看 common.js 模块中实际做了什么。从上传部分开始。Exports.fileForm 和 exports.fileUpload 分别处理上传表单的显示和实际执行用户选择的文件上传。

node_upload
截图 1:文件上传


解析 GPX 文件

文件上传后,事情变得更有趣了。这时就开始解析 GPX 文件。GPX 文件充满了具有特定间隔的跟踪点。例如:

<trkpt lon="24.8374462127686009" lat="60.1847839355469034">
    <ele>4.4632568359375</ele>
    <time>2013-09-21T12:00:11.000Z</time>
    <extensions>
        <gpxtpx:TrackPointExtension>
            <gpxtpx:hr>137</gpxtpx:hr>
        </gpxtpx:TrackPointExtension>
    </extensions>
</trkpt>
<trkpt lon="24.8374595642089986" lat="60.1846923828125000">
    <ele>4.4632568359375</ele>
    <time>2013-09-21T12:00:14.000Z</time>
    <extensions>
        <gpxtpx:TrackPointExtension>
            <gpxtpx:hr>146</gpxtpx:hr>
        </gpxtpx:TrackPointExtension>
    </extensions>
</trkpt>

如上所示,每个跟踪点都是一个独立的 XML 节点,其中包含经度和纬度坐标、海拔、时间和一些其他有趣的信息,如心率。处理这类 XML(或一般 XML)的一种简单方法是使用 node.js 库 xml2js,它可以解析 XML 并输出 JSON。这非常直接。我在 parseGPX 函数中使用了它。

当我们将 GPX 内容转换为 JSON 格式后,就可以提取跟踪点并将它们插入 MariaDB 数据库了。我们首先调用 data 模块的 connect 方法来获取与 MariaDB 的连接。之后,我们遍历 JSON 内容:

for(var i in json.gpx.trk[0].trkseg[0].trkpt) {
     lon = json.gpx.trk[0].trkseg[0].trkpt[i].$.lon;
     lat = json.gpx.trk[0].trkseg[0].trkpt[i].$.lat;
     data.insertPoint(lon, lat);
}

变量 lon 保存跟踪点的经度坐标,lat 保存纬度。不幸的是,MariaDB 尚未支持 GIS 中的第三个坐标——海拔,否则我们也可以添加它。lon 和 lat 被传递给 data.insertPoint 方法,该方法将它们作为 GIS 点存储在数据库中。

client.query('INSERT INTO trackpoints (gpsPoint) VALUES (PointFromText(:loc))', { loc: 'POINT(' + lon + " " + lat +  ')' })

请务必查看 data.js 文件,了解如何连接和断开与 MariaDB 的连接。

Screenshot 2: Parsing the GPX file
截图 2:解析 GPX 文件


在地图上显示

我想做的最后一件事是看看我可以多容易地在地图上绘制存储的 GIS 点。首先,我们需要从数据库中检索所有 GIS 点,这在 data.js 中以下列方式完成:

exports.getTrackPoints = function(trackId, httpRes) {
   var xys = [];

   client.query('SELECT X(GeomFromText(AsText(gpsPoint))) AS X, Y(GeomFromText(AsText(gpsPoint))) AS Y FROM trackpoints WHERE trackId = :pTrackId', { pTrackId: trackId })
       .on('result', function(res) {
           res.on('row', function(row) {
               xys.push(row);
           })
               .on('error', function(err) {
                   console.log('Result error: ' + inspect(err));
               })
               .on('end', function(info) {
                   console.log('Result finished successfully');
               });
       })
       .on('end', function() {
           httpRes.send(xys);
       });
}

从上面可以看出,构建了一个数组,它直接作为 HTTP 响应传递,当然默认是 JSON 格式的。这对 Google 地图非常有用,它现在可以指向一个 URL(在本例中是 /track),从那里读取 JSON 格式的点数组,并绘制一条连接每个点的所谓折线,形成一条漂亮的轨迹。

Screenshot 3: The GIS points shown as a polyline on Google Maps
截图 3:GIS 点在 Google 地图上显示为折线

将 GIS 点存储在数据库中后,当然可以做更多的事情,包括:

  • 可以计算距离
  • 我可以查看我的哪些跑步路线相互交叉(好吧,这实际上是 MariaDB 10.1 的一个特性,因为 INTERSECT 将在那里引入)
  • 计算我的跑步路线有多少位于特定区域内
  • 可以添加时间以测量持续时间和配速

总而言之,我想说的是,无论何时您考虑创建下一个应用程序,无论是 Web 应用程序还是需要后端的智能手机应用程序,Node.js + MariaDB 都是一个不错的选择,并且所有需要的组件都可用。您将拥有一个功能齐全且经过验证的 RDBMS 来支持您的应用程序。

此外,位置感知和其他地理功能在新开发中似乎几乎是强制性的。MariaDB 包含一套相当不错(且实现得当)的空间数据类型、GIS 函数和其他相关功能。