Leaflet: как поменять местами координаты X, Y при загрузке сведений о географических объектах в формате GeoJSON
В последнее время мне приходится создавать достаточно много интерактивных карт на основе библиотеки Leaflet, в связи с чем периодически возникают различного рода нетривиальные задачи, которые можно при этом решить очень просто. Попробую написать несколько статей, которые возможно кому-нибудь помогут и существенно упростят жизнь.
Недавно мне прислали файл в формате JSON, который содержал географическое описание большого количества объектов (точек и полигонов) в системе координат WGS-84. К сожалению в нем были поменяны местами координаты, в связи с чем объекты отображались на карте несколько не в том месте, в котором должны были находиться. Следовательно возникла проблема с которой легко справляется любая геоинформационная система - поменять местами координаты X, Y.
Координаты в Leaflet записываются в особом типе данных latLng, для создания которого используется специальная функция L.latLng (подробное описание можно посмотреть по ссылке: https://spec-zone.ru/leaflet~1.3/index#latlng-l-latlng). При загрузке данных из JSON я использую функцию (JS) JSON.parse примерно в таком виде:
point=JSON.parse(json_point);
polygon=JSON.parse(json_polygon);
где point - соответствующий точечный объект, который должен превратиться в маркер на карте, polygon - соответствующий площадной объект, который должен превратиться в полигон на карте, json_point, json_polygon - само описание объекта в формате JSON, я загружаю его либо напрямую из картографического сервера, либо из файла при помощи PHP-вставки:
В простом варианте можно JSON описание объекта написать в виде строки, непосредственно в значении переменной внутри скрипта.
Рассмотрим простую ситуацию, когда в файле находится только 1 объект, а координаты, после выполнения функции point=JSON.parse(json_point), описаны в массиве point.geometry.coordinates.
Но координаты поменяны местами изначально, и нам нужно их "развернуть"!
Тогда мы выполняем такое нехитрое действие:
var p_coord=L.latLng(point.geometry.coordinates[1],point.geometry.coordinates[0]);
таким образом переменная p_coord будет приведена к необходимому типу latLng и содержать координаты в правильной последовательности.
соответственно далее я вызываю маркер: L.marker(p_coord).addTo(map) и радуюсь, что все получилось.
Казалось бы все очень просто в отношении одной точки, но что произойдет если будет многоконтурный полигон с перевернутыми координатами?
Ситуация существенно осложняется тем, что после исполнения функции JSON.parse, уже получена переменная, содержащая в себе многомерный массив с координатами, структуру которого мне найти не удалось. Однако разбор массива на элементы показал, что он имеет следующую структуру:
array [номер контура] [0] [номер пары координат] [0 - координата X, 1 - координата Y]
Понимание структуры массива позволяет написать функцию для замены координат:
function rotXY(arr) {
try {
for (let i = 0; i < arr.length ; i=i+1 ) //перебор контуров
for (let j = 0; j < arr[i][0].length ; j=j+1 ) { //перебор пар координат
var a=arr[i][0][j][1];
arr[i][0][j][1]=arr[i][0][j][0];
arr[i][0][j][0]=a;
}
} catch (e) {} return (arr);
}
Тогда полигон на карте создается функцией L.polygon(rotXY(polygon.geometry.coordinates), polyOptions).
Данная ситуация достаточно часто встречается при работе с географическими данными из различных источников, надеюсь, что оно будет кому-нибудь полезным. Главное знать структуру массива с координатами полигона.
Ниже представлен листинг примера кода (точка и двухконтурный полигон расположены недалеко от г. Златоуст, но из-за особенностей выгрузки координаты поменяны местами и они оказываются севернее г. Пермь )
<html>
<head>
<title>Leaflet sample</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@latest/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet@latest/dist/leaflet.js"></script>
</head>
<body>
<div id = "map" style = "width: 100%; height: 98vh; margin:0;"></div>
</body>
<script>
var thermopoints = L.layerGroup();
var thermopolygons = L.layerGroup();
var burnpolygons = L.layerGroup();
// Создание карты
var mapOptions = {
center: [55.158, 58.968],
zoom: 14,
attributionControl: false,
}
var map = new L.map('map', mapOptions);
// Подложка OSM
var layer = new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'); // OSM
map.addLayer(layer);
// Стиль отображения полигонов
var polyOptions = {color: 'black', weight:0.5, fillColor:'#ffcf40', fillOpacity: 0.4, fillRule: 'evenodd'};
// Описание точки
window.json_point='{"type" : "Feature", "geometry" : {"type":"Point","coordinates":[58.968,55.158]}}';
point=JSON.parse(window.json_point);
var p_coord=L.latLng(point.geometry.coordinates[1],point.geometry.coordinates[0]);
//Неправильная точка (до "переворота координат")
var error_pp = L.marker(point.geometry.coordinates).addTo(map);
//точка с правильными координатами (после "переворота координат")
var pp = L.marker(p_coord).addTo(map);
//Описание полигона
window.json_polygon='{"type" : "Feature", "geometry" : {"type":"MultiPolygon","coordinates":[[[[58.971350517,55.15787519],[58.97318157,55.15787519],[58.97318132,55.15450273],[58.96727888,55.15450273],[58.967278797,55.15562044],[58.96544775,55.15562044],[58.9654475,55.1589929],[58.9713506,55.1589929],[58.971350517,55.15787519]]],[[[58.96271584,55.15936266],[58.9685893,55.15936266],[58.96858955,55.16273512],[58.96271589,55.16273512],[58.96271584,55.15936266]]]]}}';
polygon=JSON.parse(window.json_polygon);
//Неправильный полигон (до "переворота координат")
var error_ppol = L.polygon(polygon.geometry.coordinates, polyOptions).addTo(map);
//Полигон с правильными координатами (после "переворота координат")
var ppol = L.polygon(rotXY(polygon.geometry.coordinates), polyOptions).addTo(map);
function rotXY(arr) //функция перемены координат для полигона
{
try {
for (let i = 0; i < arr.length ; i=i+1 )
for (let j = 0; j < arr[i][0].length ; j=j+1 ) {
var a=arr[i][0][j][1];
arr[i][0][j][1]=arr[i][0][j][0];
arr[i][0][j][0]=a;
}
}
catch (e) {}
return (arr);
}
</script>
</html>