Формы xamGeographicMap из данных SQL server

#c# #sql-server #infragistics #spatial-query

#c# #sql-сервер #инфраструктура #пространственный запрос

Вопрос:

Используя элемент управления Infragistics xamGeographicMap, пытаюсь добавить фигуры из данных геометрии SQL server.

  • Данные верны; выбор в SSMS показывает фигуры правильно
  • Точки отображаются правильно при запросе SP_GEOMETRY (см. Пример) — так что GeographicSymbolSeries работает, а столбцы формы содержат фактические данные
  • GeographicShapeSeries не работает
  • GeographicPolyLine не работает

Итак, это работает:

         var majorCitySeries = new GeographicSymbolSeries
                              {
                                  ItemsSource = data.cities,
                                  LatitudeMemberPath = "SP_GEOMETRY.YCoordinate",
                                  LongitudeMemberPath = "SP_GEOMETRY.XCoordinate"
                              };
        GeoMap.Series.Add(majorCitySeries);
 

Но они ничего не показывают:

        var countySeries = new GeographicShapeSeries
                           {
                               ItemsSource = data.counties,     
                               ShapeMemberPath = "SP_GEOMETRY"
                           };
        GeoMap.Series.Add(countySeries);

        var br = new GeographicPolylineSeries
                 {
                     ItemsSource = data.rivers,
                     ShapeMemberPath = "SP_GEOMETRY"
                 };
        GeoMap.Series.Add(br);
 

Нужно ли мне добавлять конвертер? Образцы, они ничего не говорят. Что дает?

Ответ №1:

Хорошо, исправлено. Вот полу-универсальный конвертер:

 public static class SqlGeometryToShapeConverter
{

    public static ShapefileConverter Create<T>(IEnumerable<T> items, 
                                               Func<T, DbGeometry> geoFunc, 
                                               Func<T, string> nameFunc) 
        where T : class
    {
        var converter = new ShapefileConverter();
        foreach (var item in items)
        {
            var rec = new ShapefileRecord();
            var points = new List<Point>();
            var geometry = geoFunc(item);
            Debug.Assert(geometry.PointCount != null, "geometry.PointCount != null");
            // Points are 1 based in DbGeometry
            var pointCount = geometry.PointCount;
            for (var pointIndex = 1; pointIndex <= pointCount; pointIndex  )
            {
                var point = geometry.PointAt(pointIndex);
                Debug.Assert(point.XCoordinate != null, "point.XCoordinate != null");
                Debug.Assert(point.YCoordinate != null, "point.YCoordinate != null");
                points.Add(new Point(point.XCoordinate.Value, point.YCoordinate.Value));
            }
            rec.Fields = new ShapefileRecordFields { { "Name", nameFunc(item) } };
            rec.Points = new List<List<Point>> { points };
            converter.Add(rec);
        }
        return converter;
    }
}
 

Используйте это так:

         var countySeries = new GeographicShapeSeries
                           {
                               ItemsSource = SqlGeometryToShapeConverter.Create(data.counties, x => x.SP_GEOMETRY, x => x.County_Name),     
                               ShapeMemberPath = "Points"
                           };
        GeoMap.Series.Add(countySeries);
 

Ответ №2:

Это было хорошее начало для меня, но я столкнулся с некоторыми проблемами. Вот мой, возможно, менее элегантный подход. По сравнению с фоном инфраструктуры по умолчанию, карта немного смещена. Возможно, что-то не так с моей загрузкой данных в SQL server.

     using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Data.SqlClient;
    using System.Windows;
    using Infragistics.Controls.Maps;
    using Microsoft.SqlServer.Types;

    namespace TestMVVMLightProject.Model
    {
        public class SqlGeometryToShapeConverter : ObservableCollection<ShapefileRecord>
        {
            public SqlGeometryToShapeConverter()
            {
                //connect
                //load sql
                //go thorugh points
                SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
                builder.DataSource = "localhost";
                builder.InitialCatalog = "RefDB_91_DistToCoast";
                builder.IntegratedSecurity = true;
                SqlConnection conn = new SqlConnection(builder.ConnectionString);
                conn.Open();
                string sql = "SELECT huc_2, geom FROM Polygon2";
                using (SqlCommand oCmd = new SqlCommand(sql, conn))
                {
                    oCmd.CommandTimeout = 3000;
                    using (SqlDataReader oDr = oCmd.ExecuteReader())
                    {
                        int ordGeom = oDr.GetOrdinal("geom");
                        int ordHucZone = oDr.GetOrdinal("huc_2");
                        double minX = double.MaxValue;
                        double minY = double.MaxValue;
                        double maxX = double.MinValue;
                        double maxY = double.MinValue;
                        while (oDr.Read())
                        {
                            var rec = new ShapefileRecord();
                            rec.Points = new List<List<Point>>();

                            SqlGeography coast = (SqlGeography)oDr.GetValue(ordGeom);

                            int numPolygons = (int)coast.STNumGeometries();
                            string hucZone = oDr.GetString(ordHucZone);
                            int hucInt = int.Parse(hucZone);
                            for (int geomIndex = 1; geomIndex <= coast.STNumGeometries(); geomIndex  )
                            {
                                SqlGeography polygon = coast.STGeometryN(geomIndex);
                                var points = new List<Point>();

                                for (int verticeIndex = 1; verticeIndex <= polygon.STNumPoints(); verticeIndex  )
                                {
                                    points.Add(new Point(polygon.STPointN(verticeIndex).Long.Value, polygon.STPointN(verticeIndex).Lat.Value));
                                    if (hucInt < 19)
                                    {
                                        minX = minX < polygon.STPointN(verticeIndex).Long.Value ? minX : polygon.STPointN(verticeIndex).Long.Value;
                                        minY = minY < polygon.STPointN(verticeIndex).Lat.Value ? minY : polygon.STPointN(verticeIndex).Lat.Value;
                                        maxX = maxX > polygon.STPointN(verticeIndex).Long.Value ? maxX : polygon.STPointN(verticeIndex).Long.Value;
                                        maxY = maxY > polygon.STPointN(verticeIndex).Lat.Value ? maxY : polygon.STPointN(verticeIndex).Lat.Value;
                                    }
                                }

                                rec.Points.Add(points);
                            }

                            rec.Fields = new ShapefileRecordFields { { "HUC_2", hucZone.ToString() } };

                            this.Add(rec);
                        }
                        worldRect = new Rect(new Point(minX, minY), new Point(maxX, maxY));
                    }
                }
                conn.Close();
            }

            private Rect worldRect; 

            public Rect WorldRect
            {
                get
                {
                    return worldRect;        
                }

            }
        }
    }
 

Я вызвал это из своей модели представления (я использую MVVM Light). Это был код в моей модели представления.

     public MainViewModel()
    {
        mapData = new SqlGeometryToShapeConverter();
    }

    private SqlGeometryToShapeConverter mapData;

    public SqlGeometryToShapeConverter MapData
    {
        get
        {
            return mapData;

        }
        set
        {
            Set(() => MapData, ref mapData, value);
        }
    }
 

Это фрагмент с моей точки зрения

     <ig:XamGeographicMap Zoomable="True"
                             Height="400"
                             WorldRect="{Binding MapData.WorldRect}">
            <ig:XamGeographicMap.Series>
                <ig:GeographicShapeSeries ItemsSource="{Binding MapData}"
                                          ShapeMemberPath="Points"
                                          ShapeStyleSelector="{StaticResource shapeStyleSelector}"
                                          MarkerCollisionAvoidance="Fade">
                    <!-- custom marker with bindings to data loaded from database file (DBF) -->
                    <ig:GeographicShapeSeries.MarkerTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Item.Fields[HUC_2]}"
                                           Foreground="#FF333333"
                                           FontWeight="Bold"
                                           Margin="1 1 0 0" />
                            </Grid>
                        </DataTemplate>
                    </ig:GeographicShapeSeries.MarkerTemplate>
                </ig:GeographicShapeSeries>
            </ig:XamGeographicMap.Series>
        </ig:XamGeographicMap>