Эта статья была создана в сотрудничестве с WRLD . Спасибо за поддержку партнеров, которые делают возможным использование SitePoint.
Как веб-разработчик, вы иногда оказываетесь в положении, когда вам необходимо реализовать карту. Ваш первый выбор — использовать Google Maps, верно?
Это выглядит хорошо. Однако может потребоваться наложение дополнительной информации на карту с помощью маркеров. Вы можете использовать этот метод, или вы можете найти лучшее решение, которое позволяет вам создавать маркеры на внутренней 3D-карте! Как это круто? С помощью внутренних маркеров вы можете предоставлять пользователям уникальные возможности, благодаря которым они смогут получать доступ к информации и взаимодействовать с пользовательскими интерфейсами прямо внутри карты.
В этом уроке мы создадим две демонстрации, иллюстрирующие мощь карт WRLD . Вы узнаете, как создавать собственные приложения, которые могут накладывать информацию в режиме реального времени на 3D-карту. В первой демонстрации мы добавим интерактивные маркеры к существующей карте торгового центра. Во втором демо мы разместим цветные полигоны над парковочными местами, указывая на их вместимость.
Вы можете найти завершенный проект для обеих демонстраций в этом репозитории GitHub .
Предпосылки
Для этой статьи вам нужно только иметь фундаментальное понимание следующих тем:
Я предполагаю, что вы впервые используете карты WRLD. Тем не менее, я рекомендую вам как минимум прочитать статью:
Вам также потребуется последняя версия Node.js и npm, установленная в вашей системе (на момент написания 8.10 LTS является последней стабильной версией). Для пользователей Windows я настоятельно рекомендую вам использовать Git Bash или любой другой терминал, способный обрабатывать основные команды Linux.
Этот урок будет использовать пряжу для установки пакета. Если вы предпочитаете использовать npm
, обратитесь к этому руководству, если вы не знакомы с командами пряжи.
Получить ключ API
Прежде чем начать, вам нужно создать бесплатную учетную запись на WRLD . После того как вы вошли в систему и подтвердили свой адрес электронной почты, вам необходимо получить ключ API. Для получения подробных инструкций о том, как его приобрести, ознакомьтесь с разделом « Начало работы » по созданию динамических 3D-карт, где он хорошо документирован.
Подход к построению карты
Создание карт WRLD является крупным технологическим достижением с большими потенциальными преимуществами для многих отраслей. Существует два основных способа расширения возможностей платформы:
- Использование встроенных инструментов, например, Map Designer и Places Designer
- Создание пользовательского приложения
Позвольте мне объяснить, как каждый метод может быть использован для достижения желаемых результатов.
1. Использование Map Designer и Places Designer
Для нашей первой демонстрации мы можем использовать Places Designer для создания карточек магазина. Это потребует от нас создания Collection Set
котором будут храниться все маркеры Point of Interest
. Доступ к этому набору возможен как внутри экосистемы WRLD, так и извне через ключ API. Мы можем передать эти данные на пользовательскую карту, созданную с помощью Map Designer . С помощью этого инструмента мы можем поделиться картой с другими, используя сгенерированную ссылку. Если вы хотите узнать больше о процессе, посмотрите видеоуроки в этом плейлисте YouTube .
Прелесть этого метода в том, что кодирование не требуется. Однако в нашем случае у него есть ограничения:
- Ограничительный дизайн пользовательского интерфейса — мы можем использовать только тот интерфейс, который поставляется с Places Designer
- Ограничительный набор данных — мы не можем отображать дополнительную информацию, помимо предоставленной
Чтобы преодолеть эти ограничения, нам нужно подойти к нашей проблеме с картой торгового центра, используя второй метод.
2. Создание пользовательского приложения
Создание пользовательских приложений является наиболее гибким вариантом. Хотя это требует определенных усилий по написанию кода, оно позволяет нам всесторонне использовать весь потенциал, предоставляемый платформой WRLD. Создавая собственное приложение, мы можем создать собственный пользовательский интерфейс, добавить дополнительные поля и получать доступ к внешним базам данных в режиме реального времени. Это метод, который мы будем использовать для этого урока.
Сборка приложения
Давайте сначала создадим базовую карту, к которой мы добавим больше функциональности позже. Перейдите в каталог рабочей области и создайте новую папку для своего проекта. Давайте назовем это 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-зависимости .
yarn add babel-core babel-plugin-transform-runtime babel-runtime --dev touch .babelrc
Скопируйте этот код в файл .babelrc
:
{ "plugins": [ [ "transform-runtime", { "polyfill": false, "regenerator": true } ] ] }
Нам также понадобятся следующие пакеты для запуска нашего проекта:
- Пакетная посылка — это как упрощенная версия веб-пакета с практически нулевой конфигурацией
- JSON Server — для создания фиктивного сервера API
Установите пакеты глобально, как это:
yarn global add parcel-bundler json-server # Alternative command for npm users npm install -g parcel-bundler json-server
Это все зависимости узлов, которые нам нужны для нашего проекта. Давайте сейчас напишем немного кода JavaScript. Сначала укажите ваш ключ WRLD API в env.js
:
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
— идентификатор контейнера div, который мы указали вindex.html
-
keys.wrld
— ключ API -
center
— широта и долгота торгового центра Overgate, расположенного в Данди, Шотландия -
zoom
— высота -
indoorsEnabled
— позволяет пользователям получать доступ к внутренним картам
Давайте запустим наш проект. Зайдите в свой терминал и выполните:
parcel src/index.html
Подождите несколько секунд, пока проект завершит пакетирование. Когда это будет сделано, откройте браузер и получите доступ к localhost: 1234 . В зависимости от скорости вашего интернета, загрузка карты не займет много времени.
Красиво, не правда ли? Не стесняйтесь нажимать на синий значок. Это приведет вас в закрытое помещение. Перейдите вокруг, чтобы увидеть разные магазины. Тем не менее, вы скоро поймете, что вы не можете получить доступ к другим этажам. Там также нет кнопки для выхода из внутренней карты. Давайте исправим это в следующей главе.
Создание внутренних элементов управления
Чтобы пользователи могли переключаться между этажами, мы предоставим им виджет управления, который позволит им это делать. Просто добавьте следующие сценарии в раздел head файла 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-файле добавьте этот div в раздел body, прямо перед #map
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
в поле поиска. Нажмите ввод, и карта быстро перейдет к Overgate Mall. Нажмите на синий значок в помещении для Overgate Mall, и вы попадете на внутреннюю карту торгового центра. После загрузки найдите хранилище «Next» и щелкните правой кнопкой мыши, чтобы открыть контекстное меню. Нажмите «Что это за место? вариант. Должно появиться всплывающее окно с координатами.
Нажмите кнопку «Копировать координаты». Это даст вам точные координаты долготы / широты магазина. Сохраните этот адрес где-то временно.
Информация о карте магазина
Вам также необходимо собрать контактную информацию из каждого магазина, которая включает в себя:
- образ
- описание
- Телефон
- Эл. адрес
- Web
- щебет
- часы работы
Вы можете получить большую часть этой информации из 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": "[email protected]", "web": "https://www.jdsports.co.uk/", "twitter": "@jdhelpteam", "tags": "sports shopping", "open_time":[ { "day": "Mon", "time": "9:30am - 6:00pm" },] }
Данные хранятся в массиве, помеченном «pois». POI обозначает Достопримечательности. Теперь, когда у нас есть данные, мы можем легко сделать их доступными через точку API REST, запустив сервер JSON . Просто откройте новый терминал и выполните команду:
json-server --watch data/db.json
Запуск API должен занять несколько секунд. Как только он будет полностью загружен, вы можете протестировать его в своем браузере по адресу localhost: 3000 / pois . Вы также можете получить один POI, используя этот синтаксис:
- localhost:3000/pois/{id}
Например, localhost: 3000 / pois / 3 должен возвращать запись poi
с идентификатором 3 в формате JSON.
Дизайн карты магазина
Мы будем использовать чистую элегантную тему, чтобы аккуратно отображать контактную информацию и время открытия, используя несколько вкладок. Мы создадим маркеры, которые будут отображать всплывающее окно при нажатии. Это всплывающее окно будет иметь следующий интерфейс.
Код для этого HTML-дизайна немного длинен для размещения здесь. Вы можете просмотреть и скачать файл по этой ссылке . У дизайна только три зависимости:
- Семантический интерфейс CSS
- JQuery
- Семантический интерфейс JS
Теперь, когда у нас есть необходимые данные и дизайн, мы должны быть готовы начать работу над нашей картой помещений.
Реализация магазинных карт в Indoor Map
Сначала давайте создадим сервис, который позволит нам получать доступ к данным из API-интерфейсов JSON REST. Эти данные будут использоваться для заполнения Карт Магазина необходимой информацией. Создайте файл 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-дизайн для Store Store в формат, который позволит нам отображать данные. Мы будем использовать JsRender для этого. Разобьем наш статический дизайн на три шаблона:
- Базовый шаблон — содержит контейнеры для вкладок меню, информации и времени.
- Шаблон информации — вкладка для контактной информации магазина.
- Time Template — вкладка для часов работы магазина.
Сначала откройте 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>
Затем скопируйте этот раздел кода прямо перед div widget-container
:
... <!-- 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: WRLD требуется для создания
Popup
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, к которому мы позже добавим контент для информационных и временных вкладок.
В следующем разделе мы собираемся создать функцию с именем 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); } }, };
и// 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); } }, };
Здесь много чего происходит. Я добавил код с комментариями, объясняющими, что делает каждый раздел. Если у вас есть какие-либо сомнения относительно того, как должен выглядеть законченный код, вы можете просмотреть его по этой ссылке .
Далее нам нужно создать маркеры для каждого POI, определенного в db.json
. У каждого маркера будет прослушиватель события 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); } });
Обратите внимание, что мы передаем идентификатор POI в маркер через параметр объекта Options. Если вы вернетесь к функции showPopup
, вы увидите, что мы извлекаем этот идентификатор через объект event
. Если вы сомневаетесь в том, как должен выглядеть полный код, просмотрите его по этой ссылке .
Теперь пришло время протестировать наш код. Я предполагаю, что у вас все еще работает сервер 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
новой функцией. Скопируйте этот код и поместите его сразу после последней функции 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 []; },
Эта функция предназначена для обработки как всех записей парковочных площадок, так и только одной записи в зависимости от того, заполнено ли поле идентификатора. Давайте теперь посмотрим, как мы можем получить эти данные с сервера 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, то есть красного, зеленого, синего и альфа. Также есть функция getColorCode
которая определяет, какой цветовой код использовать, основываясь на проценте используемых слотов. Далее давайте извлечем данные о парковочных площадках с сервера JSON и создадим экземпляр 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 }); }); }); ...
Обратите внимание, что мы сохраняем ассоциацию polygon и parkingArea.id
в массиве. Мы будем работать над этим позже, чтобы сделать нашу карту в реальном времени. Убедитесь, что сервер JSON работает, чтобы этот код работал. А пока обновите страницу, чтобы увидеть обновленные результаты:
Довольно круто, не правда ли? Не стесняйтесь добавлять цветные кодовые метки, чтобы указать их значение. Теперь, текущее ограничение для карты состоит в том, что пользователи не могут видеть обновление карты, если они не обновляют всю страницу. Давайте посмотрим, как мы можем это исправить.
4. Парковочные зоны в реальном времени
Для этого мы будем использовать библиотеку sockets.io
для реализации обновлений в реальном времени. Используемая нами json server
программа 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'); });
программное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
будет предоставлять службы API на порту 3000. Второй экземпляр, 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()
функция updateParkingArea()
.
Пожалуйста, обратитесь к заполненному парковочному файлу, если вы сомневаетесь, как устроен код
Теперь давайте проведем эксперимент. Сначала нужно отменить все существующие процессы. Затем запустите пользовательский код JSON-сервера на отдельном терминале. Затем запустите код parking.html
на отдельном терминале:
# Start server first node server # Start Parking Map App parcel src/parking.html
Теперь откройте или обновите страницу localhost: 1234 . Для отправки обновлений на JSON-сервер мы будем использовать Postman . Просто установите его, если у вас его нет. После его открытия создайте новый запрос и введите следующие параметры:
- Метод — патч
- URL — localhost: 3000 / parkingAreas / 2
- Content-Type — приложение / JSON
- Кодировка — raw, JSON (приложение / json)
- Body —
{ "usedSlots": 75 }
Если вы не знаете, где находится поле Content-Type
, просто перейдите на вкладку Header. Вот скриншот Почтальона:
Когда вы нажимаете кнопку отправить, обновление на карте происходит мгновенно:
Не стесняйтесь поиграть со значением usedSlots
для других записей и посмотреть само обновление карты. Довольно блестяще!
Резюме
Теперь, когда мы подошли к концу урока, я надеюсь, что вы были поражены возможностями того, что вы можете сделать с 3D-картами WRLD. Эти демонстрации, которые можно интегрировать с данными реального мира и приложениями для WRLD в реальном мире, бесконечны.
Например, мы можем создать сервис, который получает данные с реальных датчиков парковки. Для карты торгового центра мы можем отображать более интересную информацию, такую как ежедневные предложения, которые могут побудить больше людей посетить торговый центр. Этот вид технологии применяется не только в торговых центрах, но и в других типах предприятий и учреждений. Например, вы можете интегрировать эту картографическую технологию с системой управления запасами. Карту можно использовать для визуализации расположения оборудования на территории предприятия.
Это действительно зависит от вас, чтобы предложить инновационные продукты с использованием картографической платформы WRLD.