本文是与WRLD合作创建的。 感谢您支持使SitePoint成为可能的合作伙伴。
作为Web开发人员,有时您会发现自己处于实施地图的位置。 您的首选是使用Google地图,对吗?
看起来还好 但是,可能需要您借助标记在地图上叠加其他信息。 您可以使用此方法,也可以找到更好的解决方案,使您可以在室内3D地图中创建标记! 多么酷啊? 使用室内标记,您可以为用户提供独特的体验,使他们能够访问信息并与地图内的UI进行交互。
在本教程中,我们将创建两个演示,演示WRLD映射的功能 。 您将学习如何创建可在3D地图上叠加实时信息的自定义应用。 在第一个演示中,我们将交互式标记添加到购物中心的现有室内地图中。 在第二个演示中,我们将在停车场上方放置彩色多边形,以指示容量。
您可以在此GitHub存储库中找到两个演示的完成项目。
先决条件
对于本文,您只需要对以下主题有基本的了解:
我假设这是您第一次使用WRLD映射。 但是,我建议您至少快速阅读该文章:
您还需要在系统上安装Node.js和npm的最新版本(在撰写本文时,8.10 LTS是最新的稳定版本)。 对于Windows用户,我强烈建议您使用Git Bash或任何其他能够处理基本Linux命令的终端。
本教程将使用yarn进行软件包安装。 如果您更喜欢使用npm
,如果您不熟悉yarn命令,请参考本指南 。
获取API密钥
在开始之前,您需要在WRLD上创建一个免费帐户。 登录并验证电子邮件地址后,您需要获取一个API密钥。 有关如何获取地图的详细说明,请查看“ Building Dynamic 3D Maps”中的“ 入门”部分,该文件有据可查。
构造地图的方法
WRLD地图的创建是一项重大技术成就,对许多行业都具有巨大的潜在利益。 扩展平台功能的主要方法有两种:
- 使用内置工具,例如“地图设计器”和“位置设计器”
- 构建自定义应用
让我分解一下如何使用每种方法来获得期望的结果。
1.使用地图设计器和位置设计器
对于我们的第一个演示,我们可以使用Places Designer创建商店卡。 这将要求我们创建一个Collection Set
,其中将保存所有Point of Interest
点标记。 既可以在WRLD生态系统中访问此集,也可以通过API密钥从外部访问此集。 我们可以将此数据传递给使用Map Designer创建的自定义地图。 使用此工具,我们可以使用其生成的链接与他人共享地图。 如果您想了解更多有关此过程的信息,请观看此YouTube播放列表上的视频教程。
这种方法的优点是不需要编码。 但是,就我们而言,它确实有局限性:
- 受限的用户界面设计–我们只能使用Places Designer随附的用户界面
- 限制性数据集–我们无法显示超出提供范围的其他信息
为了克服这些限制,我们需要使用第二种方法来应对我们的购物中心地图挑战。
2.构建自定义应用
构建自定义应用程序是最灵活的选择。 尽管需要一些编码工作,但它确实使我们能够全面利用WRLD平台提供的丰富潜力。 通过构建自定义应用程序,我们可以创建自己的UI,添加更多字段并实时访问外部数据库。 这是我们在本教程中将使用的方法。
构建应用
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
首先创建一个基本地图,稍后再向其中添加更多功能。 转到您的工作区目录并为您的项目创建一个新文件夹。 我们称其为mall-map
。
在代码编辑器中打开mall-map
文件夹。 如果您具有VSCode,请使用Ctrl +`访问终端并在项目目录中执行以下命令:
# Initialize package.json
npm init -f
# Create project directories
mkdir src
mkdir src/js src/css
# Create project files
touch src/index.html
touch src/js/app.js
touch src/css/app.css
touch env.js
这是您的项目结构的外观:
现在我们已经有了项目结构,可以开始编写代码了。 我们将从index.html
开始。 插入以下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="./css/app.css" />
<title>Shopping Mall</title>
</head>
<body>
<div id="map"></div>
<script src="js/app.js"></script>
</body>
</html>
接下来,让我们在css/app.css
上工作。 我正在为整个项目提供完整的样式,这样我们就不必再次访问此文件。 在适当的时候,您将随着教程的进行而了解其中的内容。
@import "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.1/leaflet.css";
@import "https://cdn-webgl.wrld3d.com/wrldjs/addons/resources/latest/css/wrld.css";
@import "https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.css";
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#map {
width: 100%;
height: 100%;
background-color: #000000;
}
/* -------- POPUP CONTENT -------- */
.main-wrapper > .segment {
padding: 0px;
width: 300px;
}
.contacts > span {
display: block;
padding-top: 5px;
}
现在我们需要开始为app.js
编写代码。 但是,我们需要几个节点依赖项:
yarn add wrld.js axios
如前所述,我们将利用现代JavaScript语法来编写代码。 因此,我们需要使用babel
将我们的现代代码编译为与大多数浏览器兼容的格式。 这需要安装babel
依赖项并通过.babelrc
文件进行配置。 确保将它们安装为dev-dependencies 。
yarn add babel-core babel-plugin-transform-runtime babel-runtime --dev
touch .babelrc
将此代码复制到.babelrc
文件中:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false,
"regenerator": true
}
]
]
}
我们还需要以下软件包来运行我们的项目:
像这样全局安装软件包:
yarn global add parcel-bundler json-server
# Alternative command for npm users
npm install -g parcel-bundler json-server
这就是我们项目所需的所有节点依赖关系。 现在让我们编写一些JavaScript代码。 首先,在env.js
提供您的WRLD API密钥:
module.exports = {
WRLD_KEY: '<put api key here>',
};
然后打开js/app.js
并复制以下代码:
const Wrld = require('wrld.js');
const env = require('../../env');
const keys = {
wrld: env.WRLD_KEY,
};
window.addEventListener('load', async () => {
const map = await Wrld.map('map', keys.wrld, {
center: [56.459733, -2.973371],
zoom: 17,
indoorsEnabled: true,
});
});
前三个语句非常明显。 我们已将所有代码放入window.addEventListener
函数中。 这是为了确保我们的代码在JavaScript依赖项之后执行,这将在稍后在index.html
指定。 在此函数内部,我们通过传递几个参数来初始化地图:
-
map
–我们在index.html
指定的div容器的ID -
keys.wrld
– API密钥 -
center
–位于苏格兰邓迪的Overgate购物中心的经度和纬度 -
zoom
–高程 -
indoorsEnabled
–允许用户访问室内地图
让我们启动我们的项目。 转到终端并执行:
parcel src/index.html
等待几秒钟,项目完成捆绑。 完成后,打开浏览器并访问localhost:1234 。 根据您的互联网速度,地图加载时间不会太长。
美丽,不是吗? 随时单击蓝色图标。 它将带您进入室内。 导航以查看其他商店。 但是,您很快就会意识到无法访问其他楼层。 也没有退出室内地图的按钮。 让我们在下一章中进行修复。
创建室内控件
为了允许用户在不同楼层之间切换,我们将为他们提供一个控件,使他们能够执行此操作。 只需将以下脚本添加到public/index.html
文件的开头部分:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdn-webgl.wrld3d.com/wrldjs/addons/indoor_control/latest/indoor_control.js"></script>
仍在html文件中,在正文部分中的#map
div之前添加此div:
<div id="widget-container" class="wrld-widget-container"></div>
现在,让我们更新js/app.js
来初始化小部件。 将此代码放在地图初始化部分之后:
const indoorControl = new WrldIndoorControl('widget-container', map);
现在刷新页面,然后单击“进入室内”图标。 您应该具有一个控件小部件,该控件可让您在楼层之间切换。 只需上下拖动控件即可在地板之间流畅地移动。
太神奇了,不是吗? 现在,让我们看看如何使我们的地图对用户更方便。
自动进入室内
您难道不觉得每次我们需要测试地图时都需要单击“室内”图标有点烦吗? 用户可能会开始导航到其他位置,而这并不是此应用程序的意图。 要解决此问题,我们需要在应用启动时自动在室内导航,而无需任何用户交互。 首先,我们需要indoor map id
来实现此功能。 我们可以从indoormapenter
事件中找到此信息。 您可以在此处找到所有与室内相关的方法。
在js/app.js
文件中添加以下代码。
...
// Place this code right after the Wrld.map() statement
map.indoors.on('indoormapenter', async (event) => {
console.log(event.indoorMap.getIndoorMapId());
});
...
刷新页面,然后检出控制台。 您应该将此ID打印出来: EIM-e16a94b1-f64f-41ed-a3c6-8397d9cfe607
。 现在让我们编写将执行实际导航的代码:
const indoorMapId = 'EIM-e16a94b1-f64f-41ed-a3c6-8397d9cfe607';
map.on('initialstreamingcomplete', () => {
map.indoors.enter(indoorMapId);
});
保存文件后,刷新页面,看看会发生什么。
室内购物中心地图应自动导航。 接下来,我们将研究如何为每个商店创建卡片。 但是首先,我们需要确定从何处获取数据。
购物中心地图规划
要为我们的地图创建商店卡,我们需要以下几项:
- 商店的确切经度/纬度坐标
- 存储联系方式和营业时间
- 商店卡的设计模板
商店卡座标
要获取经度/纬度坐标,我们需要访问maps.wrld3d.com 。 等待地图完成加载,然后在搜索框中输入地址56.459733, -2.973371
。 按Enter键,地图将快速导航到Overgate Mall。 单击Overgate购物中心的蓝色室内图标,您将被带到购物中心的室内地图。 加载后,找到“ Next”存储,然后右键单击以打开上下文菜单。 点击“这是什么地方? 选项。 坐标弹出窗口应出现。
点击“复制坐标”按钮。 这将为您提供商店的确切经度/纬度坐标。 将此位置地址临时保存在某处。
商店卡信息
您还需要从每个商店收集联系信息,包括:
- 图片
- 描述
- 电话
- 电子邮件
- 网路
- 推特
- 营业时间
您可以从Google获得大部分此类信息。 幸运的是,我已经为您收集了数据。 在本教程中,我们仅涉及一楼的四个商店。 要访问该信息,只需在项目的根目录下创建一个文件夹并将其命名为data。 接下来, 将此文件从GitHub保存到data
文件夹中。 确保将其另存为db.json
。 这是我们将使用的数据示例:
{
"id":1,
"title": "JD Sports",
"lat": 56.4593425,
"long": -2.9741433,
"floor_id": 0,
"image_url": "https://cdn-03.belfasttelegraph.co.uk/business/news/...image.jpg",
"description":"Retail chain specialising in training shoes, sportswear & accessories.",
"phone": "+44 138 221 4545",
"email": "customercare@jdsports.co.uk",
"web": "https://www.jdsports.co.uk/",
"twitter": "@jdhelpteam",
"tags": "sports shopping",
"open_time":[
{ "day": "Mon",
"time": "9:30am - 6:00pm"
},]
}
数据存储在标记为“ pois”的数组中。 POI代表景点。 现在我们有了可用的数据,我们可以通过运行JSON服务器轻松地通过API REST点对其进行访问。 只需打开一个新终端并执行以下命令:
json-server --watch data/db.json
API可能需要几秒钟才能启动。 完全加载后,您可以使用浏览器在localhost:3000 / pois对其进行测试 。 您还可以使用以下语法获取单个POI:
- localhost:3000/pois/{id}
例如, localhost:3000 / pois / 3应该以JSON格式返回ID为3的poi
记录。
商店卡设计
我们将使用简洁优雅的主题,通过几个标签来整齐地显示联系信息和开放时间。 我们将创建一个标记,当单击该标记时将显示一个弹出窗口。 该弹出窗口将具有以下UI。
用于此HTML设计的代码在这里有点长。 您可以从此链接查看和下载文件。 该设计仅具有三个依赖性:
- 语义UI CSS
- jQuery的
- 语义UI JS
现在我们有了所需的数据和设计,现在应该准备开始在室内地图上工作了。
在室内地图中实施商店卡
首先,让我们创建一个服务,该服务将允许我们从JSON REST API访问数据。 该数据将用于用必要的信息填充存储卡。 创建文件js/api-service.js
并复制以下代码:
const axios = require('axios');
const client = axios.create({
baseURL: 'http://127.0.0.1:3000',
timeout: 1000,
});
module.exports = {
getPOIs: async () => {
try {
const response = await client.get('/pois');
return response.data;
} catch (error) {
console.error(error);
}
return [];
},
getPOI: async (id) => {
try {
const response = await client.get(`/pois/${id}`);
return response.data;
} catch (error) {
console.error(error);
}
return {};
},
}
在这里,我们利用axios库从JSON服务器请求数据。
接下来,我们将存储卡的静态HTML设计转换为允许我们呈现数据的格式。 我们将为此使用JsRender 。 我们将静态设计分为三个模板:
- 基本模板–具有用于菜单,信息和时间选项卡的容器。
- 信息模板–用于存储联系信息的选项卡。
- 时间模板–商店营业时间的标签。
首先,打开index.html
并将这些脚本添加到head
,紧随jQuery和室内控制脚本之后:
<head>
...
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.90/jsrender.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js"></script>
...
</head>
接下来,将这段代码复制到widget-container
div之前:
...
<!-- Menu Tabs UI -->
<script id="baseTemplate" type="text/x-jsrender">
<div class="main-wrapper">
<div class="ui compact basic segment">
<div class="ui menu tabular"> </div>
<div id="infoTab" class="ui tab active" data-tab="Info"></div>
<div id="timeTab" class="ui tab" data-tab="Time"></div>
</div>
</div>
</script>
<!-- Info Data Tab -->
<script id="infoTemplate" type="text/x-jsrender">
<div class="ui card">
<div class="image">
<img src={{:image_url}}>
</div>
<div class="content">
<div class="header">{{:title}}</div>
<div class="description">
{{:description}}
</div>
</div>
<div class="extra content contacts">
<span>
<i class="globe icon"></i>
<a href="{{:web}}" target="_blank">{{:web}}</a>
</span>
<span>
<i class="mail icon"></i>
{{:email}}
</span>
<span>
<i class="phone icon"></i>
{{:phone}}
</span>
</div>
</div>
</script>
<!-- Opening Times Data Tab -->
<script id="timeTemplate" type="text/x-jsrender">
<table class="ui celled table">
<thead>
<tr>
<th>Day</th>
<th>Time</th>
</tr>
</thead>
<tbody>
{{for open_time}}
<tr>
<td>{{:day}}</td>
<td>{{:time}}</td>
</tr>
{{/for}}
</tbody>
</table>
</script>
...
这就是index.html的完整代码的外观。
接下来,让我们创建另一个服务,该服务将管理Popups
的创建。 创建文件js/popup-service.js
并复制以下代码:
const Wrld = require('wrld.js');
const { getPOI } = require('./api-service');
const baseTemplate = $.templates('#baseTemplate');
const infoTemplate = $.templates('#infoTemplate');
const timeTemplate = $.templates('#timeTemplate');
const popupOptions = {
indoorMapId: 'EIM-e16a94b1-f64f-41ed-a3c6-8397d9cfe607',
indoorMapFloorIndex: 0,
autoClose: true,
closeOnClick: true,
elevation: 5,
};
让我逐步解释每个块:
- 块1:创建
Popup
getPOI
需要getPOI
,获取数据需要getPOI
函数 - 块2:我们先前讨论的模板是使用
jsrender
加载的 - 块3:在
Popup
实例化期间将传递的参数。 这是参考文档 。
接下来,让我们添加用于切换标签的标签菜单。 只需将此代码添加到js/popup-service.js
:
const createMenuLink = (linkName, iconClass) => {
const link = document.createElement('a');
link.className = 'item';
const icon = document.createElement('i');
icon.className = `${iconClass} icon`;
link.appendChild(icon);
link.appendChild(document.createTextNode(` ${linkName}`));
link.setAttribute('data-tab', linkName);
link.addEventListener('click', () => {
$.tab('change tab', linkName);
$('.item').toggleClass('active');
});
return link;
};
const createMenu = (menuParent) => {
const infoLink = createMenuLink('Info', 'info circle');
infoLink.className += ' active';
menuParent.appendChild(infoLink);
const timeLink = createMenuLink('Time', 'clock');
menuParent.appendChild(timeLink);
};
您可能想知道为什么我们使用复杂的方法来创建菜单链接。 理想情况下,我们应该能够使用HTML创建它们,然后添加一个小JavaScript脚本来激活选项卡。 不幸的是,这在Popup
的上下文中不起作用。 相反,我们需要使用DOM操作方法来创建可点击的元素。
接下来,添加以下代码以完成基本内容部分:
const buildBaseContent = () => {
const htmlOutput = baseTemplate.render({});
const parent = $.parseHTML(htmlOutput)[1];
const menuParent = parent.childNodes[1].childNodes[1];
createMenu(menuParent);
return parent;
};
const baseContent = buildBaseContent();
在这里,我们将基本模板渲染为HTML。 然后,将其转换为DOM,以使我们能够附加DOM菜单。 然后,我们调出buildBaseContent()
函数来创建基本DOM,稍后将其附加到info和time选项卡的内容。
在下一节中,我们将创建一个名为showPopup
的函数。 稍后,我们将为每个商店创建标记。 当用户单击标记时,将出现一个包含存储卡的弹出窗口。 将此代码添加到js/popup-service.js
:
// Clear existing tab content before adding another
const clearTab = (tab) => {
while (tab.firstChild) {
tab.removeChild(tab.firstChild);
}
};
module.exports = {
showPopup: async (event) => {
// Fetch co-ordinates and map objects from event
const latlang = event.target._latlng;
const map = event.target._map;
// Create an instance of Popup
const popup = Wrld.popup(popupOptions)
.setLatLng(latlang);
try {
// Fetch data from api-service
const poi = await getPOI(event.target.options.id);
// Bind data with templates to render html outputs
const infoHTML = infoTemplate.render(poi);
const timeHTML = timeTemplate.render(poi);
// Convert HTML outputs to DOM objects
const infoDOM = $.parseHTML(infoHTML)[1];
const timeDOM = $.parseHTML(timeHTML)[1];
// Populate Tabs with DOM objects
const infoTab = baseContent.childNodes[1].childNodes[3];
clearTab(infoTab); // Clear existing content if any
infoTab.appendChild(infoDOM);
const timeTab = baseContent.childNodes[1].childNodes[5];
clearTab(timeTab); // Clear existing content if any
timeTab.appendChild(timeDOM);
// Populate popup with DOM content
popup.setContent(baseContent);
// Display the popup
popup.addTo(map);
// Navigate map to properly view the Popup
map.setView(latlang, 18);
} catch (error) {
popup.setContent('Oops! Something went wrong');
popup.addTo(map);
}
},
};
这里有很多事情。 我在代码中插入了注释,解释了每个部分的功能。 如果您对完整代码的外观有任何疑问,可以从此链接查看。
接下来,我们需要为db.json
定义的每个POI创建标记 。 每个标记都有一个click
事件监听器,它将触发showPopup()
函数。 更新js/app.js
如下:
..
const { getPOIs } = require('./api-service');
const { showPopup } = require('./popup-service');
...
// Place within window.addEventListener('load')
const placeMarkers = (pois) => {
let marker;
pois.forEach((poi) => {
const latlang = [poi.lat, poi.long];
marker = Wrld.marker(latlang, {
id: poi.id,
title: poi.title,
indoorMapId,
indoorMapFloorId: 1,
}).addTo(map);
marker.on('click', showPopup);
});
};
map.indoors.on('indoormapenter', async (event) => {
if (event.indoorMap.getIndoorMapId() === indoorMapId) {
// Center map properly when indoors
map.indoors.setFloor(0);
map.setView([56.459342, -2.9741433], 18);
// Create markers for each store.
const pois = await getPOIs();
placeMarkers(pois);
}
});
请注意,我们正在通过Options对象参数将POI ID传递给标记。 如果回头看showPopup
函数,您会看到我们正在通过event
对象提取此ID。 如果不确定完整代码的外观,请从此链接查看。
现在是时候测试我们的代码了。 我假设您仍然在后台运行JSON服务器。 如果没有,请重新参考如何运行它。 让我们也启动包裹打包器。 启动后,请刷新浏览器(如果尚未启动)。 现在,您应该可以单击多个标记。 单击一个标记将导致如下所示的弹出窗口:
上面的演示说明了存储卡如何在室内地图上工作。 现在,让我们看一下Wrld.js的另一个功能,在该功能中,我们可以在多个停车区域上叠加停车可用性信息。
停车位
您是否曾经压力过寻找停车场? 好吧,让我们尝试看看是否可以解决此问题。 我们将使用WRLD地图突出显示停车区。 我们将使用不同的颜色指示每个停车区的状态:
- 绿色:有停车位
- 黄色:已占用80%的停车位
- 红色:已占用停车位的100%
当然,您可以定义更多的颜色代码以提供更详细的停车位级别。 但是,请记住,人们可能很着急,他们需要在几毫秒内处理这些信息。 让我们一次一次创建此地图。
1.停车场的位置图
让我们从创建parking.html
和js/parking.js
。 我们将独立于购物中心地图逻辑来运行此解决方案。 创建HTML文件后,复制以下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="./css/app.css" />
<title>Parking Availability</title>
</head>
<body>
<div id="map"></div>
<script src="js/parking.js"></script>
</body>
</html>
接下来,将此代码复制到js/parking.js
:
const Wrld = require('wrld.js');
const env = require('../../env');
const keys = {
wrld: env.WRLD_KEY,
};
window.addEventListener('load', async () => {
// Create map instance
const map = await Wrld.map('map', keys.wrld, {
center: [56.460087, -2.975432],
zoom: 17.5,
});
});
现在运行我们的代码。 我们将为此使用parcel
。 停止现有的parcel
情况下先用Ctrl + C.要启动停车演示,执行:
parcel src/parking.html
等待包裹完成编译。 完成后,导航到localhost:1234 。 您应该对停车场有以下看法:
2.突出停车
现在让我们学习如何使用Polygon实例突出显示区域。 首先,我们需要为需要突出显示的区域的每个角收集坐标。 我们可以通过访问maps.wrld3d.com并搜索Overgate来找到停车场。 朝停车处放大,并使用鼠标中键调整相机的倾斜度,以便可以直下观看。 这将使准确地单击鼠标变得容易。 接下来,选择一个停车区,然后右键单击任何一个角落。 点击“这是什么地方?”:
单击复制坐标并将其保存在某处。 您应该获得单击的点的经度和纬度坐标。
- 56.460080, -2.974528
对每个角落执行此操作。 接下来,使用此数据来构造Polygon
实例。 这是已添加到js/parking.js
的示例。 将此代码放在map
初始化语句之后。
const polygonPoints1 = [
[56.459857, -2.974004],
[56.459889, -2.974036],
[56.459836, -2.974188],
[56.460079, -2.974526],
[56.460254, -2.974096],
[56.459954, -2.973698]];
const parking1 = Wrld.polygon(polygonPoints1).addTo(map);
如果没有,请刷新localhost:1234 。 您现在应该具有以下视图:
现在我们已经学会了突出显示,我们应该收集我们感兴趣的每个停车区的坐标。我们还需要一种使这些数据与我们接下来将要研究的代码保持距离的方法。 但是首先,请删除此部分代码,因为我们将用更好的代码替换它。
3.用于停车数据的Rest API
我们将利用JSON服务器数据库来存储所有停车坐标。 幸运的是,我已经收集了这些数据并将它们放在db.json
。 这是一个停车区的数据示例:
{
"id": 1,
"name" : "parking 1",
"polygonPoints": [
[
56.459837,
-2.973982
],
[
56.459952,
-2.973691
],
[
56.460256,
-2.974093
],
[
56.460079,
-2.974530
],
[
56.459832,
-2.974188
],
[
56.459888,
-2.974035
]
],
"totalSlots": 55,
"usedSlots": 55
},
请注意,对可用停车位的总数有一个粗略的估计。 我还对已使用的停车位进行了猜测,稍后我们将对其进行探讨。 您之前复制的db.json
文件已经具有此数据。 现在我们有了可用的停车区数据,我们应该创建一个帮助服务来获取此信息。 我们只需要使用新功能更新js/api-service
。 复制此代码并将其放在module.exports
最后一个get
函数module.exports
:
getParkingAreas: async () => {
try {
const url = id ? `/parkingAreas/${id}` : '/parkingAreas';
const response = await client.get(url);
return response.data;
} catch (error) {
console.error(error);
}
return [];
},
此功能旨在处理是否提取所有“停车区”记录,或仅提取一条记录,具体取决于是否填充了ID字段。 现在让我们看一下如何从JSON服务器提取此数据并将其覆盖在地图上。
3.彩色编码停车区
使用这些颜色代码更新js/parking.js
。 将此代码放在keys
声明之后。
// Color Codes
const fullColor = [255, 0, 0, 128]; // Completely full, 100%
const almostColor = [255, 165, 0, 128]; // Few parking slots left, 80% full
const availableColor = [0, 255, 0, 128]; // Plenty of parking space available
const getColorCode = (parkingArea) => {
const occupied = (parkingArea.usedSlots / parkingArea.totalSlots) * 100;
if (occupied === 100) {
return fullColor;
} else if (occupied >= 80) {
return almostColor;
}
return availableColor;
};
颜色代码只是表示rgba值的数组,即红色,绿色,蓝色和alpha。 还有一个getColorCode
函数,用于根据已用插槽的百分比确定要使用的颜色代码。 接下来,让我们从JSON服务器中提取Parking Areas数据,并为每条记录创建一个Polygon实例:
// Place this at the top after other imports
const { getParkingAreas } = require('./api-service');
const parkPolys = [];
....
// Place after `map` function
map.on('initialstreamingcomplete', async () => {
// Highlight Parking Areas
const parkingAreas = await getParkingAreas();
parkingAreas.forEach((parkingArea) => {
const colorCode = getColorCode(parkingArea);
const poly = Wrld.polygon(parkingArea.polygonPoints, { color: colorCode })
.addTo(map);
parkPolys.push({ id: parkingArea.id, poly });
});
});
...
请注意,我们将多边形和parkingArea.id
的关联保存在数组中。 我们稍后将对此进行处理,以使我们的地图实时。 确保JSON服务器正在运行,此代码才能正常工作。 现在,刷新页面以查看更新的结果:
很酷,不是吗? 随意添加颜色代码标签以指示其含义。 现在,地图的当前限制是,除非刷新整个页面,否则用户看不到地图更新。 让我们看看如何解决这个问题。
4.实时停车区
为此,我们将使用sockets.io
库来实现实时更新。 我们正在使用的json server
程序本身不支持sockets.io
。 因此,我们需要编写自己的自定义实现。 首先,让我们安装必要的依赖项:
yarn add json-server socket.io socket.io-client
接下来,在项目的根目录下创建文件server.js
并复制以下代码:
const jsonServer = require('json-server');
// Initialize Socket.IO Server
const socketServer = require('http').createServer();
const io = require('socket.io')(socketServer);
// Initialize JSON Server
const server = jsonServer.create();
const router = jsonServer.router('./data/db.json');
// Set default middlewares (logger, static, cors and no-cache)
const middlewares = jsonServer.defaults();
server.use(middlewares);
// To handle POST, PUT and PATCH you need to use a body-parser
// You can use the one used by JSON Server
server.use(jsonServer.bodyParser);
// Broadcast `parkingAreas` PATCH requests
server.patch('/parkingAreas/:id', (req, res, next) => {
const { id } = req.params;
const { usedSlots } = req.body;
console.log(`Parking Area ${id} updated to ${usedSlots} Used Slots`);
io.emit('parkingAreas', { id, usedSlots });
next(); // pass on to default logic
});
// Use default router
server.use(router);
// Bind JSON Server
server.listen(3000, () => {
console.log('JSON Server is running at port 3000');
});
// Bind Socket.IO Server
socketServer.listen(3001, () => {
console.log('Socket.IO Server is running at port 3001');
});
在上面的代码中,我们正在设置两个将同时运行的服务器实例。 第一个实例json server
将在端口3000上提供API服务。第二个实例socket server
将向将在端口3001连接到它的套接字客户端提供实时的信息。
在本文中,我们将使用Postman发送每个停车场的容量级别( usedSlots
)更新。 我们将使用的HTTP方法是PATCH,这将允许我们仅更新记录的一部分。 我们不能使用UPDATE方法,因为它将覆盖整个记录,从而导致我们丢失多边形点数据。
回到我们的服务器代码,您会注意到我们有一个patch
功能。 在此函数中,将提取id
和usedSlots
数据,然后将其广播到任何侦听的socket.io客户端。
现在我们已经设置了服务器,是时候设置客户端代码以接收实时更新了。 返回js/parking.js
并复制以下代码:
// Place this at the top section
const io = require('socket.io-client');
...
// Place after `getColorCode` function
const updateParkingArea = async ({ id }) => {
const parkingArea = await getParkingAreas(id);
if (parkingArea) {
const parkPoly = parkPolys.find(target => parkingArea.id === target.id);
if (parkPoly) {
parkPoly.poly.setColor(getColorCode(parkingArea));
}
}
};
const socket = io.connect('http://localhost:3001');
socket.on('connect', () => {
console.log('connected to socket 3001');
socket.on('parkingAreas', (data) => {
console.log('parkingAreas event received');
updateParkingArea(data);
});
});
客户端代码非常简单。 我们创建绑定到端口3001的套接字客户端实例。然后将其设置为侦听parkingAreas
事件,然后执行updateParkingArea()
函数。
如果对代码的排列方式有疑问,请参阅完整的parking.js 。
现在让我们做一个实验。 首先,您需要取消任何现有过程。 接下来,在单独的终端上启动定制JSON服务器代码。 然后在另一个终端上启动parking.html
代码:
# Start server first
node server
# Start Parking Map App
parcel src/parking.html
现在打开或刷新页面localhost:1234 。 要将更新发送到JSON服务器,我们将使用Postman 。 如果没有它,只需安装它。 打开后,创建一个新的Request并输入以下参数:
- 方法 –补丁
- 网址 –本地主机:3000 / parkingAreas / 2
- 内容类型 – application / json
- 编码 -原始JSON(应用程序/ json)
- 身体 -
{ "usedSlots": 75 }
如果您不知道“ Content-Type
”字段在哪里,只需单击“页眉”选项卡。 这是邮递员的屏幕截图:
当您点击发送时,地图上的更新会立即发生:
随意使用usedSlots
的值处理其他记录,并查看地图更新本身。 相当辉煌!
摘要
既然我们已经完成了本教程的结尾,我希望您对WRLD的3D地图可以做的事情感到惊讶。 这些可以与现实世界数据集成的演示以及现实世界中WRLD的应用程序无穷无尽。
例如,我们可以构建一个从实际停车传感器中获取数据的服务。 对于购物中心地图,我们可以显示更多有趣的信息,例如可以吸引更多人参观购物中心的每日报价。 这种技术不仅适用于购物中心,还可以适用于其他类型的企业和机构。 例如,您可以将此地图技术与库存管理系统集成。 该地图可用于可视化设备在营业场所的位置。
使用WRLD映射平台提供创新产品完全取决于您。
翻译自: https://www.sitepoint.com/how-to-create-a-mall-map-with-real-time-data-using-wrld/