4

Using the Infragistics xamGeographicMap control, trying to add shapes from SQL server geometry data.

  • The data is valid; a select in SSMS shows the shapes properly
  • Points show properly when querying SP_GEOMETRY (see sample) -- so GeographicSymbolSeries works, and the shape columns contain actual data
  • GeographicShapeSeries does not work
  • GeographicPolyLine does not work

So this works:

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

But these show nothing:

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

Do I need to add a converter? The samples, they tell nothing. What gives?

Stu
  • 15,675
  • 4
  • 43
  • 74

2 Answers2

1

Ok, fixed it. Here's a semi-generic converter:

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;
    }
}

Use it like this:

        var countySeries = new GeographicShapeSeries
                           {
                               ItemsSource = SqlGeometryToShapeConverter.Create(data.counties, x => x.SP_GEOMETRY, x => x.County_Name),     
                               ShapeMemberPath = "Points"
                           };
        GeoMap.Series.Add(countySeries);
Stu
  • 15,675
  • 4
  • 43
  • 74
0

This was a good start for me, but I was running into some issues. Here's my perhaps less elegant approach. Compared to the default Infragistics background, the map is slightly off. Something might be off with my loading of my data in 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;        
                }

            }
        }
    }

I called this from my view model (I'm using MVVM Light). This was the code in my view model.

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

    private SqlGeometryToShapeConverter mapData;

    public SqlGeometryToShapeConverter MapData
    {
        get
        {
            return mapData;

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

This is a snippet from my View

    <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>
Mike Sage
  • 251
  • 2
  • 8