如何使用来自 GeoJson 文件/字符串的 C#、Nest、NetTopologySuite 在 ElasticSearch 中将地理数据Geometry、GeometryCollection索引摄取为 GeoShape?

问题描述

总结

我想使用来自 GeoJson 文件或字符串的 C#、GeoShapeElasticSearchNest 中正确索引(摄取)地理数据(Geometry、GeometryCollection)为 NetTopologySuite (NTS)表示。

我正在使用以下堆栈: 弹性搜索 7.10.1 巢 7.10.1 网络拓扑套件 2.1.0 NetTopologySuite.IO.GeoJSON 2.0.4

my GitHub GIST 中,您可以找到两个示例文件(postal-area.geojson 和 geojson 文件作为场景 #7 的示例)以及下面提供的代码,以及我迄今为止尝试过的代码


我的尝试

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using Bogus.DataSets;
using Elasticsearch.Net;
using ElasticSearch;
using GeoAPI.Geometries;
using Microsoft.Extensions.Configuration;
using nest;
using nest.JsonNetSerializer;
using NetTopologySuite.Features;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NetTopologySuite.IO;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO.Converters;
using Newtonsoft.Json.Converters;
using Coordinate = NetTopologySuite.Geometries.Coordinate;
using GeometryCollection = NetTopologySuite.Geometries.GeometryCollection;

private static void Main()
{
    try {
        var defaultIndex = "my_shapes";

        string cloudId = "cloudId";
        string username = "username";
        string password = "password";
        var credentials = new BasicAuthenticationCredentials(username,password);
        //var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
        var pool = new CloudConnectionPool(cloudId,credentials);
        var settings = new ConnectionSettings(pool,(c,s) =>
            new JsonNetSerializer(c,s,contractJsonConverters: new JsonConverter[] 
            {
                    new AttributesTableConverter(),new CoordinateConverter(),new EnvelopeConverter(),new FeatureConverter(),new FeatureCollectionConverter(),new GeometryConverter(),new GeometryArrayConverter(),new StringEnumConverter()
            }))
            .DefaultIndex(defaultIndex)
            .disableDirectStreaming()
            .PrettyJson()
            .OnRequestCompleted(callDetails => {
                if (callDetails.RequestBodyInBytes != null) {
                    var json = JObject.Parse(Encoding.UTF8.GetString(callDetails.RequestBodyInBytes));

                    Console.WriteLine(
                        $"{callDetails.HttpMethod} {callDetails.Uri} \n" +
                    $"{json.ToString(Newtonsoft.Json.Formatting.Indented)}");
                }
                else {
                    Console.WriteLine($"{callDetails.HttpMethod} {callDetails.Uri}");
                }

                Console.WriteLine();

                if (callDetails.ResponseBodyInBytes != null) {
                    Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" +
                        $"{Encoding.UTF8.GetString(callDetails.ResponseBodyInBytes)}\n" +
                    $"{new string('-',30)}\n");
                }
                else {
                    Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" +
                        $"{new string('-',30)}\n");
                }
            });

        var client = new Elasticclient(settings);

        var createIndexResponse = client.Indices.Create(defaultIndex,c => c
            .Map<MyDocument>(m => m
                .Properties(p => p
                    .GeoShape(g => g
                        .Name(n => n.Geometry)
                    )
                )
            )
        );

        if (!createIndexResponse.IsValid) {
            throw new Exception($"Error creating index: {createIndexResponse.Debuginformation}");
        }

        IndexResponse indexResponse;
        MyDocument document;
        Geometry geometrypolygon;
        FeatureCollection featureCollection;

        //Working Scenario #1: Geometry from mock polygon -------------------works!!!!!!!!!!!
        var polygon = new polygon(new LinearRing(new [] {
            new Coordinate(0,0),new Coordinate(0,4),new Coordinate(4,0)
        }));
        document = new MyDocument(1,polygon);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #1 -------------------

        //Working Scenario #2:  Geometry from FeatureCollection from real GeoJson file ------------------- works
        var geojsonFileName = @"..\..\..\_GeoDataFiles\GeoJSONs\PostalArea.geojson";
        var jsonData = File.ReadAllText(geojsonFileName);
        featureCollection = new GeoJsonReader().Read<FeatureCollection>(jsonData);
        if (featureCollection == null) return;
        var geometry = featureCollection[0].Geometry;
        document = new MyDocument(1,geometry);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #2-------------------

        //NOT Working Scenario #3: Geometry deserialized (with GeoJsonSerializer) from mock GeoJson string -------------------
        //excluded coordinates arrays for clarity
        var geoJsonpolygonStr1 = "{\"type\":\"polygon\",\"coordinates\":[ ... ]}";
        var serializer = new NetTopologySuite.IO.GeoJsonSerializer();
        using(var stringReader = new StringReader(geoJsonpolygonStr1))
            using (var jsonReader = new JsonTextReader(stringReader))
            {
                /*Error:
                    {"Could not create an instance of type NetTopologySuite.Geometries.Geometry. 
                    Type is an interface or abstract class and cannot be instantiated. 
                    Path 'type',line 2,position 8."}*/
                geometrypolygon = serializer.Deserialize<Geometry>(jsonReader);
            }
        document = new MyDocument(1,geometrypolygon);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #3 -------------------

        //NOT Working Scenario #4: Geometry deserialized (with JsonConvert) from mock GeoJson string -------------------
        //excluded coordinates arrays for clarity
        var geoJsonpolygonStr2 = "{\"type\":\"polygon\",\"coordinates\":[ ... ]}";
        /*Error:
            {"Could not create an instance of type NetTopologySuite.Geometries.Geometry. 
            Type is an interface or abstract class and cannot be instantiated. 
            Path 'type',position 8."}*/
        geometrypolygon = JsonConvert.DeserializeObject<Geometry>(geoJsonpolygonStr2);
        document = new MyDocument(1,geometrypolygon);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #4 -------------------

        //NOT Working Scenario #5: GeometryCollection deserialized (with JsonConvert) from mock GeoJson string -------------------
        var geoCollectionMock =
                    @"{""type"": ""geometrycollection"",""geometries"": ["
                            + geoJsonpolygonStr1 +
                            ","
                            + geoJsonpolygonStr2 +
                        @"]
                    }";
        /*Error:
            {"Could not create an instance of type NetTopologySuite.Geometries.Geometry. 
            Type is an interface or abstract class and cannot be instantiated. 
            Path 'type',position 8."}*/
        geometrypolygon = JsonConvert.DeserializeObject<Geometry>(geoCollectionMock);
        document = new MyDocument(1,geometrypolygon);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #5 -------------------
        
        //Weired Scenario #6: GeometryCollection built from multiple Geometry objects from FeatureCollection from real GeoJson file -------------------
        //Data ingested into ElasticSearch Index,BUT,polygons from GeometryCollection can't be seen on Kibana Maps as other simple polygons can be seen 
        var geoCollectionObj = new NetTopologySuite.Geometries.GeometryCollection(new[]
                {
                    featureCollection[0].Geometry,featureCollection[1].Geometry,featureCollection[2].Geometry 
                });
        document = new MyDocument(1,geoCollectionObj);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #6 -------------------

        //Not working Scenario #7: Geometry from FeatureCollection from real GeoJson file - invalid Geometry -------------------
        var isValid = featureCollection[0].Geometry.IsValid;//= false
        /*Error:
            "type" : "mapper_parsing_exception","reason" : "Failed to parse field [geometry] of type [geo_shape]","caused_by" : {
                "type" : "invalid_shape_exception","reason" : "Self-intersection at or near point [-3.173,57.545]"
            }*/
        document = new MyDocument(99,featureCollection[99].Geometry);
        indexResponse = client.IndexDocument(document);
        //End of Scenario #7 -------------------


        if (!indexResponse.IsValid) {
            throw new Exception($"Error indexinf document: {indexResponse.Debuginformation}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"General error: {ex}");
    }
}

public class MyDocument {
    public MyDocument(int id,Geometry geometry) {
        Id = id;
        Geometry = geometry;
    }

    public int Id { get; set; }
    
    public Geometry Geometry { get; set; }
}

这是来自 my GitHub GIST 的 GeoJson 文件,用作场景 #7 的示例。貌似有效,其他平台显示(GitHub mapBox地图预览、QGIS、geojson.io)

Scenario #7 source file: postal-area-ab-aberdeen-valid-or-invalid?.geojson


问题

  1. 关于不工作的场景(#3、#4、#5)如何将 GeoJson 字符串反序列化为 Geometry 对象?
  2. 关于场景 #6,为什么 GeometryCollection 数据在 Kibana Map 上不像简单的几何(多边形)那样可见?
    • 2.1.不知道它是否相关,但是在 Kibana 地图上拖动和缩放时,我在浏览器 JS 控制台中收到此错误
      message: "Input data given to 'cfe5e9a5-de63-4beb-85b2-4b67ad455ae9' is not a valid GeoJSON object."_ proto _: Object
      overrideMethod @ react_devtools_backend.js:2430
      Ut.fire @ maps.chunk.1.js:31```
      
  3. 将 ElasticSearch geoshape 作为多个单独的 Geometry 对象(单个文档中的多边形)而不是一个 GeometryCollection(单个文档中的多边形)摄取有什么区别和优缺点?
    • 3.1.索引如何(执行时间和性能 - 即使使用批量索引)?
    • 3.2.查询搜索、过滤(可用性、性能等)如何?
  4. 关于场景 #7,为什么有些几何图形似乎无效?
    • 4.1. NTS 有 MakeValid 方法吗?或者我如何在 C# 中解决这个问题?

免责声明/其他说明

代码是从RussCamGIST复制和改编的。
可在此 StackOverflow Chat room 中找到有关此主题的初步讨论。
当前代码可能对正在尝试此操作并从 RussCam 的旧 examplearticle 开始的其他人有用。
代码是从我的源文件提取的,所以如果你发现一些错别字,请多多包涵。


解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...