Эта статья была создана в сотрудничестве с WRLD . Спасибо за поддержку партнеров, которые делают возможным использование SitePoint.
Мы только что завершили серию на платформе WRLD. До сих пор мы рассматривали, как использовать WRLD с внешними картами. В этом уроке мы собираемся разработать и использовать карту помещений (также известную как план этажа, если вы собираетесь быть педантичным, как Дуайт Шруте). Как вы могли бы сказать, я думаю, что Office будет хорошей темой для этого. Давайте начнем с цитаты:
Я когда-нибудь покину эту компанию? Смотри, я все о верности. На самом деле, я чувствую, что частью того, за что мне платят, является моя преданность. Но если бы было где-то еще, что ценит лояльность выше … Я иду туда, где они ценят лояльность больше всего. — Дуайт К. Шруте
В этом уроке мы рассмотрим следующие темы:
- Создание карты помещений
- Импорт карты в WRLD
- Подсветка отдельных комнат
- Создание расписания и циклический просмотр
Код для этого урока был протестирован с современной версией Chrome.
Создание комнатных карт
Мы собираемся попытаться воссоздать офис из The Office . Я думаю, что это будет немного весело. Мы начнем с установки некоторого программного обеспечения. Я на Mac, так что это инструкции, как сделать это на Mac. Видимо, в Windows все намного проще …
Первая часть программного обеспечения, которая нам нужна, называется QGIS. Вы можете скачать его прямо с их сайта , но ссылка на Mac переходит за пределы сайта в довольно хитрое место. Я немного больше доверяю программному обеспечению, которое я могу установить через Homebrew , потому что я вижу, что он делает на каждом этапе пути. Если вы работаете на Mac, но у вас еще нет Homebrew, я предлагаю вам установить его. Как только это будет сделано, вы можете начать установку QGIS через несколько пакетов Python:
brew install gdal brew install python
Когда я попытался установить
python
, Homebrew сказал мне, что у меня уже установлен Python. Однако командаpip
отсутствовала. Мне пришлось переустановитьpython
, используяbrew reinstall python
; а затем мне пришлось заново связать его, используяbrew unlink python && brew link python
. После этого у меня была командаpip
.
Вы можете следить за этим с помощью:
pip install matplotlib pip install psycopg2 brew tap osgeo/osgeo4mac brew install qgis2
Все это работает, на момент написания. Это не гарантия, что это сработает к тому времени, как вы прочитаете это. Возможно, вам придется прибегнуть к официальному, но в то же время хитроумному сайту загрузки QGIS, о котором я упоминал ранее.
Я узнал об этом процессе из учебника об установке QGIS через Homebrew .
После всего этого вы сможете запустить команду qgis2
…
Вам также нужно будет установить несколько плагинов. Перейдите в раздел Плагины → Управление и установка плагинов. Установите плагин OpenLayers и плагин Lat Lon Tools.
Далее нам нужно выяснить, где это. В шоу говорится, что офис находится по адресу 1725 Slough Avenue в Скрантоне, штат Пенсильвания. Фактическое местоположение здания действительно на 13927 Сатикой-стрит в Панорама Сити, Калифорния.
Самый простой способ подтвердить местоположение на карте (если вы помните, как это место выглядит) — это найти адрес в чем-то вроде Google Maps, который обеспечивает режим просмотра улиц. Просто чтобы доказать, что местоположение Panorama City является правильным, я искал этот адрес в режиме просмотра улиц и получил следующее:
Теперь пришло время начать создавать карту. Добавьте новый слой OpenStreetMap, перейдя в Web → плагин OpenLayers → OpenStreetMap → OpenStreetMap. Вы должны увидеть увеличенную карту мира. Перейдите в Плагины → Инструменты Lat Lon → Увеличить до широты, долготы и введите нужные координаты (в данном случае: 34.2107329, -118.4365886). Вы можете изменить масштаб на 1: 500 для более детального просмотра.
Далее нажмите Растр → Георефермент → Георефермент. Откроется новое пустое окно. Нажмите Файл → Открыть растр, чтобы выбрать план этажа. «Какой план этажа?», Спросите вы. То же самое вы можете скачать, выполнив поиск «План этажа офиса».
Я должен упомянуть, что вас спросят, какая тип справочной системы координат (CRS или SRS) вам нужен. Выберите «WGS 84 / Pseudo Mercator». Я не очень понимаю, почему, но это было по умолчанию, и, похоже, оно мне помогло! План этажа должен быть отображен в редакторе.
Нам нужно связать внутреннюю карту с наружной картой. Нажмите Добавить точку (в окне Georeferencer) и щелкните точку периметра (например, угол здания). Затем нажмите С холста карты и выберите соответствующую точку на наружной карте.
Повторите это несколько раз. Я знаю, что технически, вероятно, неточно сопоставлять карту помещений 1: 1 с формой наружного здания, но это то, что я сейчас сделал. Не стесняйтесь делать то же самое. Постарайтесь быть максимально точным.
Когда вы довольны точками, которые вы наметили, нажмите «Настройки» → «Параметры трансформации» и настройте параметры примерно так:
По большей части это значение по умолчанию, но целевой SRS должен быть EPSG: 3857. Нажмите на. Затем нажмите зеленую кнопку воспроизведения (которая является кнопкой «Начать привязку»). Это вернет вас к главному окну QGIS, за исключением того, что теперь план этажа будет перекрывать карту на открытом воздухе. Вы можете щелкнуть правой кнопкой мыши по плану этажа, на панели слоев и выбрать Свойства. Там вы можете настроить режим смешивания на что-то менее резкое. Мне нравится режим смешивания Lighten.
Теперь нам нужно начать рисовать элементы на плане этажа. Нажмите Слой → Создать слой → Новый слой с шейп-файлом. Установите тип в Polygon, выберите тот же SRS EPSG: 3857 и добавьте атрибуты type
, name
и highlight
в список атрибутов. Значения по умолчанию «Текстовые данные» и «80» подходят для атрибутов типа и имени, но для атрибута highlight необходимо установить целое число. Значения в поле Длина не нужно менять. Нажмите Ok и присвойте файлу формы имя.
Когда вы увидите его на панели слоев, дважды щелкните по нему и установите прозрачность 50%, чтобы вы все равно могли видеть карту внизу. Слой фигур должен быть верхним, поэтому перетащите его, если его там еще нет. Затем нажмите кнопку «Переключить редактирование» (карандаш), а затем нажмите кнопку «Добавить объекты» (которая выглядит как зеленый блог с желтым значком над ним).
Проследите границу плана этажа. Когда вы это сделаете, щелкните правой кнопкой мыши на форме, которую вы сделали, и присвойте ей тип building_outline
. Это граница для того, как WRLD будет отображать карту в помещении. Снова нажмите кнопку «Добавить элементы» и начните отслеживать такие объекты, как стены, окна, двери и комнаты. Они не должны пересекаться. Когда вы отследили сегмент, присвойте ему соответствующий тип, такой как wall
, window
, door
или room
. Обычно вы можете оставить поле «имя» пустым, но если вы укажете имя комнаты, WRLD отобразит его. Если вы планируете использовать какие-либо блики, вы можете добавить 1
к элементам room
в атрибуте highlight
.
Это немного менее хаотично, если вы рисуете комнаты вместо стен, окон и дверей, которые их составляют. Здесь есть полный список элементов, которые вы можете отследить (и их типы). Отслеживайте функции, пока не получите столько деталей, сколько вам нужно.
Прежде чем мы сможем импортировать карту в WRLD, мы должны убедиться, что идентификаторы объектов уникальны. Если вы давали уникальные идентификаторы, то все готово. В противном случае откройте список объектов, нажав Слой → Открыть таблицу атрибутов, и присвойте каждому объекту идентификатор. Порядок не имеет значения.
Щелкните правой кнопкой мыши слой шейп-файла и выберите Сохранить как. Чтобы загрузить свое здание, теперь вам нужно обновить CRS до EPSG: 4326 и экспортировать его в виде файла геоджона, а не шейп-файла. Преобразование в 4326 изменяет формат с Псевдо-Меркатора на Меркатор, что означает, что точки вашего построенного плана этажа теперь сохраняются по широте и долготе — которые WRLD Indoor Map Designer использует для правильного размещения вашей карты в мире при преобразовании ее в 3D. модель. Сохраните файл где-нибудь и откройте текстовый редактор в той же папке, в которую вы сохранили файл слоя формы. Создайте новый файл JSON с именем main.json
:
{ "id": "the-office", "name": "The Office", "owner": "Dunder Mifflin", "location": { "type": "Point", "coordinates": [-118.4365886, 34.2107329] }, "levels": [ { "id": "the-office-1", "name": "1", "readable_name": "First floor", "z_order": 0, "filename": "floor-plan.geojson" } ] }
Это из main.js
Поле id
— это то, что вы можете узнать по карте. Вы можете положить что-нибудь там. Имя — это читаемое имя карты. Владелец — это название вашей компании (или компании, претендующей на строительство на WRLD). Местоположение — это точка на карте, где обитает внутренняя карта. Этот массив [longitude, latitude]
.
Затем есть множество уровней. Вы можете иметь несколько этажей на одно здание. Мы только разработали один, так что это тот, который мы определили в массиве. Поместите эти два файла в папку и создайте ZIP-файл из папки. Затем зайдите на https://mapdesigner.wrld3d.com/indoormap/latest и начните создавать карту!
Нажмите New Indoor Map и назовите ее. Затем найдите подходящее здание, используя широту и долготу. Когда вы увидите желаемое здание, щелкните по нему и нажмите «Заявить здания». Убедитесь, что вы выбрали опцию Скачать план этажа, так как это упростит отслеживание в QGIS. Затем перейдите к шагу «Загрузить карту» и загрузите созданный вами ZIP-файл. Это займет несколько минут, чтобы обработать. В случае успеха, вы должны увидеть кнопку на карте, чтобы нажать на. Нажав на нее, вы попадете внутрь здания …
Как видите, я потратил некоторое время, чтобы выяснить, что координаты location
были «долгота, широта» вместо «широта, долгота». Когда вы будете довольны картой помещений, нажмите Сделать карту общедоступной.
Здесь есть более подробное руководство по созданию этих карт. Я нашел части этого бесполезными, но ваш пробег может варьироваться …
Взаимодействие с внутренними картами
Для утверждения вашей карты может потребоваться некоторое время. А пока давайте посмотрим, как мы можем взаимодействовать с картами помещений.
В этой части руководства мы будем работать с кодом, размещенным на CodePen. CodePens являются демонстративными, и наилучшие результаты будут достигнуты с использованием выдержек из кода и шагов в статье. Если вы хотите локальную настройку, ищите подробности о том, как ее настроить, в предыдущем руководстве .
Давайте начнем с создания карты и ввода готового здания:
const map = L.Wrld.map("map", "f24b71dd92ab9e38cf4aa53806ab813d", { center: [56.459801, -2.977928], zoom: 15, indoorsEnabled: true, }) map.indoors.on("indoormapenter", () => { map.indoors.setFloor(2) map.setView([56.459984, -2.978238], 20) }) map.on("initialstreamingcomplete", () => { map.indoors.enter("westport_house") })
Это из CodePen
В этом примере мы переходим к координатам офисов WRLD. Как только карта закончила загрузку (через initialstreamingcomplete
), мы просим карту «пойти в закрытое помещение». На событии indoormapenter
мы устанавливаем для выбранного этажа значение 2
и увеличиваем карту дальше.
Это будет работать с картами, которые вы отправили, а также, когда они будут утверждены.
Мы можем взаимодействовать с функциями, прослушивая событие щелчка:
map.indoors.on("indoorentityclick", (event) => { map.indoors.clearEntityHighlights() map.indoors.setEntityHighlights(event.ids[0], [255, 0, 0, 128]) })
Это из CodePen
indoorentityclick
получает событие, содержащее идентификатор функции, на которую мы indoorentityclick
. Мы можем использовать события clearEntityHighlights
и setEntityHighlights
для переключения любых пользовательских цветов на функции, которые мы хотим выделить.
Вероятно, пришло время для другой цитаты …
Я видел Свадебные Крашеры случайно. Я купил билет на «Гризли» и пошел не в тот театр. Через час я понял, что попал не в тот театр, но продолжал ждать. Потому что это то, что относится к атакам медведей … они приходят, когда вы меньше всего этого ожидаете. — Дуайт … снова
Создание повторяемого шаблона выделения
Давайте попробуем эксперимент. Давайте сделаем кнопку «запись», которая записывает клики, которые мы делаем на карте; а затем повторяет их все обратно к нам. Нам нужна кнопка записи и воспроизведения, а также множество нажатий …
<button id="record">record</button> <button id="play">play</button>
Это из CodePen
Теперь давайте подключимся к этим кнопкам с событиями нажатия:
map.indoors.on("indoorentityclick", (event) => { map.indoors.clearEntityHighlights() map.indoors.setEntityHighlights(event.ids[0], [255, 0, 0, 128]) clicks.push({ time: new Date(), id: event.ids[0] }) }) let recording = false let started = null let clicks = [] const record = document.querySelector("#record") const play = document.querySelector("#play") record.addEventListener("click", () => { if (recording) { recording = false record.innerText = "record" } else { recording = true record.innerHTML = "stop" started = new Date() clicks = [] } }) play.addEventListener("click", () => { map.indoors.clearEntityHighlights() for (let click of clicks) { setTimeout(() => { map.indoors.clearEntityHighlights() map.indoors.setEntityHighlights(click.id, [255, 0, 0, 128]) }, click.time.getTime() - started.getTime()) } })
Это из CodePen
Давайте сначала посмотрим на событие кнопки записи. При нажатии, если запись еще не началась, мы изменяем текстовое значение кнопки, чтобы stop
и очищаем массив щелчков. Мы также фиксируем момент, когда была нажата кнопка записи, чтобы мы могли рассчитывать наши события относительно нее.
Если мы нажмем на него снова, мы сможем сбросить значение текста кнопки и остановить запись.
Затем, когда мы нажимаем на кнопку воспроизведения; мы очищаем все выделенные функции и начинаем циклически перебирать «записанные» события. Они находятся в массиве, поэтому мы можем использовать цикл for...of
Мы устанавливаем таймаут для каждого события щелчка, беря время щелчка и вычитая started
время из него. Все тайм-ауты создаются практически в одно и то же время, но они запускаются в зависимости от того, как долго после начала записи они произошли.
Наконец, нам нужно фиксировать события щелчка, когда щелкают объекты. Итак, мы indoorentityclick
наш ранее indoorentityclick
событий indoorentityclick
, добавив события click в массив clicks
. Попробуйте, записав несколько кликов и затем воспроизведя их. Это довольно затягивает …
Создание расписания
Давайте расширим эту идею, чтобы включить настраиваемое расписание. Допустим, кто-то в WRLD передвигается по офису в течение дня. Или представьте (в соответствии с нашей темой), что Дуайт хотел отслеживать движения Джима …
Он мог бы сделать это, если бы знал расписание Джима:
const schedule = [ { "start": "08:00", "duration": "00:30", "id": "0003" }, { "start": "08:30", "duration": "00:30", "id": "0004" }, { "start": "09:15", "duration": "00:45", "id": "0028" }, { "start": "10:00", "duration": "01:00", "id": "Meeting Room" }, { "start": "12:00", "duration": "01:30", "id": "0039" } ];
Это из CodePen
Мы можем сделать функцию, которая вычисляет количество минут для формата времени, который мы используем (например, 01:30
→ 90
):
const timeToMinutes = (time) => { let holder = new Date(); holder.setHours(0); holder.setMinutes(0); const start = holder.getTime(); const parts = time.split(":"); holder.setHours(parseInt(parts[0], 10)); holder.setMinutes(parseInt(parts[1], 10)); const end = holder.getTime(); return (end - start) / 1000 / 60; }; const schedule = [ // ... ] for (let event of schedule) { console.log(timeToMinutes(event.duration)) }
Это из CodePen
Это напечатает 30
, 30
, 45
, 60
и 90
по порядку. Затем нам нужно добавить эти минуты ко времени начала, чтобы определить время окончания:
const timeToMinutes = (time) => { // ... } const startFromTime = (time) => { const parts = time.split(":"); let holder = new Date(); holder.setSeconds(0); holder.setHours(parseInt(parts[0], 10)); holder.setMinutes(parseInt(parts[1], 10)); return holder; }; const endFromStartAndMinutes = (start, minutes) => { return new Date(start.valueOf() + (minutes * 1000 * 60)); }; const schedule = [ // ... ] for (let event of schedule) { const start = startFromTime(event.start) const minutes = timeToMinutes(event.duration) const end = endFromStartAndMinutes(start, minutes) console.log(start, end) }
Это из CodePen
… И это будет записывать время начала и окончания относительно текущего дня. Возможно, вы захотите, чтобы начальное и конечное время endFromStartAndMinutes
к фиксированному дню, поэтому вам нужно изменить startFromTime
и endFromStartAndMinutes
чтобы учитывать день.
Учитывая это время начала и окончания, мы могли бы закодировать карту помещения, чтобы показать комнату, где сейчас находится этот сотрудник:
const schedule = [ // ... ] setInterval(() => { map.indoors.clearEntityHighlights(); for (let event of schedule) { const start = startFromTime(event.start); const minutes = timeToMinutes(event.duration); const end = endFromStartAndMinutes(start, minutes); const now = new Date(); if (now >= start && now <= end) { console.log("Time is now " + now.toLocaleTimeString() + ", in the " + start.toLocaleTimeString() + " to " + end.toLocaleTimeString() + " interval. Highlighting entity " + event.id); map.indoors.setEntityHighlights(event.id, [255, 0, 0, 128]); } } }, 1000);
Это из CodePen
Сравнивая текущее время с временем начала и окончания каждого события; мы можем сказать, в каком событии находится сотрудник (и, соответственно, выделить эту функцию).
Если вы не видите выделенных объектов, вероятно, у вас нет запланированных на точное время просмотра кода. Попробуйте настроить расписание этого CodePen и откройте консоль, чтобы увидеть, какая сущность должна быть выделена. Я также удалил код записи / воспроизведения из предыдущих примеров, чтобы было легче увидеть, что происходит в этой демонстрации.
Просто подумайте, как это можно расширить. Вы можете добавить расписание каждого, чтобы создать виртуальную систему бронирования событий. Или вы можете создать анимированную визуализацию, чтобы увидеть, есть ли какие-либо конфликты. Это тоже еще одна отправная точка для приложения.
Резюме
Мы потратили много времени на изучение всех деталей создания карт для помещений. WRLD — не единственная платформа, которая понимает карты, созданные QGIS, поэтому это хороший навык, чтобы изучить, если вы хотите делать все виды картографирования.
Мы также рассмотрели, как карты QGIS импортируются и используются внутри платформы WRLD. Мы можем определять карты с огромным количеством деталей и взаимодействовать с ними в режиме реального времени. Когда вы знаете, что устанавливать и как рисовать объекты, картографирование в помещении становится довольно простым.
Потратьте некоторое время, чтобы подумать, как вы могли бы использовать это, чтобы смоделировать ваш дом или офис. Возможно, вы могли бы использовать карты помещений в сочетании с другими функциями Leaflet для создания карт теплового потока или использования энергии для карт помещений. Возможно, вы могли бы создать свое собственное приложение для планирования работы офиса.
Я должен упомянуть: если вы хотите работать с картами внутренних помещений, но не хотите создавать их самостоятельно, WRLD предлагает картографический сервис для помещений за плату. Я не экспериментировал с более высокими уровнями обслуживания и функциональности, но кажется, что WRLD довольно гибки в отношении того, как 3D-карты помещений становятся общедоступными (для таких вещей, как стадионы и аэропорты) по сравнению с частными (для таких вещей, как больницы и школы). ). После этого урока они также сказали мне, что хотят упростить документацию и рабочий процесс для создания карты помещений. Это немного подробно на данный момент, но стоит результатов, если вы спросите меня. Представьте, если бы это было еще проще сделать …
Мы находимся в конце нашего путешествия WRLD. Это стало одним из моих любимых встраиваемых сервисов. С ним весело работать, и он может быть достаточно подробным и гибким. Более того, я не заплатил ни цента, чтобы использовать его. Надеюсь, вы получили столько же удовольствия от этих уроков, сколько я их написал. Дайте мне знать, что вы создали в Twitter . Вы также можете связаться с WRLD с вопросами или показать им, что вы создали.