ThinkGeo.com    |     Documentation    |     Premium Support

MsSql2008 layer and Google Map Srid

Good morning,


I'm doing a bit of an experiment trying to get data stored in SQL Server to line up with the data displayed in an OpenStreetMap layer. 


I have learned the hard way many months ago (and just duplicated it a few minutes ago) that if I don't specify the SRID on the MsSql2008 layer, the map doesn't show.  If I do this:


MsSql2008FeatureLayer layer = new MsSql2008FeatureLayer(connString, "vwRoads_Freeway", "gid");


I get an empty map.  (I could also believe the features are drawn, but not where I am looking based on the extent I'm setting for the map.)  Currently, we have our SQL Server data in SRID 3395, and I must do this:


MsSql2008FeatureLayer layer = new MsSql2008FeatureLayer(connString, "vwRoads_Freeway", "gid", 3395);


...which works just fine.  I've been told the SRID used by OpenStreetMap, GoogleMaps, etc. is 3785.  HOWEVER, ManagedProj4 does not seem to know of this number; it returns an empty string, and if I use the value as th SRID in the MsSql2008FeatureLayer the data does not show.  I presume that's using Proj4 as well and doesn't know the code 3785.  I realize that through ManagedProj4 I can use GetGoogeMapParameterString, but that doesn't help since there is no MsSql2008FeatureLayer constructor that takes the parameter string. 


So my question is this: if I create an MsSql2008FeatureLayer WITHOUT A PROJECTION IDENTIFIER  (like the first example above) can I somewhere else (perhaps in the Projection property of the FeatureSource using a ManagedProj4?) create a projection using the parameter string for Google Maps I CAN get from ManagedProj4?  Would I be creating a ManagedProj4 with the same "from" and "to" parameter strings?  Or is there a better way to get the projection parameters into the layer?  I already have an entire database of data in the 3785 projection, so I just need to know how to tell MsSql2008FeatureLayer what projection the data is in.  Or so I presume.


Thanks,


Allen



Allen, 
  
 Thanks for your post and question. 
  
 If I do not understand wrong, we can try to set the Projection property via its FeatureSource property  
 msSqlFeatureLayer.FeatureSource.Projection.If I misunderstood anything, please feel free to let me know. 
  
 Any more questions please feel free to let me know. 
  
 Thanks. 
  
 Yale 


Hi Yale, 
  
 Yes, I think you have the idea.  Just after I posted the initial message, I went back and tried this: 
  
 //Code before this loads an OpenStreetMapLayer for reference as to where the map really is 
  
             LayerOverlay staticOverlay = new LayerOverlay(); 
  
             string connString = … 
  
             MsSql2008FeatureLayer layer = new MsSql2008FeatureLayer(connString, "Zones", "gid"); 
  
             ManagedProj4Projection proj4 = new ManagedProj4Projection(); 
             proj4.InternalProjectionParameters = ManagedProj4Projection.GetGoogleMapParametersString(); 
             proj4.ExternalProjectionParameters = ManagedProj4Projection.GetGoogleMapParametersString(); 
  
             layer.FeatureSource.Projection = proj4; 
             layer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.State1; 
             layer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; 
             staticOverlay.Layers.Add(layer); 
             map1.Overlays.Add(staticOverlay); 
  
             layer.Open(); 
             RectangleShape extent = layer.GetBoundingBox(); 
             layer.Close(); 
  
             map1.CurrentExtent = extent; 
             map1.Refresh(); 
  
 The OpenStretMapLayer is repositioned to the extent I would expect of the Sql Server layer but the Sql Server data does not display on the map.  The map zooms to where the data should be, so it seems like the data is OK. 
  
 If I change my connection string to a different database (with its data in SRID 3395) and build the SqlServer layer this way 
  
             MsSql2008FeatureLayer layer = new MsSql2008FeatureLayer(connString, "Zones", "gid", 3395); 
  
 it works, although the data does line up perfectly because the two Mercator projections have slightly different definitions. 
  
 Any thoughts? 
  
 Thanks, 
 Allen

After reading my post, I guess the questions I have are as follows: 
  
 1.  Based on the documentation, about all I can assign to the Projection property is either a Proj4, ManagedProj4, or RotationProjection. Correct? 
 2. If I am supposed to use a ManagedProj4, do I have to assign BOTH the InternalProjectionParameters and ExternalProjectionParameters? 
 3. The documentation for ManagedProj4 also shows ExternalProjectionParametersString and InternalProjectionParametersString properties, which I presume differ from ExternalProjectionParameters and InternalProjectionParameters but there is nothing there to indicate exactly what the purpose of ExternalProjectionParametersString and InternalProjectionParametersString do. Am I assigning the Google Maps parameters string to the wrong Proj4 property? 
  
 These were the things I wasn’t real sure about when I coded the above test example. 


Allen,


Thanks for your post and question.
 
I am not a GIS specialist, I would be very glad if my following answers can give you any help.
1) Ideally, We can assign any kinds of projection to the Projection property, while for now; we only have made three concreted projection systems integrated in our products, which is the Proj4, ManageProj4, RotationProjection. Meanwhile, we can get any customized projection assigned if we want.
2) Yes, as you thought, for any Proj4 or ManagedProj4 projection, we have to assign a InternalProjectionString(or Srid) and a ExternalProjection String(or Srid) .
3) The InternalProjectonstring specify the projection system which the current data stores, while the ExternalProjectionString specify the target projection system.
 
Any more questions please feel free let me know.
 
Thanks
 
Yale

Hi Yale… 
  
 Here’s a BIG problem that just came to me as we discussed this here in the office.  We already are assigning a RotationProjection object to each layer’s Projection property to implement map rotation, and the RotationProjection class doesn’t (as far as I can tell) have an SRID identifier setting, so this idea doesn’t appear to be workable for us.  It’s still an issue I need to explore, but I have some other priorities for a day or two, but will have to get back to this, maybe first thing next week. 
  
 But to go back to #2…if I do this (assign a string to both, as I showed in my example) and then plug that ManagedProj4 into the SQL layer, the data doesn’t display on the map. 
  
 Allen

Allen, 
  
 Thanks for your post and feedbacks. 
  
 Please try the following way to see if we can dig out the problem: 
 1) Get the features from the sql server directly without setting projection, which should be the source projection coordinate system. 
 2) Project the features into the target projection features by calling the ConvertToExternal API. 
 3) Investigate whether the projected features are correct like putting them into the target map control without setting any projection any more. 
  
 Any more questions please feel free to let me know. 
  
 Thanks. 
  
 Yale 


Hi Yale, 
  
 Sorry for the delay…I’ve been busy with a few other higher priorities that are taking longer than expected.  I think I can do what you ask.  It sounds like this is what you want me to try: 
  
 1: Load the data using the MsSql2008FeatureLayer constructor that doesn’t take an SRID. 
 2: Iterate through all of the features and call ConvertToExternalProjection (I presume I have to specify the desired projection somewhere here) 
 3: Load the projected features into an InMemory layer so I can see if they appear 
  
 Is that close? 
  
 Thanks, 
 Allen

Allen, 
  
 Thanks for your post and questions. 
  
 That is exactly what I meant. Besides, if it still does not appear on the map control, please take an initial look at the lat/long of the features if they are within the current extent scope. 
  
 Any more questions please feel free to let me know. 
  
 Thanks. 
  
 Yale 


OK, Yale…I’m close to winding down on these other priorities, so I should be able to give this a try in a day or two. 
  
 By the way, another thing I noticed with this: if I don’t specify the Srid in the Sql2008 constructor (as I said before, the features don’t display) then I calculate an extent based on those features (Layer.GetBoundingBox) and set the map to that extent, it seems to go to the expected extent, so it seems the data is in the layer. (I display the mouse world coordinates upon mouse move, so I can see where in the map I am even though nothing is displayed.)   So when this happens (1) the map DOES appear to move to the location of the data’s extent which seems to indicate it’s aware of the data in the layer but (2) the data itself doesn’t display. 
  
 Thanks for your assistance… 
  
 Allen

Yale,


I've come to realize the topic of this thread has changed as the discussion evolved.  Originally my issue was with getting a layer loaded in a projection where there is no SRID code that Proj4 understands, and eventually we got to the issue that when loading SqlServer layers without an SRID that the features don't display but when an SRID is used they are displayed.  Since we're at that point, I tested that scenario.  We are looking at options to the whole SRID issue internally and I will return to that some time in the future. 


I built a test program with four scenarios and attached the results in a PDF document; here is a summary.


Note: the test program shows both map scale and extent center point based on GetBoundingBox of the Sql layer.


(1) Loaded a Sql Server layer of municipal boundaries and defined the SRID on the constructor; the features are displayed on the map in a black pen of width 5.


(2) Loaded a Sql Server layer of municipal boundaries using the constructor that DOESN'T take an SRID; the features are not displayed. However, the scale and center point remain correct as in step #1.


(3) Keeping things the same as in step (2), I added an InMemoryFeatureLayer, with a yellow pen of width 3.  The features from the Sql layer are simply copied into the InMemory layer like this...



 



Collection<Feature> features = layer.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns);List<FeatureSourceColumn> columns = new List<FeatureSourceColumn>();InMemoryFeatureLayer inMemory = new InMemoryFeatureLayer(columns, features);

..and the InMemory features (in yellow) display correctly; I think this proves that the data in the Sql layer is correct, but just not being displayed.  The scale and map center are identical to steps 1 and 2.


(4) I keep the same scenario as #3 but add the SRID back to the Sql Server layer constructor.  Now BOTH layers are shown: the black (wider) SQL layer features peak out from underneath the yellow (InMemory) features.  The scale and map center have not changed.


So the basic issue here is this: I load a Sql Server layer without an SRID and the features are not displayed.  If I then simply copy those features to an InMemoryLayer without any processing, they ARE displayed. 


I hope that provides some insight into the issue!


Allen


 



 


 



Sql_Layer_SRID_test.pdf (154 KB)

Allen, 
  
 Thanks for your tests and detailed explanations. 
  
 I verified the source code for MsSqlFeatureSource and found the default srid number is 4326, which can explain the #2 item you tested.  That means if you do not specify a srid explicitly, it will set the default 4326 number for the srid. 
  
 Please take a try to set the srid number to 0 in #2 item case to see if it can solve the issue. 
  
 I will report this to our development team to see if they have any thoughts about it. 
  
 Any more questions please feel free to let me know. 
  
 Thanks. 
  
 Yale 


Allen, 
  
 One more thing to make sure, how you imported shape file data into sql server? When you import it, did you specify the correct SRID for it? 
  
 Thanks. 
  
 Yale 


Hi Yale, 
  
 Sorry for the delay…there’s been some higher priority stuff going on again! 
  
 When I get a minute I will try with the SRID set to 0 and get back to you. 
  
 As far as I have been able to tell, MapSuite (unlike the map component we moved from) doesn’t have built-in functionality to do this conversion, so we wrote our own little application to read the shapefile and build the SQL insert statements.  Yes, this utility automatically inserts our current SRID (3395) for us.  If you’re interested, I could post one of the insert statements it builds. 
  
 Allen

Allen, 
  
 Thanks for your post and questions. 
  
 If possible, please show us the insert statements to insert data into sql server. 
  
 Also, if possible, could you take a quick try to import data into SQL server using the following tool? When imported, please set the correct SRID number to it. Or if you want, you can send us the data, we can try against on our side. 
 sharpgis.net/page/SQL-Server-2008-Spatial-Tools.aspx 
  
 Any more questions please feel free to let me know. 
  
 Thanks. 
  
 Yale 


I got away from this for a while and the boss has been asking questions agian… 
  
 Here’s the issue in a nutshell: 
  
 I was trying to load data in SRID 3785 (OpenStreetMap, Google, etc.) into a SQL Server layer, but the Proj4 at version 4.5 did not recognize this number.  There is no version of the constructor that accepts a projection string.  Also, since we are rotating the map, the layer’s Projection slot is already occupied by a RotationProjection object.  So essentially there was no way to tell MapSuite about the SRID.  We had been considering changing our data to 3785 so that it automatically would line up with OSM data, but since we couldn’t get the SRID into the SQL layer we gave up on the whole idea. 
  
 I see in the changelog for version 5 that Proj4 has been enhanced.  Can anyone tell me if 3785 is now supported?  We are very close to release and I don’t want to mess things up by installing version 5 on my computer at this time. 
  
 Thanks, 
 Allen

 Allen,



 


I am not sure the number 3785 is working with our MapSuite, but I know we use 900913 as EPSG number for Google map, you can try following code:


            winformsMap1.MapUnit = GeographyUnit.Meter;
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean);

            GoogleMapsOverlay googleOverlay = new GoogleMapsOverlay();
            winformsMap1.Overlays.Add(googleOverlay);

            MsSql2008FeatureLayer worldLayer = new MsSql2008FeatureLayer(connString, "Zones", "gid"); 
            worldLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            worldLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.CreateSimpleAreaStyle(GeoColor.SimpleColors.Transparent, GeoColor.FromArgb(100, GeoColor.SimpleColors.Green));

            Proj4Projection proj4 = new Proj4Projection();
            proj4.InternalProjectionParametersString = Proj4Projection.GetDecimalDegreesParametersString();
            proj4.ExternalProjectionParametersString = Proj4Projection.GetGoogleMapParametersString();
            worldLayer.FeatureSource.Projection = proj4;

            LayerOverlay staticOverlay = new LayerOverlay();
            staticOverlay.Layers.Add("WorldLayer", worldLayer); 
            winformsMap1.Overlays.Add(staticOverlay);

            winformsMap1.CurrentExtent = new RectangleShape(-10000000, 10000000, 10000000, -10000000);
            winformsMap1.Refresh();

Thanks,


James