Русский
Русский
English
Статистика
Реклама

Map

JavaScript за 60 секунд работаем с картой (Geolocation API, Leaflet.js, Nominatim)

14.12.2020 12:16:00 | Автор: admin


Доброго времени суток, друзья!

В этом небольшом туториале мы вместе с вами выполним три простых задания:

  • С помощью Geolocation API и Leaflet.js определим текущее местоположение пользователя и отобразим его на карте
  • Реализуем анимированный переход между городами
  • Реализуем переключение между адресами с предварительным получением названия объекта и его координат

Код проекта находится здесь.

Поиграть с кодом можно здесь:


Определяем текущее местоположение пользователя


Geolocation API позволяет пользователю предоставлять веб-приложению данные о своем местоположении. В приложении для запроса этих данных используется метод Geolocation.getCurrentPosition(). Данный метод принимает один обязательный и два опциональных параметра: success функция обратного вызова, получающая объект Position при предоставлении разрешения, error функция обратного вызова, получающая объект PositionError при отказе в доступе, и options объект с настройками. Вот как это выглядит в коде:

navigator.geolocation.getCurrentPosition(success, error, {  // высокая точность  enableHighAccuracy: true})function success({ coords }) {  // получаем широту и долготу  const { latitude, longitude } = coords  const position = [latitude, longitude]  console.log(position) // [широта, долгота]}function error({ message }) {  console.log(message) // при отказе в доступе получаем PositionError: User denied Geolocation}

Отображаем местоположение пользователя на карте


В качестве карты мы будем использовать Leaflet.js. Данный сервис является альтернативой Google Maps и OpenStreetMap, уступает им по функционалу, но подкупает простотой интерфейса. Создаем разметку, в которой подключаем стили и скрипт карты:

<head>  <!-- стили карты -->  <link      rel="stylesheet"      href="http://personeltest.ru/aways/unpkg.com/leaflet@1.7.1/dist/leaflet.css"      integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="      crossorigin=""    />    <!-- скрипт карты -->    <script      src="http://personeltest.ru/aways/unpkg.com/leaflet@1.7.1/dist/leaflet.js"      integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="      crossorigin=""    ></script>    <!-- наши стили -->    <link rel="stylesheet" href="style.css" /></head><body>  <!-- контейнер для карты -->  <div id="map"></div>  <!-- кнопка для вызова функции -->  <button id="my_position">My Position</button>  <!-- наш скрипт-модуль -->  <script src="script.js" type="module"></script></body>

Добавляем минимальные стили (style.css):

* {  margin: 0;  padding: 0;  box-sizing: border-box;}body {  min-height: 100vh;  display: grid;  place-content: center;  place-items: center;  background-color: rgb(241, 241, 241);}#map {  width: 480px;  height: 320px;  border-radius: 4px;  box-shadow: 0 0 1px #222;}button {  padding: 0.25em 0.75em;  margin: 1em 0.5em;  cursor: pointer;  user-select: none;}

Создаем модуль map.js следующего содержания:

// создаем локальные переменные для карты и маркера// каждый модуль имеет собственное пространство именlet map = nulllet marker = null// функция принимает позицию - массив с широтой и долготой// и сообщение, отображаемое над маркером (tooltip)export function getMap(position, tooltip) {  // если карта не была инициализирована  if (map === null) {    // второй аргумент, принимаемый методом setView - это масштаб (zoom)    map = L.map('map').setView(position, 15)  } else return  // что-то типа рекламы  // без этого карта работать не будет  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {    attribution:      ' <a href="http://personeltest.ru/aways/www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'  }).addTo(map)  // добавляем маркер с сообщением  L.marker(position).addTo(map).bindPopup(tooltip).openPopup()}

Наконец, создаем script.js:

// импортируем функциюimport { getMap } from './map.js'// находим кнопку и добавляем к ней обработчикdocument.getElementById('my_position').onclick = () => {  navigator.geolocation.getCurrentPosition(success, error, {    enableHighAccuracy: true  })}function success({ coords }) {  const { latitude, longitude } = coords  const currentPosition = [latitude, longitude]  // вызываем функцию, передавая ей текущую позицию и сообщение  getMap(currentPosition, 'You are here')}function error({ message }) {  console.log(message)}

Открываем index.html в браузере, нажимаем на кнопку, предоставляем разрешение на получение данных о местоположении, видим нашу позицию на карте.



Отлично. Двигаемся дальше.

Анимированный переход между городами


Предположим, что у нас имеется объект с тремя городами (Москва, Санкт-Петербург, Екатеринбург) и их координатами (db/cities.json):

{  "Moscow": {    "lat": "55.7522200",    "lon": "37.6155600"  },  "Saint-Petersburg": {    "lat": "59.9386300",    "lon": "30.3141300"  },  "Ekaterinburg": {    "lat": "56.8519000",    "lon": "60.6122000"  }}

Нам необходимо реализовать плавное переключение между этими городами на карте.

Добавляем в разметку контейнер для городов:

<div id="cities"></div>

Переписываем script.js:

import { getMap } from './map.js'// получаем контейнер для городовconst $cities = document.getElementById('cities');(async () => {  // получаем объект с городами  const response = await fetch('./db/cities.json')  const cities = await response.json()  // перебираем города  for (const city in cities) {    // создаем кнопку    const $button = document.createElement('button')    // текстовое содержимое кнопки - название города    $button.textContent = city    // получаем широту и долготу    const { lat, lon } = cities[city]    // записываем название города, широту и долготу    // в соответствующие data-атрибуты    $button.dataset.city = city    $button.dataset.lat = lat    $button.dataset.lon = lon    // добавляем кнопку в контейнер    $cities.append($button)  }})()// обрабатываем нажатие кнопки$cities.addEventListener('click', ({ target }) => {  // нас интересует только нажатие кнопки  if (target.tagName !== 'BUTTON') return  // получаем название города, широту и долготу из data-атрибутов  const { city, lat, lon } = target.dataset  const position = [lat, lon]  // вызываем функцию, передавая ей позицию и название города  getMap(position, city)})

Также немного изменим map.js:

let map = nulllet marker = nullexport function getMap(position, tooltip) {  if (map === null) {    map = L.map('map').setView(position, 15)  } else {    // перемещение к следующей позиции    map.flyTo(position)  }  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {    attribution:      ' <a href="http://personeltest.ru/aways/www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'  }).addTo(map)  // удаление предыдущего маркера  if (marker) {    map.removeLayer(marker)  }  marker = new L.Marker(position).addTo(map).bindPopup(tooltip).openPopup()}

Открываем index.html. При нажатии первой кнопки сразу получаем позицию и название города. При нажатии второй и последующих кнопок плавно перемещаемся между городами.



Плавное переключением между адресами


Предположим, что у нас имеются три объекта с названиями и адресами (db/addresses.json):

{  "Театр драмы": "Октябрьская площадь, 2",  "Театр оперы и балета": "Проспект Ленина, 46А",  "Коляда-Театр": "Проспект Ленина, 97"}

Нам необходимо реализовать переключение между этими объектами на карте. Но как нам это сделать без координат? Никак. Следовательно, нам каким-то образом нужно эти координаты получить. Для этого воспользуемся сервисом Nominatim от OpenStreetMap. О том, как правильно сформировать строку запроса, смотрите здесь. Я продемонстрирую лишь один из возможных вариантов.

Итак, создаем в разметке контейнер для адресов:

<div id="addresses"></div>

Переписываем script.js:

// получаем контейнер для адресовconst $addresses = document.getElementById('addresses');(async () => {  // названия и адреса объектов  const response = await fetch('./db/addresses.json')  const addresses = await response.json()  // для каждого места  for (const place in addresses) {    // создаем кнопку    const $button = document.createElement('button')    $button.textContent = place    // получаем адрес    const address = addresses[place]    // формируем строку запроса    const query = address.replace(      /([А-ЯЁа-яё]+)\s([А-ЯЁа-яё]+),\s([0-9А-ЯЁа-яё]+)/,      '$3+$1+$2,+Екатеринбург'    )    // получаем, например, 2+Октябрьская+площадь,+Екатеринбург    // записываем данные в соответствующие data-атрибуты    $button.dataset.address = address    $button.dataset.query = query    $addresses.append($button)  }})()// обрабатываем нажатие кнопки$addresses.addEventListener('click', async ({ target }) => {  if (target.tagName !== 'BUTTON') return  // получаем данные из data-атрибутов  const { address, query } = target.dataset  // получаем ответ от сервиса  const response = await fetch(    `https://nominatim.openstreetmap.org/search?q=${query}&format=json&limit=1`  )  // format - формат данных, limit - количество объектов с данными  // парсим ответ, извлекая нужные сведения  const { display_name, lat, lon } = (await response.json())[0]  // редактриуем название объекта  const name = display_name.match(/[А-ЯЁа-яё\s(\-)]+/)[0]  const position = [lat, lon]  // формируем сообщение  const tooltip = `${name}<br>${address}`  // вызываем функцию  getMap(position, tooltip)})

Открываем index.html. При нажатии первой кнопки сразу получаем позицию и название театра. При нажатии второй и последующих кнопок плавно перемещаемся между театрами.



Круто. Все работает, как ожидается.

На этом позвольте откланяться. Надеюсь, вы нашли для себя что-нибудь интересное. Благодарю за внимание и хорошего дня.
Подробнее..

Немного об использовании regex в map nginx

21.05.2021 18:18:17 | Автор: admin

Давно ничего не писал, поэтому разбавим конец пятницы простыми, но не всегда очевидными иcканиями в Nginx.

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

Поскольку переменные вычисляются только в момент использования, само по себе наличие даже большого числа объявлений переменных map не влечёт за собой никаких дополнительных расходов на обработку запросов.

И здесь важным является не только, то что "map не влечёт за собой никаких дополнительных расходов на обработку запросов", а и то, что "переменные вычисляются только в момент использования".

Как известно, конфиг Nginx, в основном, декларативен. Это касается и директивы map, и, не смотря на то, что она расположена в контексте http, её вычисление не происходит до момента обработки запроса. То есть при использовании результирующей переменной в контекстах server, location, if и т.п. мы "подставляем" не готовый результат вычисления, а лишь "формулу" по который этот результат будет вычислен в нужный момент. В этой конфигурационной казуистике не возникает проблем до того момента, пока мы не используем регулярные выражения. А именно регулярные выражения с выделениями. А ещё точнее, регулярные выражения с неименованными выделениями. Проще показать на примере.

Допустим у нас есть домен example.com с множеством поддоменов 3-го уровня, а-ля ru.example.com, en.example.com, de.example.com и т.д., и мы хотим их перенаправить на новые поддомены ru.example.org, en.example.org, de.example.org и т.п. Вместо того чтобы описывать сотни строк редиректов мы поступим вот так:

map $host $redirect_host {  default "example.org";  "~^(\S+)\.example\.com$"  $1.example.org;}server {    listen       *:80;    server_name  .example.com;  location / {        rewrite ^(.*)$ https://$redirect_host$1 permanent;    }

Здесь мы ошибочно ожидали, что при запросе ru.example.com произойдет вычисление регулярки в map и, соответственно, попав в location переменная $redirect_host будет содержать значение ru.example.org, однако на деле это не так:

$ GET -Sd ru.example.comGET http://ru.example.com301 Moved PermanentlyGET https://ru.example.orgru

Оказалось, что на момент исполнения запроса наша переменная равна ru.example.orgru. Всё из-за того, что мы пренебрегли предупреждением "переменные вычисляются только в момент использования", а в нашем rewrite оказалась некая регулярка вложенная в регулярку.

Самый простой вариант решения - не использовать regexp одновременно и в map и в месте вычисления переменной, например, для данного конкретного случая это может выглядеть так:

map $host $redirect_host {  default "example.org";  "~^(\S+)\.example\.com$"  $1.example.org;}server {    listen       *:80;    server_name  .example.com;    location / {        return 301 https://$redirect_host$request_uri;    }}

Но что делать, если альтернативного решения без регулярок нет (ну или просто очень хочется).
Пробуем использовать именованные выделения в map:

map $host $redirect_host {  default "example.org";  "~^(?<domainlevel3>\S+)\.example\.com$"  $domainlevel3.example.org;}server {    listen       *:80;    server_name  .example.com;    location / {        rewrite ^(.*)$ https://$redirect_host$1 permanent;    }}

Попытка не увенчалась успехом:

$ GET -Sd ru.example.comGET http://ru.example.com301 Moved PermanentlyGET https://ru.example.orgru

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

map $host $redirect_host {  default "example.org";  "~^(?<domainlevel3>\S+)\.example\.com$"  $domainlevel3.example.org;}server {    listen       *:80;    server_name  .example.com;    location / {        rewrite ^(?<requri>.*)$ https://$redirect_host$requri permanent;    }}

И теперь всё работает как ожидалось:

$ GET -Sd ru.example.comGET http://ru.example.com301 Moved PermanentlyGET https://ru.example.org/
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru