Сопоставление функции GeoJson со свойством

Я пытаюсь сопоставить Multipolygon из GeoJSON FeatureCollection, который у меня есть, со свойством, которое является типом NetTopologySuite для Multipolygon.

Вот что я получил:

ГеоJSON:

{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"bbox":[13.55188823869398,44.440081209521175,16.6289966237125,46.16059787999315],"type":"MultiPolygon","coordinates":[[[[15.395583981629768,44.57162377024533],[15.387003350545871,44.481720735232564],[15.261789625068943,44.48813408824978],[15.270172550335417,44.578048939581144],[15.395583981629768,44.57162377024533]]],[[[15.896964543940912,44.544415530182626],[16.022240457648902,44.53723684127118],[16.14748751000305,44.52990760944508],[16.137724258293254,44.440081209521175],[16.012673860640266,44.44739698760816],[15.88759478603406,44.45456249478511],[15.76248761526664,44.46157767625901],[15.771660353342368,44.55144362101705],[15.896964543940912,44.544415530182626]]],[[[15.152955209351642,44.674242834298695],[15.027294842687972,44.6803775578638],[15.035349165740053,44.770301745657044],[15.161209210808025,44.764155656145604],[15.152955209351642,44.674242834298695]]],[[[15.395583981629768,44.57162377024533],[15.40420062719178,44.661519892738035],[15.52978449007481,44.65493177334668],[15.538636545464684,44.74480883793866],[15.664392732134285,44.73805721601009],[15.655341665136223,44.64819262251561],[15.646328471445097,44.55832105973176],[15.520969484947091,44.56504779340633],[15.395583981629768,44.57162377024533]]],[[[15.906373586831855,44.63426143624865],[15.780871560861883,44.641302492390295],[15.790121463337991,44.731154339362874],[15.915822144904903,44.7241002616354],[15.906373586831855,44.63426143624865]]],[[[14.909464652174844,44.77629632941221],[14.783556270565919,44.78213936065267],[14.791243042374749,44.8720788835874],[14.917352248635115,44.86622499380096],[14.909464652174844,44.77629632941221]]],[[[15.40420062719178,44.661519892738035],[15.278590669072724,44.667956929679484],[15.28704418791397,44.75785810878928],[15.295533315163146,44.84775252708752],[15.421542808951754,44.84129159985158],[15.412853498605203,44.751409152650304],[15.40420062719178,44.661519892738035]]],[[[15.295533315163146,44.84775252708752],[15.169497988826626,44.854061767894706],[15.17782174856998,44.943961219958226],[15.304058260811797,44.937640234683336],[15.295533315163146,44.84775252708752]]],[[[15.67348189503577,44.82791488944938],[15.547525868962715,44.83467903673759],[15.55645268017813,44.92454241922587],[15.682609378238029,44.91776569199236],[15.67348189503577,44.82791488944938]]],[[[15.574419653120414,45.10424893283504],[15.447831530393547,45.11089856282971],[15.456668762059818,45.20075425647369],[15.583460263041763,45.19409216255446],[15.574419653120414,45.10424893283504]]],[[[15.084394984513631,45.309708772973096],[15.076133963305915,45.21982378925574],[14.94923765800067,45.2258745480123],[14.941215654803312,45.13597182321009],[14.814498911953551,45.1418585743132],[14.822316698924032,45.23177234906947],[14.695371707066238,45.237517145249605],[14.703017502529459,45.32743542422996],[14.710695871844338,45.4173474925388],[14.8380521975077,45.41158102170872],[14.830167727940616,45.321679814539486],[14.957293763601468,45.315770914953696],[14.965384175175874,45.40566097472601],[15.092691175810298,45.39958739941671],[15.084394984513631,45.309708772973096]]],[16.19120538066795,46.06345399526351],[16.062454717796655,46.07086579545412]]]]},"properties":{"code":"1167","maptype":"Distribution","category":"Species","isocode":"HR","refgrid":"EEA 10x10km","sensitive":"non-sensitive"}}],"fileName":"Test"}

Моя модель:

public class Species
    {
        public int Id { get; set; }
        public string Name { get; set; }
        //property to which I want to map the MultiPolygon feature from GeoJSON. This is a NetToplogySuite type.
        public MultiPolygon Range { get; set; }
    }

Я попытался использовать библиотеки NetTopologySuite и GeoJSON.Net для преобразования и сопоставления GeoJSON, но далеко не продвинулся. Я получаю GeoJSON в виде строки в своем контроллере:

using NetTopologySuite.IO;
using NetTopologySuite.Geometries;
using GeoJSON.Net.Feature;
using GeoJSON.Net;

    public void GeoJson(string json)
            {

                Species newSpecies = new Species();

                var reader = new GeoJsonReader();
                var featureCollection = reader.Read<FeatureCollection>(json);

                if (featureCollection == null)
                {
                    return;
                }

                // loop through all the parsed features
                for (int featureIndex = 0;
                     featureIndex < featureCollection.Features.Count;
                     featureIndex++)
                {
                    // get json feature
                    var jsonFeature = featureCollection.Features[featureIndex];
                    Geometry geom = null;

                    // get geometry type to create appropriate geometry
                    switch (jsonFeature.Geometry.Type)
                    {
                        case GeoJSONObjectType.Point:
                            break;
                        case GeoJSONObjectType.MultiPoint:
                            break;
                        case GeoJSONObjectType.LineString:
                            break;
                        case GeoJSONObjectType.MultiLineString:
                            break;
                        case GeoJSONObjectType.Polygon:
                            break;
                        case GeoJSONObjectType.MultiPolygon:
                            {
                                //this is where I want to convert GeoJSON MultiPolygon to a NetToplogySuite MultiPolygon but I'm don't know how. Basically I want to map the newSpecies.Range to the MultiPolygon jsonFeature. 

                            }
                            break;
                        case GeoJSONObjectType.GeometryCollection:
                            break;
                        case GeoJSONObjectType.Feature:
                            break;
                        case GeoJSONObjectType.FeatureCollection:
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }

Любые идеи?

Благодарю вас!


person Frets    schedule 29.10.2019    source источник


Ответы (2)


Прочитав почти каждую страницу, существующую в Интернете, я отрефакторил ваш ответ после того, как (как я считаю) является ошибкой копирования и вставки. Я никогда не мог узнать, для чего вы использовали firstLevelCoordinateList, кроме добавления.

Итак, я провел рефакторинг вашего кода. Эта версия может содержать следы GeoJSON.Net, но с тех пор я пытался удалить его следы и полностью использовать NetTopologySuite. Однако позже появилось другое решение...

using GeoJSON.Net;
using GeoJSON.Net.Contrib.EntityFramework;
using GeoJSON.Net.Feature;
using NetTopologySuite.Geometries;
using NetTopologySuite.Geometries.Implementation;
using RekylService.ExternalSync.DataPortal.Imports.Interfaces;
using System.Collections.Generic;
using System.Linq;
using LineString = GeoJSON.Net.Geometry.LineString;
using MultiLineString = GeoJSON.Net.Geometry.MultiLineString;
using MultiPoint = GeoJSON.Net.Geometry.MultiPoint;
using MultiPolygon = GeoJSON.Net.Geometry.MultiPolygon;
using Point = GeoJSON.Net.Geometry.Point;
using NTS = NetTopologySuite;

namespace Parser
{
    public class GeoShapeJsonParser
    {
        public ICollection<Geometry> ParseGeoShapes(string data)
        {
            var reader = new NTS.IO.GeoJsonReader();
            var featureCollection = reader.Read<FeatureCollection>(data);

            if (featureCollection == null)
                return null;

            var geoFactory = NTS.NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);

            var shapes = new List<Geometry>();

            foreach (var shape in featureCollection.Features)
            {
                if (shape.Type == GeoJSONObjectType.MultiPolygon)
                {
                    var multipolygon = shape.Geometry as MultiPolygon;

                    var ringList = new List<NTS.Geometries.LinearRing>();
                    var polygonList = new List<NTS.Geometries.Polygon>();

                    foreach (var xpolygon in multipolygon.Coordinates) //I had a name conflict, hence xpolygon instead of polygon ;)
                    {
                        foreach (var lineString in xpolygon.Coordinates) //the first lineString is the shell, and the ones following are holes
                        {
                            var linearRingCoordinates = new List<NTS.Geometries.Coordinate>();

                            foreach (var coordinate in lineString.Coordinates)
                            {
                                var coordinates = new NTS.Geometries.Coordinate(coordinate.Longitude, coordinate.Latitude);

                                linearRingCoordinates.Add(coordinates);
                            }

                            var ring = geoFactory.CreateLinearRing(linearRingCoordinates.ToArray());

                            ringList.Add(ring);
                        }

                        var polygon = geoFactory.CreatePolygon(ringList.First(), ringList.Skip(1).ToArray()); //this takes the first ring as outer shell and the rest as holes

                        polygonList.Add(polygon);
                    }

                    shapes.Add(geoFactory.CreateMultiPolygon(polygonList.ToArray()));
                }
            }

            return shapes;

Это решение также учитывает дыры в полигонах. Однако вот в чем дело (теперь 100% NetTopologySuite и все формы):

    public class GeoShapeJsonParser
    {
        public ICollection<Geometry> ParseGeoShapes(string data)
        {
            var reader = new GeoJsonReader();
            var featureCollection = reader.Read<FeatureCollection>(data);

            var shapes = new List<Geometry>();

            foreach (var feature in featureCollection)
                shapes.AddRange(Extract(feature));
        }

        public IEnumerable<Geometry> Extract(IFeature feature)
        {
            var extract = new List<Geometry>();

            new GeometryExtracter<Point>(extract).Filter(feature.Geometry);
            new GeometryExtracter<MultiPoint>(extract).Filter(feature.Geometry);
            new GeometryExtracter<LineString>(extract).Filter(feature.Geometry);
            new GeometryExtracter<MultiLineString>(extract).Filter(feature.Geometry);
            new GeometryExtracter<LinearRing>(extract).Filter(feature.Geometry);
            new GeometryExtracter<Polygon>(extract).Filter(feature.Geometry);
            new GeometryExtracter<MultiPolygon>(extract).Filter(feature.Geometry);

            return extract;
        }

Это абсолютно лучший, самый короткий и быстрый способ разобрать все геофигуры. Я поражен отсутствием документации по этой теме.

Чтобы я мог сохранить это на SQL-сервере, я превращаю каждую форму в WKT, прежде чем отправлять их, используя "the extracted geometry object".ToText().

В репозитории я назначаю их полю как DbGeography.FromText("the extracted geometry object").

person Martin Björkvall    schedule 18.06.2020
comment
Спасибо, Мартин! Ваш способ кажется лучше, поэтому я приму его как решение этой проблемы. Да уж, отсутствие документации ошеломляет для такой общей задачи. - person Frets; 19.06.2020

Вот решение, которое мне удалось написать после нескольких дней проб и ошибок. По сути, geoJSON передается в виде строки, преобразуется в JSON с помощью метода Parse из Newtonsoft.Json, а затем в FetureCollection с использованием GeoJsonReader из GeoJSON.NET. Затем я перебираю все проанализированные объекты и создаю соответствующие геометрии. В данном случае меня интересовала только функция Multipolygon, которую я преобразовал в тип Multipolygon из NetTopologySuite.

using NetTopologySuite.IO;
using NetTopologySuite.Geometries;
using GeoJSON.Net.Feature;
using GeoJSON.Net;

[HttpPost]
public void GeoJson(string json) {
 var test = json;
 var item = JObject.Parse(json);

 Species newSpecies = new Species();

 var reader = new GeoJsonReader();
 var featureCollection = reader.Read < FeatureCollection > (json);

 if (featureCollection == null) {
  return;
 }

 // loop through all the parsed features
 for (int featureIndex = 0; featureIndex < featureCollection.Features.Count; featureIndex++) {
  // get json feature
  var jsonFeature = featureCollection.Features[featureIndex];

  // get geometry type to create appropriate geometry
  switch (jsonFeature.Geometry.Type) {
   case GeoJSONObjectType.Point:
    break;
   case GeoJSONObjectType.MultiPoint:
    break;
   case GeoJSONObjectType.LineString:
    break;
   case GeoJSONObjectType.MultiLineString:
    break;
   case GeoJSONObjectType.Polygon:
    break;
   case GeoJSONObjectType.MultiPolygon:
    {
     var multipoly = jsonFeature.Geometry as GeoJSON.Net.Geometry.MultiPolygon;
     var polygonList = new List < Polygon > ();
     foreach(var firstLevel in multipoly.Coordinates) {
      var firstLevelCoordinateList = new List < List < Coordinate >> ();

      foreach(var secondLevel in firstLevel.Coordinates) {

       var secondLevelCoordinateList = new List < Coordinate > ();
       foreach(var thirdLevel in secondLevel.Coordinates) {
        var coordinates = new Coordinate(thirdLevel.Longitude, thirdLevel.Latitude);
        secondLevelCoordinateList.Add(coordinates);
       }

       var coordArr = new Coordinate[secondLevelCoordinateList.Count];

       for (int i = 0; i < secondLevelCoordinateList.Count; i++) {
        coordArr[i] = secondLevelCoordinateList[i];
       }
       var poly = new GeometryFactory().CreatePolygon(coordArr);
       polygonList.Add(poly);
       firstLevelCoordinateList.Add(secondLevelCoordinateList);
      }

     }
     var polyArr = new Polygon[polygonList.Count];

     for (int i = 0; i < polygonList.Count; i++) {
      polyArr[i] = polygonList[i];
     }
     //NetTopologySuite Multipolygon property
     newSpecies.Range = new GeometryFactory().CreateMultiPolygon(polyArr);
    }
    break;
   case GeoJSONObjectType.GeometryCollection:
    break;
   case GeoJSONObjectType.Feature:
    break;
   case GeoJSONObjectType.FeatureCollection:
    break;
   default:
    throw new ArgumentOutOfRangeException();
  }
 }
}
person Frets    schedule 31.10.2019