Статьи

Создание поиска ванной комнаты Джорджа Костанзы с использованием WRLD

Эта статья была спонсирована WRLD 3D . Спасибо за поддержку партнеров, которые делают возможным использование SitePoint.

«Где-нибудь в городе? В любом месте города: я расскажу вам лучший общественный туалет.

Это слова Джорджа Костанцы к Джерри Сайнфельду в 1991 году. В этом эпизоде ​​с Сайнфельдом ; дальновидный Джордж изобрел приложение раньше времени — поиск ванной! Если вы частый путешественник, родитель или просто кто-то, кто знает важность чистого и ухоженного места для некоторого «спокойствия», вы поймете полезность этой идеи.

Джордж Костанца - великолепные услуги

Итак, на этот раз во втором уроке из нашей серии WRLD мы собираемся создать… давайте назовем это «приложением для поиска объектов».

Предварительный просмотр нашего приложения Facility Finder Краткий обзор того, что мы собираемся построить вместе

Это не первый раз, когда кто-то пробовал это сделать. В 2010, bathroomreview.ca сделал именно это (как описано в Forbes ). Но сайт больше не работает.

В последнем уроке мы рассмотрели немало вопросов, на этот раз мы собираемся использовать часть этого обучения. Например, мы будем использовать ParcelJS для создания наших статических файлов, но не будем вдаваться в подробности о том, как его настроить снова. Мы также выделим здания и установим соответствующие погодные условия и время суток, в зависимости от того, что они для пользователя. Если вы не знаете, как это работает, вернитесь к предыдущему руководству .

В этом уроке мы рассмотрим следующие темы:

  • Создание простого серверного API AdonisJS (для кэширования данных о местоположении и обработки запросов CORS).
  • Запрашивать данные об объектах общего пользования с веб-сайтуuge.rest , если в радиусе 10 метров от пользователя нет кэшированных объектов. Мы будем использовать Google Matrix API для расчета расстояния между точками интереса.
  • Подсветка зданий с общественными удобствами, окрашенными в соответствии с их рейтингом Зеленый для хорошего, красный для плохого. Каждое здание будет иметь информационную карту для дополнительной информации (например, как добраться до ванной).

В конце мы немного поговорим о том, как превратить такое приложение в жизнеспособный бизнес. В том-то и дело, не так ли? WRLD API предоставляют инструменты для визуализации реальных данных на карте реального мира. Наша задача — выяснить, как использовать эту технологию для коммерческих приложений!

Код для этого урока можно найти на Github . Он был протестирован с современными версиями Firefox, Node и macOS.

Получение данных объекта

Давайте начнем с изучения того, как получить данные учреждения и форму, в которой мы их получаем. Мы собираемся использоватьugerestrooms.org в качестве источника данных. Мы узнаем, что мы можем искать по широте и долготе, просматривая документацию . На самом деле, мы можем сделать следующий запрос и посмотреть набор объектов рядом с моим местоположением:

curl https://www.refugerestrooms.org/api/v1/restrooms/by_location.json? ↵ lat=-33.872571799999996&lng=18.6339362 

Есть несколько других параметров, которые мы могли бы указать (например, фильтровать ли по доступным и / или унисекс-объектам), но главное, что это дает нам, — это способ вставить координаты в поиск и найти близлежащие местоположения.

Мы не можем просто позвонить из браузера. Есть множество причин безопасности, почему это запрещено. Есть и причины производительности. Что если 10 человек подали одинаковый запрос, стоя на расстоянии 10 метров друг от друга? Было бы бесполезно запускать 10 запросов на один и тот же удаленный сервер, если бы мы могли обслуживать его быстрее с помощью кэширующего прокси.

Вместо этого мы собираемся настроить простой API-интерфейс кеширования AdonisJS. Наше браузерное приложение будет отправлять запросы в API AdonisJS, и если нет данных «поблизости»; он отправит запрос в API Refuge. Мы не можем тратить слишком много времени на детали AdonisJS, поэтому вам придется проверить документацию для деталей.

Я также почти закончил писать книгу об этом , так что это лучшее место, чтобы узнать, как это работает!

Самый простой способ создать новое приложение AdonisJS — это установить инструмент командной строки:

 npm install --global @adonisjs/cli 

Это позволяет командной строке Adonis глобально. Мы можем использовать его для создания нового каркаса приложения:

 adonis new proxy 

Это займет немного времени, так как установлено несколько вещей. Когда он закончится, вы должны увидеть сообщение для запуска сервера разработки. Это можно сделать с помощью:

 adonis serve --dev 

Откройте http://127.0.0.1:3333 в своем браузере, и вас должна приветствовать эта красота:

Экран приветствия Адониса

Создание миграций и моделей

Давайте расскажем результаты поиска в базе данных. AdonisJS поддерживает несколько различных движков, но мы будем использовать SQLite для простоты. Мы можем установить соответствующий драйвер, используя:

 npm install --save sqlite3 

Далее давайте сделаем миграцию и модель. Нас интересуют только координаты, используемые для поиска, и возвращаемый JSON. Если координаты достаточно близки к тому месту, где ищет пользователь, мы будем использовать существующий поисковый ответ вместо повторного запроса поисковых данных.

Мы можем использовать утилиту командной строки adonis для создания миграций и моделей:

 adonis make:migration search adonis make:model search 

Это создает пару файлов. Первый — это миграция, к которой мы можем добавить три поля:

 "use strict" const Schema = use("Schema") class SearchSchema extends Schema { up() { this.create("searches", table => { table.increments() table.string("latitude") table.string("longitude") table.text("response") table.timestamps() }) } down() { this.drop("searches") } } module.exports = SearchSchema 

Это из proxy/database/migrations/x_search_schema.js

Мы добавили поля latitude , longitude и response . Первые два имеют смысл как string даже если они содержат данные с плавающей запятой, потому что мы хотим выполнить поиск по ним.

Далее, давайте создадим одну конечную точку API:

 "use strict" const Route = use("Route") // we don't need this anymore... // Route.on("/").render("welcome") Route.get("search", ({ request, response }) => { const { latitude, longitude } = request.all() // ...do something with latitude and longitude }) 

Это из proxy/start/routes.js

Каждый маршрут AdonisJS определяется в файле routes.js Здесь мы закомментировали начальный «приветственный» маршрут и добавили новый «поисковый» маршрут. Закрытие вызывается с объектом контекста; который имеет доступ к request и request .

Мы можем ожидать, что поисковые запросы предоставят параметры строки запроса latitude и longitude ; и мы можем получить это с request.all . Мы должны проверить, есть ли у нас какие-либо неопределенно связанные координаты. Мы можем сделать это, используя модель Search :

 const Search = use("App/Models/Search") const searchablePoint = (raw, characters = 8) => { const abs = Math.abs(parseFloat(raw)) return parseFloat(abs.toString().substr(0, characters)) } Route.get("search", async ({ request, response }) => { const { latitude, longitude } = request.all() const searchableLatitude = searchablePoint(latitude) const searchableLongitude = searchablePoint(longitude) // console.log(searchableLatitude, searchableLongitude) const searches = await Search.query() .where("latitude", "like", `%${searchableLatitude}%`) .where("longitude", "like", `%${searchableLongitude}%`) .fetch() // console.log(searches.toJSON()) response.send("done") // ...do something with latitude and longitude }) 

Это из proxy/start/routes.js

Начнем с импорта модели Search . Это кодовое представление таблицы базы данных, которую мы создали (используя миграцию). Мы будем использовать это для запроса к базе данных о «близких» поисках.

Прежде чем мы сможем это сделать, нам нужен способ поиска почти координат. Функция searchablePoint берет необработанную строку координат и создает абсолютное значение с плавающей запятой, удаляя необязательное - с начала строки. Затем он возвращает первые 8 символов строки координат. Это сократит -33.872527399999996 до 33.872527 . Затем мы можем использовать эти 8 символов в предложении SQL «где как», чтобы возвращать все поиски с похожими строками координат.

AdonisJS эффективно использует ключевые слова async и await . Такие методы, как Search.query возвращают обещания, поэтому мы можем await их результатов, пока еще пишем 100% асинхронный код.

Я пропускаю много деталей AdonisJS, которые мне действительно не нравятся. Если вы боретесь с этой частью; поговори со мной в твиттере , и я укажу тебе правильное направление.

Соответствующие близлежащие места

Теперь, когда у нас есть «близлежащие» местоположения, мы можем сравнить их относительные расстояния с тем, где находится пользователь. Если у вас еще нет ключа API Google, обратитесь к предыдущему руководству, чтобы узнать, как его получить. Мы собираемся стать сервисом Google Distance Matrix:

 https://maps.googleapis.com/maps/api/distancematrix/json? ↵ mode=walking& ↵ units=metric& ↵ origins=-33.872527399999996,18.6339164& ↵ destinations=-33.872527399999997,18.6339165& ↵ key=YOUR_API_KEY 

Сервис «Матрица расстояний» на самом деле допускает несколько источников, поэтому мы можем объединить все ваши предыдущие поиски в длинную строку происхождения:

 const reduceSearches = (acc, search) => { const { latitude, longitude } = search return `${acc}|${latitude},${longitude}` } Route.get("search", async ({ request, response }) => { const { latitude, longitude } = request.all() // ...get searches const origins = searches .toJSON() .reduce(reduceSearches, "") .substr(1) // console.log(origins) response.send("done") // ...do something with latitude and longitude }) 

Это из proxy/start/routes.js

Мы можем преобразовать результаты поиска в массив объектов. Это полезно, потому что мы можем уменьшить массив, объединяя широту и долготу каждого поиска в строку. Эта строка будет начинаться с | , поэтому нам нужно получить строку, начинающуюся с индекса 1 .

Я фанат API fetch браузера, поэтому давайте установим полифилл NodeJS:

 npm install --save node-fetch-polyfill 

Используя этот polyfill, мы можем получить список расстояний от Google:

 "use strict" const fetch = use("node-fetch-polyfill") const Env = use("Env") const Route = use("Route") const Search = use("App/Models/Search") const searchablePoint = (raw, characters = 8) => { // ... } const reduceSearches = (acc, search) => { // ... } Route.get("search", async ({ request, response }) => { const { latitude, longitude } = request.all() // ...get origins const key = Env.get("GOOGLE_KEY") const distanceResponse = await fetch( `https://maps.googleapis.com/maps/api/distancematrix/json? ↵ mode=walking&units=metric&origins=${origins}& ↵ destinations=${latitude},${longitude}&key=${key}`, ) const distanceData = await distanceResponse.json() // console.log(distanceData) response.send("done") // ...do something with data }) 

Это из proxy/start/routes.js

fetch возвращает обещание, поэтому мы можем его await . Ответ имеет метод json , который сериализует необработанный ответ в массив или объект JSON. Затем скомбинируйте исходные координаты (всего, что отдаленно напоминает начальную точку), мы получим список всех расстояний. Ответные объекты находятся в том же порядке, что и исходные координаты. Это станет полезным, когда мы продолжим …

AdonisJS обеспечивает собственную поддержку файлов .env . Мы можем отказаться от файлов env.example.js и env.js из предыдущего урока; и просто используйте уже .env и .env.example . Я добавил GOOGLE_KEY к обоим, как и вы. Затем мы можем использовать Env.get чтобы получить значение.

Мы можем проверить результаты, чтобы определить, находятся ли какие-либо из них в пределах 10 метров от запрошенных координат:

 Route.get("search", async ({ request, response }) => { const { latitude, longitude } = request.all() // ...get distance data for (let i in distanceData.rows) { const { elements } = distanceData.rows[i] if (typeof elements[0] === "undefined") { continue } if (elements[0].status !== "OK") { continue } const matches = elements[0].distance.text.match(/([0-9]+)\s+m/) if (matches === null || parseInt(matches[1], 10) > 10) { continue } response.json(JSON.parse(searchRows[i].response)) return } // ...cached result not found, fetch new data! }) 

Это из proxy/start/routes.js

Мы можем перебрать строки расстояния, выполнив несколько проверок для каждого. Если исходные координаты недействительны, служба Матрица расстояний может вернуть ошибку для этой строки. Если элементы имеют неправильный формат (неопределенный или ошибочный), мы пропускаем строку.

Если есть действительное измерение (которое в форме nm , где n равно 1 — 10); тогда мы возвращаем ответ для этой строки. Нам не нужно запрашивать новые данные об убежище. В том вероятном случае, если у нас нет близких кешированных координат; мы можем запросить новые данные:

 Route.get("search", async ({ request, response }) => { const { latitude, longitude } = request.all() // ...check for cached data const refugeResponse = await fetch( `https://www.refugerestrooms.org/api/v1/restrooms/by_location.json? ↵ lat=${latitude}&lng=${longitude}`, ) const refugeData = await refugeResponse.json() await Search.create({ latitude, longitude, response: JSON.stringify(refugeData), }) response.json(refugeData) return }) 

Это из proxy/start/routes.js

Если нет кэшированных поисков, мы запрашиваем новый набор результатов Refuge. Мы можем вернуть их без изменений; но не перед сохранением поиска в базе данных. Первый запрос должен быть немного медленнее, чем последующие. По сути, мы перекладываем обработку Refuge API на API матрицы расстояний. Теперь у нас также есть способ управления разрешениями CORS.

Получение результатов в браузере

Давайте начнем использовать эти данные в браузере. Попробуйте настроить цепочку сборки ParcelJS (или посмотрите назад на предыдущее руководство, где мы это делали). Это включает в себя установку и загрузку WRLD SDK в файл app.js Это должно выглядеть так:

 const Wrld = require("wrld.js") const tester = async () => { const response = await fetch( "http://127.0.0.1:3333/search? ↵ latitude=-33.872527399999996&longitude=18.6339164", ) const data = await response.json() console.log(data) } tester() 

Это из app/app.js

Вы должны иметь возможность связать это с помощью следующей команды:

 parcel index.html 

Структура вашей папки должна выглядеть следующим образом:

Структура папки с посылками

Это та же структура папок, что и в предыдущем уроке. Вы также можете скопировать все это, заменив содержимое app.js тем, что вы видите выше. Функция tester должна продемонстрировать, что мы пока не можем запрашивать данные с нашего кеширующего прокси-сервера. Для этого нам нужно включить слой AdonisJS CORS :

 "use strict" module.exports = { /* |-------------------------------------------------------------------------- | Origin |-------------------------------------------------------------------------- | | Set a list of origins to be allowed... */ origin: true, // ...rest of the CORS settings } 

Это из proxy/config/cors.js

Если мы установим origin в true , все запросы CORS будут успешными. В производственной среде вы, вероятно, захотите предоставить замыкание, которое условно возвращает true; так что вы можете ограничить, кто может делать запросы к этому API.

Если вы обновите браузер, он будет открыт для URL, который обслуживает ParcelJS; Теперь вы должны увидеть результаты в консоли:

Cors Working

Не обращайте внимания на это предупреждение. Это просто замена модуля ParcelJS Hot …

С этого момента мы можем начать использовать кеширующий прокси-сервер для поиска ближайших объектов к набору координат. Давайте добавим карту!

Интеграция с WRLD

Давайте начнем с добавления файлов env.js и env.example.js из первого урока в папку app . Затем мы можем использовать их для рендеринга карты снова:

 const Wrld = require("wrld.js") const env = require("./env") const keys = { wrld: env.WRLD_KEY, } // ...tester code window.addEventListener("load", async () => { const map = Wrld.map("map", keys.wrld, { center: [40.7484405, -73.98566439999999], zoom: 15, }) }) 

Это из app/app.js

Вот мы и вернулись в Эмпайр Стейт Билдинг. Было бы лучше, если бы мы могли начать где-то ближе к пользователю. И, если бы мы могли предоставить способ переопределить геолокацию с помощью пользовательских координат. Давайте подключимся к API геолокации HTML5:

 window.addEventListener("load", async () => { let map navigator.geolocation.getCurrentPosition( position => { const { latitude, longitude } = position.coords map = Wrld.map("map", keys.wrld, { center: [latitude, longitude], zoom: 15, }) }, error => { map = Wrld.map("map", keys.wrld, { center: [40.7484405, -73.98566439999999], zoom: 15, }) }, ) }) 

Это из app/app.js

Мы можем использовать getCurrentPosition чтобы получить getCurrentPosition координаты пользователя. Если пользователь отклоняет запрос данных геолокации или что-то еще идет не так, мы можем по умолчанию установить набор известных координат.

Там нет документированного аргумента ошибки, но я хотел бы поместить параметр там, чтобы сделать код более понятным.

Об этом позаботилось автоматическое определение местоположения. Теперь, что если мы хотим переопределить его с помощью пользовательских координат? Мы можем добавить некоторые входные данные формы в наш HTML и нацелить их на некоторый Javascript:

 <body> <div id="map"></div> <div class="controls"> <input type="text" name="latitude" /> <input type="text" name="longitude" /> <input type="button" name="apply" value="apply" /> </div> <script src="./app.js"></script> </body> 

Это из app/index.html

 .controls { position: absolute; top: 0; right: 0; background: rgba(255, 255, 255, 0.5); padding: 10px; } 

Это из app/app.css

 window.addEventListener("load", async () => { let map const latitudeInput = document.querySelector("[name='latitude']") const longitudeInput = document.querySelector("[name='longitude']") const applyButton = document.querySelector("[name='apply']") applyButton.addEventListener("click", () => { map.setView([latitudeInput.value, longitudeInput.value]) }) navigator.geolocation.getCurrentPosition( position => { const { latitude, longitude } = position.coords latitudeInput.value = latitude longitudeInput.value = longitude map = Wrld.map("map", keys.wrld, { center: [latitude, longitude], zoom: 15, }) }, error => { map = Wrld.map("map", keys.wrld, { center: [40.7484405, -73.98566439999999], zoom: 15, }) }, ) }) 

Это из app/app.js

Мы начинаем с получения ссылок на новые элементы input которые мы добавили. Когда applyButton , мы хотим перецентрировать карту. Когда данные геолокации успешны, мы можем заполнить эти входные данные соответствующей широтой и долготой.

Теперь, как насчет подсветки близлежащих зданий?

 let map let highlightedFacilities = [] const highlightFacilities = async (latitude, longitude) => { for (let facility of highlightedFacilities) { facility.remove() } highlightedFacilities = [] const facilitiesResponse = await fetch( `http://127.0.0.1:3333/search?latitude=${latitude}&longitude=${longitude}`, ) const facilitiesData = await facilitiesResponse.json() for (let facility of facilitiesData) { // console.log(facility) const color = facility.upvote >= facility.downvote ? [125, 255, 125, 200] : [255, 125, 125, 200] const highlight = Wrld.buildings .buildingHighlight( Wrld.buildings .buildingHighlightOptions() .highlightBuildingAtLocation([ facility.latitude, facility.longitude, ]) .color(color), ) .addTo(map) highlightedFacilities.push(highlight) } } window.addEventListener("load", async () => { // ...add button event navigator.geolocation.getCurrentPosition( position => { const { latitude, longitude } = position.coords // ...create map map.on("initialstreamingcomplete", () => { highlightFacilities(latitude, longitude) }) }, error => { // ...create map map.on("initialstreamingcomplete", () => { highlightFacilities(40.7484405, -73.98566439999999) }) }, ) }) 

Это из app/app.js

Когда мы создаем карту или меняем ее фокус, мы можем вызвать функцию highlightFacilities . Он принимает latitude и longitude , удаляет все ранее выделенные здания и выделяет все здания, возвращенные поиском прокси кэширования.

Мы выбираем зеленую подсветку для зданий с 50% и более голосов; и красная подсветка для отдыха. Это облегчит поиск лучших объектов.

Мы могли бы даже обновить входы переопределения текущим центром карты, чтобы пользователь мог перемещаться и находить новые ванные комнаты рядом с этим регионом карты. Мы могли бы также сделать выделенные здания немного более четкими; путем добавления маркеров карты и отображения всплывающих окон при нажатии / нажатии:

 let map let highlightedFacilities = [] let highlighterMarkers = [] const highlightFacilities = async (latitude, longitude) => { for (let facility of highlightedFacilities) { facility.remove() } highlightedFacilities = [] for (let marker of highlighterMarkers) { marker.remove() } highlighterMarkers = [] const facilitiesResponse = await fetch( `http://127.0.0.1:3333/search?latitude=${latitude}&longitude=${longitude}`, ) const facilitiesData = await facilitiesResponse.json() for (let facility of facilitiesData) { const location = [facility.latitude, facility.longitude] // ...add highlight color const intersection = map.buildings.findBuildingAtLatLng(location) let marker if (intersection.found) { marker = L.marker(location, { elevation: intersection.point.alt, title: facility.name, }).addTo(map) } else { marker = L.marker(location, { title: facility.name, }).addTo(map) } if (facility.comment) { marker.bindPopup(facility.comment).openPopup() } highlighterMarkers.push(marker) } } window.addEventListener("load", async () => { // ...add button event navigator.geolocation.getCurrentPosition( position => { const { latitude, longitude } = position.coords // ...create map map.on("panend", event => { const { lat, lng } = map.getBounds().getCenter() latitudeInput.value = lat longitudeInput.value = lng }) }, error => { // ...create map map.on("panend", event => { const { lat, lng } = map.getBounds().getCenter() latitudeInput.value = lat longitudeInput.value = lng }) }, ) }) 

Это из app/app.js

Мы можем добавить события panend в места, где мы создаем карту. Это срабатывает, когда пользователь начинает панорамирование, и карта останавливается. Мы получаем видимые границы карты и получаем центр от этого.

Затем в функцию highlightFacilities мы добавили маркеры и дополнительные всплывающие окна (если есть рекомендации по отображению. Это немного облегчает поиск выделенных зданий и поиск дополнительной информации об объектах, которые они содержат.

Добавление атмосферы

Давайте закончим, добавив некоторые атмосферные эффекты в вид карты. Для начала мы можем добавить конечную точку «погодные условия» в наш прокси-сервер кэширования:

 Route.get("condition", async ({ request, response }) => { const { latitude, longitude } = request.all() const key = Env.get("OPENWEATHER_KEY") const weatherResponse = await fetch( `http://api.openweathermap.org/data/2.5/weather? ↵ lat=${latitude}&lon=${longitude}&appid=${key}`, ) const weatherData = await weatherResponse.json() response.json(weatherData) }) 

Это из proxy/start/routes.js

Это требует создания учетной записи Open Weather Map . .env нами ключ API необходимо добавить в .env и .env.example . Затем мы можем начать запрашивать эти данные в браузере. Если погода для этого региона совпадает с одной из предустановок погоды WRLD; мы можем применить его к карте. Мы также можем использовать время браузера, чтобы установить время суток:

 const Wrld = require("wrld.js") const env = require("./env") const keys = { wrld: env.WRLD_KEY, } let map let highlightedFacilities = [] let highlighterMarkers = [] const highlightFacilities = async (latitude, longitude) => { // ...highlight buildings and add markers try { const weatherResponse = await fetch( `http://127.0.0.1:3333/condition? ↵ latitude=${latitude}&longitude=${longitude}`, ) const weatherData = await weatherResponse.json() if (weatherData.weather && weatherData.weather.length > 0) { const condition = weatherData.weather[0].main.toLowerCase() switch (condition) { case "snow": map.themes.setWeather(Wrld.themes.weather.Snowy) break case "few clouds": case "scattered clouds": case "broken clouds": map.themes.setWeather(Wrld.themes.weather.Overcast) break case "mist": map.themes.setWeather(Wrld.themes.weather.Foggy) break case "shower rain": case "rain": case "thunderstorm": map.themes.setWeather(Wrld.themes.weather.Rainy) break default: map.themes.setWeather(Wrld.themes.weather.Clear) break } } const time = new Date().getHours() if (time > 5 && time <= 10) { map.themes.setTime(Wrld.themes.time.Dawn) } else if (time > 10 && time <= 16) { map.themes.setTime(Wrld.themes.time.Day) } else if (time > 16 && time < 21) { map.themes.setTime(Wrld.themes.time.Dusk) } else { map.themes.setTime(Wrld.themes.time.Night) } } catch (e) { // weather and time effects are entirely optional // if they break, for whatever reason, they shouldn't kill the app } } const latitudeInput = document.querySelector("[name='latitude']") const longitudeInput = document.querySelector("[name='longitude']") const applyButton = document.querySelector("[name='apply']") const initMapEvents = async (latitude, longitude) => { map.on("initialstreamingcomplete", () => { highlightFacilities(latitude, longitude) }) map.on("panend", event => { const { lat, lng } = map.getBounds().getCenter() latitudeInput.value = lat longitudeInput.value = lng }) applyButton.addEventListener("click", () => { map.setView([latitudeInput.value, longitudeInput.value]) highlightFacilities(latitudeInput.value, longitudeInput.value) }) } window.addEventListener("load", async () => { navigator.geolocation.getCurrentPosition( position => { // ...create map initMapEvents(latitude, longitude) }, error => { // ...create map initMapEvents(latitude, longitude) }, ) }) 

Это из app/app.js

Я воспользовался возможностью, чтобы переместить весь код создания карты в многократно initMapEvents функцию initMapEvents . Кроме того, я добавил погодные и временные эффекты в функцию highlightBuildings ; поскольку это самое разумное место, чтобы изменить эти вещи. Мы не хотим, чтобы на карте продолжался снег, если пользователь вводит координаты пустыни…

К сожалению, без особой работы время дня всегда будет зависеть от браузера пользователя, но я не думаю, что это важно для этого урока.

Резюме

Это был забавный проект для создания. Более того, это то, что вы могли бы сделать и превратить в бизнес (надеюсь, с большим успехом, чем различные подвиги Джорджа). Может быть, вы обнаружили другое, что людям нужно приложение для поиска. Если у вас есть необходимые разрешения и ограничения учетной записи (например, OpenWeatherMap, Google, Refuge и WRLD), вы можете создать любое приложение для поиска.

На мой взгляд, есть пара способов заработать на таком приложении. Вы можете продать его в магазинах iOS и Android. Вы можете встроить его в приложение React Native или даже в простую оболочку веб-приложения.

Кроме того, вы можете показывать рекламу на экране. Пользователи могут заплатить за удаление этих объявлений, но тогда вам, возможно, придется подумать о входе в аккаунт и / или восстановлении покупок.

В любом случае, это практичная вещь, которую вы можете построить; менее чем в 200 строк кода. Сделайте еще один шаг вперед и добавьте направления для каждой точки интереса. Возможно, даже позволить пользователям фильтровать интересные места, чтобы отображались только замыкания 3…

WRLD имеет большинство инструментов, которые вам нужны.