Геоинформатика. Работа с leaflet

В этом занятии мы научимся создавать интернет-страницу, в которою будет встроена карта вашего района.
Для работы нам понадобится текстовый редактор (блокнот). Рекомендую вместо стандартного блокнота использовать программу Notepad++. Для вставки карты на сайт мы будем использовать JavaScript библиотеку с открытым исходным кодом Leaflet. Также потребуется проект QGIS по району города.

Создадим новый текстовый документ формата .txt и добавим в него следующий html-код:

  1.   <head>
  2.     <title>Название страницы</title>
  3.   </head>
  4.   <body>
  5.     <h1>
  6.       Заголовок страницы
  7.     </h1>
  8.     <p>
  9.       Текст страницы
  10.     </p>
  11.   </body>
  12. </html>

Это начальная структура любого сайта. Далее следует сохранить и закрыть текстовый документ. Сменить расширение файла с .txt на .html (в свойствах папок должен быть установлен показ расширений файлов) и открыть файл в браузере.
Для использования Leaflet на вашем сайте необходимо в заголовок сайта (head) добавить ссылки на CSS-стили и код Leaflet:

  1.   <link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css"
  2.    integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
  3.    crossorigin=""/>
  4.   <script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"
  5.    integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
  6.    crossorigin=""></script>

В тело сайта (body) нужно добавить div-элемент с id=map и заданными размерами:

  1. <div id="map"  style="position: absolute; left: 10px; right: 10px; top: 100px; bottom: 10px; overflow:false;"></div>

Создаем блок script в теле сайта. Итоговый html-код представлен ниже:

  1.   <head>
  2.     <title>Название страницы</title>
  3.        
  4.  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css"
  5.   integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
  6.   crossorigin=""/>
  7.  <script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"
  8.   integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
  9.   crossorigin=""></script>
  10.   </head>
  11.   <body>
  12.     <h1>
  13.       Заголовок страницы
  14.     </h1>
  15.     <p>
  16.       Текст страницы
  17.     </p>
  18.     <div id="map"  style="position: absolute; left: 10px; right: 10px; top: 100px; bottom: 10px; overflow:false;"></div>
  19.     <script type="text/javascript">
  20.       //Здесь будет писаться весь дальнейший код
  21.     </script>
  22.   </body>
  23. </html>

Весь нижеследующий код должен писаться в блок script в теле сайта.
Теперь мы можем инициализировать карту и установить географические координаты центра карты и масштаб:

  1. var map = L.map('map').setView([55.753, 37.619], 13);

Мы получили контейнер карты с серым фоном. Теперь надо подключить базовую картографическую подложку. В примере мы будем использовать тайловый слой OSM:

  1. L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  2.     attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
  3. }).addTo(map);

Маркеры

Добавить стандартный маркер на карту:

  1. L.marker([51.5, -0.09]).addTo(map)
  2.     .bindPopup('A pretty CSS3 popup.<br> Easily customizable.')
  3.     .openPopup();

Стилизованный маркер:
Чтобы создать собственный маркер, необходимо два изображения — изображение маркера и изображение его тени. Формат изображения png с прозрачным фоном.

Стиль маркера в Leaflet определяется объектами L.Icon, которые передаются в качестве опции при создании маркеров. Создадим собственную иконку:

  1. var greenIcon = L.icon({
  2.     iconUrl: 'leaf-green.png',
  3.     shadowUrl: 'leaf-shadow.png',
  4.  
  5.     iconSize:     [38, 95], // размер значка
  6.     shadowSize:   [50, 64], // размер тени
  7.     iconAnchor:   [22, 94], // точка привязки значка
  8.     shadowAnchor: [4, 62],  // точка привязки тени
  9.     popupAnchor:  [-3, -76] // точка указателя всплывающего меню
  10. });

Разместим маркер на карте:

  1. L.marker([51.5, -0.09], {icon: greenIcon}).addTo(map);

Работа с GeoJson

Добавить геометрию из файла geojson:
В QGIS сохраняем слой границ как bound.geojson
Открываем файл bound.geojson в текстовом редакторе, в начале файла объявляем переменную adm. Сохраняем файл. Пример файла с объявленной переменной*:

  1. var adm = {
  2. "type": "FeatureCollection",
  3. "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
  4. "features": [
  5. //массив объектов слоя
  6. ]
  7. }

*это не совсем корректно, т.к. измененный geojson не будет открываться в ГИС, но такой подход позволяет работать с геометрией geojson локально, без использования сервера.

Подключаем в заголовке сайта (head) файл bound.geojson (файл должен располагаться в одной папке со страницей сайта):

  1. <script src="bound.geojson"></script>

Добавляем слой geoJson (хранится в объявленной переменной adm) на карту:

  1. L.geoJson(adm).addTo(map);

Для стилизации слоя используется атрибут style:

  1. var myStyle = {
  2.     "color": "#ff7800",        //цвет линии
  3.     "weight": 5,                  //ширина линии в px
  4.     "opacity": 0.65,            //непрозрачность линии
  5.     "dashArray": '3,5',        //пунктирная линия
  6.     "lineJoin": 'miter',         //стиль соединения в углах, по умолчанию 'round'
  7.     "lineCap": 'square'        //стиль законцовок, по умолчанию 'round'
  8.     "fillColor": "#800026", //цвет фона
  9.     "fillOpacity": 0.65       //непрозрачность фона
  10. };
  11.  
  12. L.geoJSON(adm, {
  13.     style: myStyle
  14. }).addTo(map);

Добавить всплывающее окно по клику на объекте (в слое adm должно быть поле popupContent с содержимым всплывающего окна, например: "Привет!"):

  1. function onEachFeature(feature, layer) {
  2.     if (feature.properties && feature.properties.popupContent) {
  3.         layer.bindPopup(feature.properties.popupContent);
  4.     }
  5. }
  6.  
  7. L.geoJSON(adm, {
  8.     onEachFeature: onEachFeature
  9. }).addTo(map);

Добавление события при клике по объекту (в примере по клику на объект карта перемещается к объекту):

  1. function zoomToFeature(e) {
  2.     map.fitBounds(e.target.getBounds());
  3. }
  4.  
  5. function onEachFeature(feature, layer) {
  6.     layer.on({click: zoomToFeature});
  7. }
  8.  
  9. geojson = L.geoJson(adm, {
  10.     onEachFeature: onEachFeature
  11. }).addTo(map);

Группы слоев

Предположим, у вас есть несколько слоев, которые вы хотите объединить в группу, чтобы обрабатывать их как один в своем коде:

  1. var littleton = L.marker([39.61, -105.02]).bindPopup('This is Littleton, CO.'),
  2.     denver    = L.marker([39.74, -104.99]).bindPopup('This is Denver, CO.'),
  3.     aurora    = L.marker([39.73, -104.8]).bindPopup('This is Aurora, CO.'),
  4.     golden    = L.marker([39.77, -105.23]).bindPopup('This is Golden, CO.');

Вместо добавления их непосредственно на карту вы можете сделать следующее, используя класс LayerGroup:

  1. var cities = L.layerGroup([littleton, denver, aurora, golden]);

Теперь слой cities, объединяющий маркеры городов в один слой, который можно сразу добавить или удалить с карты.

Управление слоями

Leaflet имеет приятный небольшой элемент управления, который позволяет пользователям контролировать, какие слои они видят на карте.
Существует два типа слоев: базовые слои, которые являются взаимоисключающими (на карте одновременно может быть виден только один), например картографические основы, и (2) наложения, которые представляют собой тематические элементы, которые отображаются поверх базового слоя.
Создание базовых слоев:

  1. var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  2.     attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
  3. }).addTo(map);
  4.  
  5. var osmHOT = L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
  6.     maxZoom: 19,
  7.     attribution: '© OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France'});

Далее создадим два объекта. Один будет содержать наши базовые слои, а другой — тематические объекты.

  1. var baseMaps = {
  2.     "OpenStreetMap": osm,
  3.     "OpenStreetMap.HOT": osmHOT
  4. };
  5.  
  6. var overlayMaps = {
  7.     "Cities": cities
  8. };

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

  1. var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);

Возможно стилизовать клавиши объектов для слоев. Например, этот код сделает метку карты OpenStreetMap.HOT красной:

  1. var baseMaps = {
  2.     "OpenStreetMap": osm,
  3.     "<span style='color: red'>OpenStreetMap.HOT</span>": osmHOT
  4. };

Работа с событиями

Каждый раз, когда что-то происходит в Leaflet, например, пользователь нажимает на маркер или изменяется масштаб карты, соответствующий объект отправляет событие, на которое вы можете подписаться с помощью функции. Он позволяет реагировать на взаимодействие с пользователем, например: открывается окно с координатами при клике на карту:

  1. var popup = L.popup();
  2.  
  3. function onMapClick(e) {
  4.     popup
  5.         .setLatLng(e.latlng)
  6.         .setContent("Координаты клика: " + e.latlng.toString())
  7.         .openOn(map);
  8. }
  9.  
  10. map.on('click', onMapClick);