Newbie: Problem with CoordinateTransformation

Topics: General Topics, SharpMap Project, WinForms Controls
Jun 26, 2009 at 12:05 AM
Edited Jun 26, 2009 at 1:55 PM

Hello,  I created a form with a UK map on it, and I managed to click on a county and get the information I want (county name) following the HowTo on this site (How do I return a set of feature attribute data from a click on a map?)


But since the shape of UK country looked stretched, I found how to do a transformation using a function that I found here on the forum (Function wgs84toGoogle() As ICoordinateTransformation)

so I did something like layUk.CoordinateTransformation = transformToGoogle  and I got a better looking map (LEFT: google transform, RIGHT: original map)


PROBLEM: after I transfom my layUk layer that onclick method doesn't work anymore. I get a nullreferenceexception and I can't seem to retrieve any information anymore 

Could you please explain to me how to solve this? or (even better) how to load a not strecthed map (like you see on the left) without having to use an "on the fly transformation" ?

what I would like is just to show a good UK map with county's info on click..


ps. (im using version 0,9 and I get my uk map from a .shp file)



EDIT: actually I found a way to add a .prj file using MapWindow program, so I just added a prj of mercator2_sp and the map now looks good without any transformation needed

therefore the next post is probably quite useless



thanks in advance



Jun 26, 2009 at 1:02 PM

I found a way to solve my problem so I'll post it here in case anyone is interested. My uk.shp file had no .PRJ file with it


this is the result that I wanted to get, color + information on mouseclick on the left image


Some Declarations


    Dim path As String = My.Application.Info.DirectoryPath & "\shp\uk\uk.shp"

    Dim mymap As SharpMap.Map
    Dim mymap1 As SharpMap.Map

    Dim layUk As New Layers.VectorLayer("Uk")
    Dim layUk1 As New Layers.VectorLayer("Uk1")

    Dim MapProvider As New SharpMap.Data.Providers.ShapeFile(path, True)
    'Dictionary to store countyname - geometry object
    Dim GeoList As New Dictionary(Of String, Geometries.Geometry)



On load I populate the dictionary with countyname/geometry, taken from my MapProvider object

    Sub LoadGeometry()
        Dim curr As String
            Dim ext = MapProvider.GetExtents
            Dim fCount = MapProvider.GetFeatureCount

            For i = 0 To fCount - 1
                Dim rowSelected As FeatureDataRow = MapProvider.GetFeature(i)
                curr = rowSelected.Item(0).ToString
                GeoList.Add(rowSelected.Item(0).ToString.ToUpper, rowSelected.Geometry)

        Catch ex As Exception
        End Try
    End Sub



I create my 2 maps, I will transform one layer and leave the other as it is

    Sub GenerateMap()
            ' Create 2 maps, I'll put them in 2 MapWindows Control 
            mymap = New Map(New Size(400, 800))
            mymap1 = New Map(New Size(400, 800))

            layUk = New Layers.VectorLayer("Uk")
            layUk.DataSource = MapProvider
            layUk.Style.Fill = Brushes.White
            layUk.Style.Outline = Pens.Black
            layUk.Style.EnableOutline = True

            layUk1 = New Layers.VectorLayer("Uk1")
            layUk1.DataSource = MapProvider
            layUk1.Style.Fill = Brushes.White
            layUk1.Style.Outline = Pens.Black
            layUk1.Style.EnableOutline = True

            Dim transformToGoogle As ICoordinateTransformation = wgs84toGoogle()
            layUk.CoordinateTransformation = transformToGoogle



            mi.Map = mymap
            mi1.Map = mymap1

        Catch ex As Exception
        End Try
    End Sub



this is the function that I found here on the forum to transform to GoogleCS, I created another one named GoogleToWgs84 that does the opposite transformation


    Public Shared Function wgs84toGoogle() As ICoordinateTransformation
        Dim csFac As CoordinateSystemFactory = New SharpMap.CoordinateSystems.CoordinateSystemFactory()
        Dim ctFac As New CoordinateTransformationFactory()

        Dim wgs84 As IGeographicCoordinateSystem = csFac.CreateGeographicCoordinateSystem("WGS 84", AngularUnit.Degrees, HorizontalDatum.WGS84, PrimeMeridian.Greenwich, New AxisInfo("north", AxisOrientationEnum.North), New AxisInfo("east", AxisOrientationEnum.East))

        Dim parameters As New List(Of ProjectionParameter)()
        parameters.Add(New ProjectionParameter("semi_major", 6378137.0R))
        parameters.Add(New ProjectionParameter("semi_minor", 6378137.0R))
        parameters.Add(New ProjectionParameter("latitude_of_origin", 0.0R))
        parameters.Add(New ProjectionParameter("central_meridian", 0.0R))
        parameters.Add(New ProjectionParameter("scale_factor", 1.0R))
        parameters.Add(New ProjectionParameter("false_easting", 0.0R))
        parameters.Add(New ProjectionParameter("false_northing", 0.0R))
        Dim projection As IProjection = csFac.CreateProjection("Google Mercator", "mercator_1sp", parameters)

        Dim epsg900913 As IProjectedCoordinateSystem = csFac.CreateProjectedCoordinateSystem("Google Mercator", wgs84, projection, LinearUnit.Metre, New AxisInfo("East", AxisOrientationEnum.East), New AxisInfo("North", AxisOrientationEnum.North))

        Return ctFac.CreateFromCoordinateSystems(wgs84, epsg900913)
    End Function



this is the MouseUp handler on the MapImage control


   Private Sub mi_MouseUp(ByVal WorldPos As SharpMap.Geometries.Point, ByVal ImagePos As System.Windows.Forms.MouseEventArgs) Handles mi.MouseUp
        If ImagePos.Button = Windows.Forms.MouseButtons.Right Then

                  'the function is similar to the wgs84ToGoogle one, I just invert the 2 CS in the ctFac.CreateFromCoordinateSystems method
                Dim trans As ICoordinateTransformation = GoogleToWgs84()

                'take mouse click location from MapImage 
                Dim p = mi.Map.ImageToWorld(ImagePos.Location)

                'transform point from google mercator CS to original WGS84 CS
                Dim toPoint = trans.MathTransform.Transform(p)

                Dim ds As New FeatureDataSet
                MapProvider.ExecuteIntersectionQuery(toPoint.GetBoundingBox, ds)
                DataGridView1.DataSource = ds.Tables(0)

                Dim nRow As Integer = 0
                If ds.Tables(0).Count > 1 Then
                    nRow = CInt(InputBox("Choose County from 1 to " & ds.Tables(0).Count, "Color County")) - 1
                End If
                Dim CurrentCountyName As String = ds.Tables(0).Item(nRow).Item(0).ToString.ToUpper

                'Dictionary of CountyName, Geometry that I created previously
                Dim QueryGeoList = From g In GeoList _
                                   Where g.Key.ToUpper = CurrentCountyName _
                                   Select g.Value

                'Create layer with random color
                Dim r As New Random(CInt(Now.Millisecond))
                Dim laySelected = New SharpMap.Layers.VectorLayer("Selection")
                laySelected.DataSource = New SharpMap.Data.Providers.GeometryProvider(QueryGeoList.Single)
                laySelected.Style.Fill = New System.Drawing.SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))

                'Add layer to map
                'transform layer
                laySelected.CoordinateTransformation = trans

                mi.Map = mymap
Catch ex As Exception MsgBox(ex.Message) Finally MapProvider.Close() End Try End If End Sub


the code is not very polished but I hope it's understandable, if anyone has suggestions, feel free to post them..