Привет!
Работая в структуре телеком-провайдинга, я ежедневно сталкиваюсь с такими данными, как схема распределения волокон в оптическом кабеле. Это документ (обычно Visio), где схематически отрисован какой-либо кабель и его разварка на ODF и в муфтах. Выглядит это примерно так:
В нашей организации «исторически сложилось» деление кабелей по районам, поэтому на общем сетевом диске есть условная папка «Схемы», а в ней папки по районам. Кабелей много, есть деление по типам. Каждый раз поиск трассы до какого-либо узла осуществлялся последовательным открытием схем кабелей от начала «включения» и до этого узла. Если трасса короткая и находится в пределах района – проблем нет, но когда она включает в себя несколько – это становится утомительным занятием.
Ввиду того что, однажды я заинтересовался таким проектом как OpenStreetMap, было принято решение отрисовать схему прохождения кабелей поверх картографической основы и научить эту схему быть «интерактивной».
Итак, задача:
- Схематично отрисовать трассу прохождения кабелей
- Привязать участки трассы к visio-документам
- Осуществить показ документа при клике на участок трассы
- Отрисовать пункты в городах
Для реализации используем:
- QGIS – отрисовка геоданных
- Sqlite с дополнением spatialite – хранение данных
- Leaflet – отображение геоданных
- PHP – магия
Внимание! В статье описан примерный порядок действий для получения простейшей карты.
Отрисовка геоданных
Скачиваем/устанавливаем QGIS, доставляем плагин QSpatiaLite.
Создаем базу данных, соглашаемся на преобразование
Хранить данные будем в разных таблицах, т.к. 1 таблица = 1 слой в QGIS, а слои должны быть только идентичных типов (Точка, линия, полигон).
Создаем 2 таблицы и загружаем их в QGIS:
- Point – для пунктов провайдера
- Line – соединительные линии
Векторные геоданные теперь у нас есть в базе данных, их можно редактировать в QGIS и выводить куда угодно.
Отображение геоданных
Для отображения данных будем использовать библиотеку Leaflet (обзор на хабре — раз, два).
Для вывода массива точек и линий на карту Leaflet-у нужен geojson, значит, возьмем в руки PHP.
Нам понадобится прекрасный проект geoPHP, а так же дополнение PHP SpatiaLite — libspatialite. Первый будет получать данные из sqlite и преобразовывать в 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.
Настала пора отобразить данные на карте.
<!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