Как я могу преобразовать geoshape / geotrace / geopoint в GeoJSON?
Я использую API для доступа к пространственным данным, собранным с помощью kobotoolbox. API возвращает пространственные данные в виде geoshape / geotrace / geopoint, но мне нужно скрыть эти данные в Geojson, чтобы я мог отображать их на моей карте с помощью листовки.
вот образец строки геоформы
'-6.725577650887138 39.10606026649475 0.0 0.0;-6.72631550943841 39.10506717860699 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.725577650887138 39.10606026649475 0.0 0.0;'
Заранее большое спасибо!
1 ответ
Эта «геоформа» не похожа на какой либо -известный формат векторных данных, а скорее похожа на строку разделенных 4-элементных векторов, которые, в свою очередь, представляют собой числа, разделенные пробелами.
Нужен наивный разбор.
Начните с разделения строки на массив строк с помощью
String.prototype.split()
:
let geoshape = '-6.725577650887138 39.10606026649475 0.0 0.0;-6.72631550943841 39.10506717860699 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.725577650887138 39.10606026649475 0.0 0.0;';
let points = geoshape.split(';');
Сейчас
points
является из с. Мы можем превратить каждую из этих строк в строку из s, предоставив функцию, которая разбивает ее на пробелы:
function spaceSeparatedStringToArray(str) {
return str.split(' ');
}
... и еще одна функция, которая заставляет
String
содержащий текстовое представление числа в , нравиться...
function stringToNumber(str) {
return Number(str);
}
... а теперь давайте в некоторых
Array.prototype.map()
магия и некоторая магия цепочки методов , чтобы сделать функцию, в которой вход представляет собой строку, разделенную пробелами, а выход представляет собой
Array
из s:
function stringToNumber(str) { return Number(str); }
function spaceSeparatedStringToArrayOfNumbers(str) {
return str.split(' ').map(stringToNumber);
}
Иногда лучше определить те вспомогательные функции, которые передаются (или
reduce()
или же
filter()
и т. д.) как анонимные лямбда-функции , например:
function spaceSeparatedStringToArrayOfNumbers(str) {
return str.split(' ').map(function(str){ return Number(str) });
}
Вместо этого вы можете использовать синтаксис стрелочной функции, если хотите:
function spaceSeparatedStringToArrayOfNumbers(str) {
return str.split(' ').map(str=>Number(str) );
}
И поскольку мы используем Number
как функцию , мы можем напрямую использовать это как параметр
map()
:
function spaceSeparatedStringToArrayOfNumbers(str) {
return str.split(' ').map(Number);
}
Теперь, когда у нас есть эта функция, давайте вернемся к началу и применим эту функцию к каждой из разделенных подстроками:
function spaceSeparatedStringToArrayOfNumbers(str) {
return str.split(' ').map(Number);
}
let geoshape = '-6.725577650887138 39.10606026649475 0.0 0.0;-6.72631550943841 39.10506717860699 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.725577650887138 39.10606026649475 0.0 0.0;';
let points = geoshape.split(';');
let vectors = points.map(spaceSeparatedStringToArrayOfNumbers);
Давайте изменим это, чтобы добавить еще больше лямбда, цепочки методов и синтаксиса стрелок:
let vectors = geoshape.split(';').map((str)=>str.split(' ').map(Number));
Кроме того, поскольку формат GeoJSON ожидает, что каждая координата будет двухкомпонентным вектором, а не четырехкомпонентным вектором, давайте
map
каждый вектор снова, чтобы сохранить только первые два компонента. Здесь используется деструктуризация массива :
let vectors = geoshape
.split(';')
.map((str)=>str.split(' ').map(Number))
.map(([x,y,w,z])=>[x,y]);
Видеть? Я определил лямбда-функцию, которая принимает единственный аргумент, предполагает, что это массив (чисел) с длиной 4, разрушает массив, а затем возвращает новый массив, содержащий первые два элемента. Он также может работать как:
let coords = geoshape
.split(';')
.map((str)=>str.split(' ').map(Number))
.map(([x,y])=>[x,y]);
Ваш образец данных заканчивается
;
, и это вызывает пустую строку, поэтому я тоже отфильтрую это:
let coords = geoshape
.split(';')
.filter(str=> str!=='')
.map((str)=>str.split(' ').map(Number))
.map(([x,y])=>[x,y]);
Я предполагаю, что вы работаете в Дар-эс-Саламе, а не в Бадахосе, поэтому давайте поменяем широту и долготу (см. Также https://macwright.com/lonlat/ ), поменяв местами
x
а также
y
в
([x,y])=>[y,x]
:
let coords = geoshape
.split(';')
.filter(str=> str!=='')
.map((str)=>str.split(' ').map(Number))
.map(([x,y])=>[y,x]);
Теперь на перейдитеgeojson.org и прочтите RFC 7946, чтобы узнать, как указываются структуры данных GeoJSON. У нас уже есть координаты геометрии LineString, поэтому все, что нужно, - это немного обернуть:
let geojsonFeature = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": coords
}
};
Если ваш образец набора данных должен быть многоугольником с внешним корпусом и без внутренних корпусов (прочтите OGC SFA, чтобы узнать, что «корпус» означает в этом контексте), тогда вы можете заключить координаты в дополнительный встроенный массив и указать
Polygon
как тип геометрии:
let geojsonFeature = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [ coords ]
}
};
Теперь ты можешь накормить его
L.geoJson
, например
let line = L.geoJson(geojsonFeature).addTo(map);
И, наконец, я собираю все вместе просто ради этого:
let geoshape = '-6.725577650887138 39.10606026649475 0.0 0.0;-6.72631550943841 39.10506717860699 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.727484560110362 39.10561669617891 0.0 0.0;-6.725577650887138 39.10606026649475 0.0 0.0;';
let leafletLine = L.geoJson({
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": geoshape
.split(';')
.filter(str=> str!=='')
.map((str)=>str.split(' ').map(Number))
.map(([x,y])=>[y,x])
}
}).addTo(map);
См. Рабочий пример здесь . И, пожалуйста , не копируйте код вслепую. Прочтите связанную документацию и ресурсы и поймите, что происходит.