Делаем карту кабельных трасс «на коленке»

в 13:58, , рубрики: OpenStreetMap, OSM, php, карты, метки:

Привет!
Работая в структуре телеком-провайдинга, я ежедневно сталкиваюсь с такими данными, как схема распределения волокон в оптическом кабеле. Это документ (обычно Visio), где схематически отрисован какой-либо кабель и его разварка на ODF и в муфтах. Выглядит это примерно так:

image

В нашей организации «исторически сложилось» деление кабелей по районам, поэтому на общем сетевом диске есть условная папка «Схемы», а в ней папки по районам. Кабелей много, есть деление по типам. Каждый раз поиск трассы до какого-либо узла осуществлялся последовательным открытием схем кабелей от начала «включения» и до этого узла. Если трасса короткая и находится в пределах района – проблем нет, но когда она включает в себя несколько – это становится утомительным занятием.

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

Итак, задача:

  1. Схематично отрисовать трассу прохождения кабелей
  2. Привязать участки трассы к visio-документам
  3. Осуществить показ документа при клике на участок трассы
  4. Отрисовать пункты в городах

Для реализации используем:

  • QGIS – отрисовка геоданных
  • Sqlite с дополнением spatialite – хранение данных
  • Leaflet – отображение геоданных
  • PHP – магия


Внимание! В статье описан примерный порядок действий для получения простейшей карты.

image Отрисовка геоданных

Скачиваем/устанавливаем QGIS, доставляем плагин QSpatiaLite.

image

Создаем базу данных, соглашаемся на преобразование

image

Хранить данные будем в разных таблицах, т.к. 1 таблица = 1 слой в QGIS, а слои должны быть только идентичных типов (Точка, линия, полигон).

Создаем 2 таблицы и загружаем их в QGIS:

  • Point – для пунктов провайдера
  • Line – соединительные линии

image

image

А дальше, отрисовываем что нам нужно:

Для отрисовки на основе каких-либо карт нужно добавить подложку этих самых карт. В этом поможет плагин QuickMapServices

image

Векторные геоданные теперь у нас есть в базе данных, их можно редактировать в QGIS и выводить куда угодно.

image Отображение геоданных

Для отображения данных будем использовать библиотеку Leaflet (обзор на хабре — раз, два).

Для вывода массива точек и линий на карту Leaflet-у нужен geojson, значит, возьмем в руки PHP.
Нам понадобится прекрасный проект geoPHP, а так же дополнение PHP SpatiaLite — libspatialite. Первый будет получать данные из sqlite и преобразовывать в geojson, а дополнение будет преобразовывать координаты в читаемый формат.

Делаем страницу layer.php, преобразующую данные из БД в geojson:

<?php
# Подключаем geoPHP с экспортом в geojson
include_once('geoPHP/geoPHP.inc');
function wkt_to_json($wkt) {
    $geom = geoPHP::load($wkt,'wkt');
    return $geom->out('json');
}
$layer = $_GET['layer'];

# Подключение к БД
$conn = new SQLite3('myDB.sqlite');

# Добавляем расширение
$conn->loadExtension('libspatialite.so');


# Строим SQL запрос, в зависимости от запрашиваемого слоя
switch ($layer) {
    case 'point':
        $sql = 'SELECT PK_UID, ST_asText(Geometry) AS wkt FROM '.$layer.'';
		break;
	case 'line':
		$sql = 'SELECT PK_UID, ST_asText(Geometry) AS wkt FROM '.$layer.'';
		break;	
}


# Пробуем выполнить запрос или возвращаем ошибку
$rs = $conn->query($sql);
if (!$rs) {
    echo 'An SQL error occured.n';
    exit;
}

# Строим массив GeoJson
$geojson = array(
   'type'      => 'FeatureCollection',
   'features'  => array()
);


while ($row = $rs->fetchArray(SQLITE3_ASSOC)) {
    $properties =  $row;
    # Удаляем ненужное
    unset($properties['wkt']);
    unset($properties['GEOMETRY']);
    $feature = array(
         'type' => 'Feature',
         'geometry' => json_decode(wkt_to_json($row['wkt'])),
         'properties' => $properties
    );
    # Добавляем параметры
    array_push($geojson['features'], $feature);
}
header('Content-type: application/json; charset=utf8');
echo json_encode($geojson, JSON_UNESCAPED_UNICODE);
$conn = NULL;
?>

Закидываем файл на сервер, кладем рядом myDB.sqlite, в котором отрисовано 2 слоя.

В ту же папку кладем библиотеку geoPHP.
В php.ini прописываем путь к расширениям sqlite3, разрешаем динамическую загрузку и закидываем дополнение в указанную папку:

[sqlite3]
sqlite3.extension_dir = /usr/lib/
enable_dl=on;

Теперь при открытии файла layer.php?layer=point будет отображаться geojson c точками из слоя point.

Настала пора отобразить данные на карте.

Создадим простейшую HTML-страницу с библиотекой Leaflet:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Карта сети</title>
        <link rel="stylesheet" href="css/leaflet.css" />
        <link rel="stylesheet" href="css/MarkerCluster.css" />
        <link rel="stylesheet" href="css/MarkerCluster.Default.css" />
		<!--[if lte IE 8]>
             <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.ie.css" />
          <![endif]-->
        <!-- Custom styles for this template -->
        <style>
            html, body, #map {
            height: 100%;
            width: 100%;
            }
        </style>
    </head>

    <body>
        <div id="map"></div>
        <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
        <script type="text/javascript" src="js/leaflet.js"></script>
        <script type="text/javascript" src="js/leaflet.markercluster-src.js"></script>
		<script type="text/javascript">
            var map;
			var minimal = L.tileLayer('http://b.tiles.maps.sputnik.ru/{z}/{x}/{y}.png', {
				attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors',
				minZoom: 0, maxZoom: 18});
            // Overlay Layers
            var line = L.geoJson(null, {
				style: function( feature ) {
					var style = {};
						style.color = "#1110d6";
						style.weight = 4;
						style.opacity = 0.9;
                    return style;

				},
				onEachFeature: function( feature, line ) {
					var id_line = feature.properties.PK_UID;
                    line.bindPopup('<a class="link" href="line/'+id_line+'.png"" target="_blank">Схема</a>');

				}
			});
            $.getJSON("layer.php?layer=line", function (data) {
                line.addData(data);
            });
			var points = new L.MarkerClusterGroup({ spiderfyOnMaxZoom: false, showCoverageOnHover: false,disableClusteringAtZoom: 15});
			var point = L.geoJson(null, { 
				onEachFeature: function( feature, point ) {
						var aa = feature.geometry.coordinates;
						var id = feature.properties.PK_UID;
						var sw = new L.Marker( new L.LatLng(aa[1], aa[0]));
						sw.bindPopup('<a class="link" target="_blank">'+id+'</a>');
						points.addLayer(sw);
					}	
			});	
			$.getJSON("layer.php?layer=point", function (data) {
                point.addData(data);
            });		

            map = L.map("map", {layers: [minimal,line,points]});
	        map.setView([57.545995,29.930411], 6);
	
            var baseLayers = {
				"OSM": minimal
            };

            var overlays = {
			"Оптические кабели": line,
			"Пункты": points				
            };
            
			var scaleControl = L.control.scale();
            var layerControl = L.control.layers(baseLayers, overlays).addTo(map);
    
        </script>
    </body>
</html>

Останется конвертировать схемы из VSD в PNG и положить в папку line рядом с файлами карты.

Автор: muz1kand

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js