如何将 geoshape/geotrace/geopoint 转换为 GeoJSON?

How can I convert a geoshape/geotrace/geopoint to GeoJSON?

我正在使用 API 访问使用 kobotoolbox 收集的空间数据。 api returns 空间数据作为 geoshape/geotrace/geopoint,但我需要将该数据转换为 Geojson,以便我可以使用传单将它们显示在我的地图上。

这里是 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;'

非常感谢!

“geoshape”看起来不像任何 well-known vector data format,而是看起来像一串 ; 分隔的 4 元素向量,而这些向量又是 space 分隔的数字。

一些天真的解析是有序的。

首先将字符串拆分为字符串数组 with 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(';');

现在 pointsStringArray。我们可以通过提供一个按 spaces:

拆分的函数,将每个字符串变成 Array of Numbers
function spaceSeparatedStringToArray(str) {
     return str.split(' ');
}

...以及另一个将包含数字文本表示的 String 强制转换为 Number 的函数,例如...

function stringToNumber(str) {
    return Number(str);
}

...现在让我们输入 some Array.prototype.map() magic and some method chaining magic,创建一个函数,其中输入是 space 分隔的字符串,输出是 ArrayNumbers:

function stringToNumber(str) { return Number(str); }

function spaceSeparatedStringToArrayOfNumbers(str) {
    return str.split(' ').map(stringToNumber);
}

有时将传递给 map()(或 reduce()filter() 等)的辅助函数定义为 anonymous lambda-functions 会更好,例如:

function spaceSeparatedStringToArrayOfNumbers(str) {
    return str.split(' ').map(function(str){ return Number(str) });
}

如果需要,您可以使用 arrow function syntax

function spaceSeparatedStringToArrayOfNumbers(str) {
    return str.split(' ').map(str=>Number(str) );
}

并且由于我们使用的是Number as a function,我们可以直接将其用作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);

让我们改变它以添加更多的 lambda 和方法链以及箭头语法:

let vectors = geoshape.split(';').map((str)=>str.split(' ').map(Number));

此外,由于 GeoJSON 格式要求每个坐标作为 2 分量向量而不是 4 分量向量,让我们再次 map 每个向量以仅保留前两个分量。这使用 array destructuring:

let vectors = geoshape
               .split(';')
               .map((str)=>str.split(' ').map(Number))
               .map(([x,y,w,z])=>[x,y]);

看到了吗?我定义了一个接受单个参数的 lambda 函数,假设它是一个长度为 4 的(数字)数组,解构该数组,然后 returns 一个包含前两个元素的新数组。它也可以像这样工作:

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]);

我假设你在达累斯萨拉姆而不是巴达霍斯工作,所以让我们通过交换 xy([x,y])=>[y,x]:

let coords = geoshape
               .split(';')
               .filter(str=> str!=='')
               .map((str)=>str.split(' ').map(Number))
               .map(([x,y])=>[y,x]);

现在转到 geojson.org and read 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);

看到一个working example here。还有,,不要盲目复制粘贴代码。请务必阅读链接的文档和资源,并了解发生了什么。