ThinkGeo.com    |     Documentation    |     Premium Support

Highlighting Features from WFSFeatureLayer

Has anyone tried to highlight a feature from a WFSFeatureLayer? I receive an IndexOutOfBounds exception on map.refresh when trying to do so in a very straight-forward manner by using GetFeaturesByColumnValue.


 


The stack trace is as follows:


"   at ThinkGeo.MapSuite.Core.x01af2673a54dd1c0.GetBoundingBoxFromWkb(Byte[] wkb)    at ThinkGeo.MapSuite.Core.Feature.GetBoundingBox()    at ThinkGeo.MapSuite.Core.InMemoryFeatureSource.GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable`1 returningColumnNames)    at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesForDrawingCore(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)    at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesForDrawing(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)    at ThinkGeo.MapSuite.Core.FeatureLayer.DrawCore(GeoCanvas canvas, Collection`1 labelsInAllLayers)    at ThinkGeo.MapSuite.Core.Layer.Draw(GeoCanvas canvas, Collection`1 labelsInAllLayers)    at ThinkGeo.MapSuite.DesktopEdition.LayerOverlay.DrawCore(GeoCanvas canvas)    at ThinkGeo.MapSuite.DesktopEdition.Overlay.Draw(GeoCanvas canvas)    at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x27d0591c2adda8d9(RectangleShape xb35a33b423b17f65, Overlay x99251f66cdabc2ad, Int32 xa209325f5c895f7e, Int32 x7454a0d1965919b1, GeographyUnit xbb704b4400ce6f76)    at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x568a187128faa92b(IEnumerable`1 xa6f0db4f183189f1)    at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x9ac8c50f434f4b39(Int32 xb565f4681f05557a)    at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.Refresh()    at WFS_Test.fmMain.btnSearch_Click(Object sender, EventArgs e) in C:\Documents and Settings\nsoto.DESLAURIERS\My Documents\Visual Studio 2008\Projects\WFS_Test\fmMain.vb:line 191    at System.Windows.Forms.Control.OnClick(EventArgs e)    at System.Windows.Forms.Button.OnClick(EventArgs e)    at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)    at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)    at System.Windows.Forms.Control.WndProc(Message& m)    at System.Windows.Forms.ButtonBase.WndProc(Message& m)    at System.Windows.Forms.Button.WndProc(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)    at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)    at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)    at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.Run(ApplicationContext context)    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)    at WFS_Test.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81    at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()    at System.Threading.ThreadHelper.ThreadStart_Context(Object state)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.ThreadHelper.ThreadStart()"



Nelson,


Following codes shows how to highlight a feature by using the API GetFeaturesByColumnValue.
 
In the form_load event, we load the WfsFeatureLayer as required:

private void LoadWfsFeatureLayer_Load(object sender, EventArgs e)
        {
            winformsMap1.MapUnit = GeographyUnit.Meter;
            winformsMap1.CurrentExtent = new RectangleShape(455417.2, 4992437.4, 485795.9, 4960950.6);
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean);

            //WfsFeatureLayer wfsFeatureLayer = new WfsFeatureLayer(@"datafinder.org/wfsconnector/com.esri.wfs.Esrimap/MN_MetroGIS_DataFinder_WFS_Water_Resources", "Watersheds-watersheds_2_a");
            WfsFeatureLayer wfsFeatureLayer = new WfsFeatureLayer(@"giswebservices.massgis.state.ma.us/geoserver/wfs", "GISDATA.COUNTIES_POLYM");
            wfsFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Country1;
            wfsFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            LayerOverlay staticOverlay = new LayerOverlay();
            staticOverlay.Layers.Add("WfsFeatureLayer", wfsFeatureLayer);
            winformsMap1.Overlays.Add(staticOverlay);

            InMemoryFeatureLayer highlightLayer = new InMemoryFeatureLayer();
            highlightLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            highlightLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Forest1;

            LayerOverlay highlightOverlay = new LayerOverlay();
            highlightOverlay.Layers.Add("HighlightLayer", highlightLayer);
            winformsMap1.Overlays.Add("HighlightOverlay", highlightOverlay);

            wfsFeatureLayer.Open();
            winformsMap1.CurrentExtent = wfsFeatureLayer.GetBoundingBox();
            wfsFeatureLayer.Close();

            winformsMap1.Refresh();
        }

 

In the button click event, using the following codes to highlight features:

private void btnHighlightAFeature_Click(object sender, EventArgs e)
        {
            winformsMap1.Overlays["HighlightOverlay"].Lock.EnterWriteLock();
            try
            {
                FeatureLayer worldLayer = winformsMap1.FindFeatureLayer("WfsFeatureLayer");
                InMemoryFeatureLayer highlightLayer = (InMemoryFeatureLayer)winformsMap1.FindFeatureLayer("HighlightLayer");

                worldLayer.Open();
                //Collection<Feature> feauters = worldLayer.FeatureSource.GetAllFeatures( ReturningColumnsType.AllColumns);
                Collection<Feature> queryFeauters = worldLayer.FeatureSource.GetFeaturesByColumnValue("FIPS_ID","25003");
                worldLayer.Close();

                highlightLayer.InternalFeatures.Clear();
                foreach (Feature feature in queryFeauters)
                {
                    highlightLayer.InternalFeatures.Add(feature.Id, feature);
                }
            }
            finally
            {
                winformsMap1.Overlays["HighlightOverlay"].Lock.ExitWriteLock();
            }
            winformsMap1.Refresh();
        }

 

Any more questions just let me know.
 
Thanks.
 
Yale


 
 

Yale, I will send you a project today that is relatively stripped down and still shows this error. on map.Refresh using your above code which is the normal way to highlight features, and Index was outside the bounds of the array error ioccurs with the stack trace above as mentioned: 
  


Hi Nelson,


We made some change to your sample fixed the display layer to “GISDATA.COUNTIES_POLYM” and the column to “FIPS_ID”.  You can take a look at the result by clicking “MassGIS” button, “Connect” button and “Highlight by Column Value” button by sequence. And also we create another sample to show you how to highlight a feature by clicking the map when using the WfsFeatureLayer.
 
We will send you the sample project soon and any questions please let me know.
 
Thanks,
 
sun

I made a Public Shared WithEvents layerWFS_FeatureSource as WfsFeatureSource 



Then, I am hooking into the RequestedData event and using Debug.Print to export the data. 



When I add the layer to the map, I am setting WfsFeatureSource = layerWFS.FeatureSource 



I tried to paste the outputs in here but it seems they are too large for the thread. I will email to Support.


I am only being returned 6 our of 36 or 46 columns when doing the below:


dim layerColumns As Collection(Of FeatureSourceColumn) = layerWFS.QueryTools.GetColumns


I only get the following Columns, even though as you can see in the XML there are many more that are missing:


OBJECTID, ROTATION, COM_VALUE, EXMPT_VAL, SHAPE.area, and SHAPE.len



Also, directly in regards to highlighting a layer, when I try to add a feature from WFSFeatureLayer that is pointing to my OpenSpace layer with OBJECTID = 6, I get the following error on map.refresh: 
  
 Index was outside the bounds of the array. 
  
 I sent the corresponding URL and XML output to support as well. If you could kindly take a look at both that would be terrific.  
  
 Just to reiterate, the first email I sent is in reference to the FeatureSourceColumns being returned which point to my NHSITE layer. 
  
 The second is about highlighting feature from the WFS layer pointing at OPENSPACE layer with OBJECTID=6. 
  
 Thanks.

Nelson,


We tried to connect to the service URL in your e-mail but it failed to connect. Also, I tried to create a WfsFeatureSource based on your link:
 


string serviceUrl = "GeoTS/ArcGIS/services/BrooklineWFS/MapServer/WFSServer/";
string layerName = "BrooklineWFS:nhsite";

WfsFeatureSource featureSource = new WfsFeatureSource(serviceUrl, layerName);
featureSource.RequestedData += new EventHandler<RequestedDataWfsFeatureSourceEventArgs>(featureSource_RequestedData);
featureSource.Open();


 
The feature source open failed because the service URL still can not be connected. So I think you need to keep your WFS service available so that we can test against the GetColumns stuff.
 
Thanks,
 
sun

sun, 
  
 I am really confused… I was told the major point for me receiving build 382 was so that you could take my XML responses and inject them into the events of WFSFeatureSource so that I would not need to make my WFS publicly available. It is not a public resource , will never be a public resource, is intended strictly for internal use and is not even licensed for us to be used in a public way.  
  
 I thought support would be clued into this so as to make the most of both of our times…

Nelson,


We just want to make sure we can get the same response XML as yours in our machine, and we also want to recreate the IndexOutOfBounds exception you mentioned before by accessing your WFS service.
 
For the GetColumns method, we found some problem in the code of our WfsFeatureSource using the XML you supplied to us. The code has been modified and it can get 49 columns from your XML. You can get the new build and test against this tomorrow. Thank you for helping us finds the problem!
 
And about the IndexOutOfBounds exception, I found that there maybe some invalid feature in the return features of function GetFeaturesByColumnValue. I saw that in the XML you sent to us contains all the features in your service, but we failed to load the XML with features. Could you please send us a txt file which contains the XML without any format? You can save the XML to a txt file in the RequestedData event like this:
 

private void featureSource_RequestedData(object sender, RequestedDataWfsFeatureSourceEventArgs e)
        {
            StreamWriter writer = null;

            try
            {
                FileStream stream = new FileStream("XmlResponse.txt", FileMode.OpenOrCreate, FileAccess.Write);
                writer = new StreamWriter(stream);
                writer.WriteLine(e.ServiceUrl);
                writer.WriteLine("");
                writer.WriteLine(e.XmlResponse);
                writer.WriteLine("");
                writer.WriteLine("");
            }
            finally
            {
                if (writer != null)
                {
                    writer.Close();
                }
            }
        }

 
Sorry for the inconvenience and any more questions please let me know.
 
Thanks,
 
sun

Hi again sun, 
  
 Thanks for the updated build. The column names now seems to be returning correctly. I am now having a new problem however. In my parcels layer, I am able to highlight by some criteria but not others. I am also noticing in my identify tool, I am using a GetFeaturesIntersecting using a point which works fine except for the feature returned, the first attribtue it is returning is "parcels" when it should be OBJECTID. the attribute is empty. There are also only 42 columns being returned when there should be 52. Of course, every column needs to be returned regardless of whether the value is empty or not. 
  
 I have sent the Parcels XML over to support. It would be really helpful if using the OpenSpace, Parcels, and NHSITE files I have sent you, you could check every function and property of the WFSFeatureLayer to verify that they are working correctly and consistantly returning all the data. It will be nice to know it has been throughly checked across the board.  
  
 Thank you!

Just to add a little more specific feedback, I am noticing that if I do GetAllFeatures and specify all columns returned, I am getting most of them but not all, and parcels is the first attribute (it isn’t an attrbute at all) but I am assuming that is due to XML parsing. Also, If I specify that I want the OBJECTID column duriung GetAllFeatures, I am returned ‘parcels’, Shape.area and Shape.len. I am hoping that wherever this issue is popping up throughout various functions that it will be corrected.

Nelson,


Thanks for sending us the XML file with all the features, but we also need the schema XML file of the Parcel layer like the NHSITE layer you have sent to us. That’s because every layer has a corresponding schema file to describe the columns information. 
 
The columns of a specific layer will be specified at the open method, so when you write the code like this:
 


string serviceUrl = "GeoTS/ArcGIS/services/BrooklineWFS/MapServer/WFSServer/";
string layerName = "BrooklineWFS:parcels";

WfsFeatureSource featureSource = new WfsFeatureSource(serviceUrl, layerName);
featureSource.RequestingData += new EventHandler<RequestingDataWfsFeatureSourceEventArgs>(featureSource_RequestingData);
featureSource.RequestedData += new EventHandler<RequestedDataWfsFeatureSourceEventArgs>(featureSource_RequestedData);
featureSource.Open();


 


You will get the response XML in the RequestedData event, then save it to a txt file and send it to us.
 
Thanks,
 
sun

I did send it... It's 7MB and I was told by support@thinkgeo.com at 12PM on Friday that it was forwarded as necessary...


Edit: After being a little confused and checking out the XML dump file after a couple of calls, it looks like the StreamWriter code you supplied is not clearing the entire contents of the file (or deleting the file altogether) before writing a new one so after subsequent requests if the second request is smaller than the first one, you will get a mixture of both. This might be what is causing some confusion on your end as well when receiving these files. I am going to make a full XML response for OpenSpace, NhSite, and Parcels and then the request made for a GetAllColumns for each as well.


Sorry for the inconvenience.



Nelson,


First of all, I want to make sure the columns count of parcels layer is 54, there names is as following and you can check this use the sample in the attachment:
 
OBJECTID, MAP, BLOCK, BLK_LOT_SUB, PARCEL_ID, CLASS_CODE, PAR_ADD_NO_1, PAR_ADD_NO_2, PAR_ADD_ST_1, PAR_ADD_ST_2, OWNER_NAME, CO_OWNER_NAME, OWNER_ADDRESS, OWNER_CITY, OWNER_STATE, OWNER_ZIP, OWNER_COUNTRY, TOT_LND_AREA, RES_EXEMPT, RES_VALUE, COM_VALUE, EXMPT_VAL, DEED_BOOK, DEED_PAGE, DEED_CERT, DEED_DATE, SALE_VALID, PCT_INT_OWND, TOT_FIN_AREA, ZONING, SALE_PRICE, ATTEMPT_DATE, INSPECT_DATE, ENTRANCE, INFO_SOURCE, COLLECTOR_ID, CUR_RE_VALUE, CUR_RE_TAX, SBD_PARCEL_ID, SUBDVSN_FY, SUBDVSN_TYPE, ACCOUNT, LOT_SUB, LOT, SUB, DATE_UPDATE, UPDATED, NOTE_, LOC_ID, ROTATION, ASSOC_PAR_1, GEOTMSPARCELNO, Shape.area, Shape.len,
 
Secondly, you said the returning column name is “parcels”, I think you may set the wrong layer name, you should set it like this:
 


string serviceUrl = "GeoTS/ArcGIS/services/BrooklineWFS/MapServer/WFSServer/";
string layerName = "BrooklineWFS:parcels";

WfsFeatureLayer featureLayer = new WfsFeatureLayer(serviceUrl, layerName);


 
For the returning columns of the GetFeaturesIntersecting for parcels layer, its number is only three: OBJECTID, Shape.area, Shape.len, and I think this result is right. You can take a look at the XML with features, every feature just contains that three columns. Other columns don’t exist at all but not have empty value like you said.
 
This is a screenshot of the parcels layer only containing three columns:
 

 
This is a screenshot of the openspace layer containing all the columns but some of them are empty: 

 
The XML node should be like this if the column’s value is empty, and the WfsFeatureSource will parse it and get an empty value.
<BrooklineWFS:GRANTOR />
 
Any more questions please let me know.
 
Thanks,
 
sun

 



1068-Post6095Demo.zip (10.6 KB)

Hi sun, the errors I seem to be getting have been corrected by matching the typeName of the layers case-sensitively. Can this be changed? We can control it enough on our end but still if it must be case sensitive then the layer shouldn't load at all. This leads me to believe it is a minor bug in the XML parsing in Map Suite. 



I am also receiving an odd error that may be a result of the scenario we've been discussing above but I am unsure. 



I have a function that gets me all the unique values for a column in a layer, in this instance that layer will be parcels. I then use this to generate a Value Based Style. This is nice for zoning and such. Well, when I try to do this for the 'MAP' column, I am getting the following error on Map.Refresh and it persists unless I make the layer invisible or remove it entirely. 



"The requested column is not in the feature." 



I am not sure why this would be happening.


 


Edit: This is happening because GeoCache.IsActive is turned on. Turning it off resolves the problem. Can this be fixed?



I am also getting an error when using my HistoricLandmarks layer and trying to GetFirstFeaturesWellKnownType of Index out of bounds. Please take a look?



1080-HistoricSite_FirstFeaturesWKT.txt (3.98 KB)
1081-HistoricSite_FullFeatureDescription.txt (8.67 KB)

Nelson,


First, about the case sensitive problem, we have fixed it.
 
Second, about the value style on the parcels layer, both the ‘MAP’ and ‘ZONING’ columns don’t work in my sample. This is because there are only three columns on each feature of the parcels layer, and they are “OBJECTID”, “Shape.area” and “Shape.len”. You can take a look at the screenshot in my last reply. So I think you can use the value style based on only these three columns for the parcels layer. 
 
At last, about the Index out of bounds error on the HistoricLandmarks layer, I can recreate the exception and it has been fixed.
 
Any more questions please let me know.
 
Thanks,
 
sun 

Thank you on the fixes. I will see if I can request a new build. 
  
 About the parcels and only those columns being available, it is because the GeoCache is active and not getting all of the columns. I spoke with David about it yesterday and he recommended trying to do a GetAlFeaturesInsideBoundingBox to retrieve all features and columns first but this didn’t work quite right for me. You will notice that if you look at the schema of the Parcels layer, it actually has 54 columns but here is the problem as I understood it: 
  
 Pretty much, the GeoCache will cache the first request made to a FeatureSource only, so when I do a WFSFeatureLayer.GetBoundingBox, it is caching that response which is the entire data set except without columns returned. Can you advise me on how I can GeoCache my entire set of Features and Column Data so that I can minimize my trips back to the WFS server every time I zoom in and pan out and maintain the column data? 
  
 Thanks.

Also, it seems like while there are 54 Columns, only 42 or 43 are being attached to features that are not cached, depending on which feature I am looking at. This all ties into an issue I am trying to figure out right now which is the following: 
  
 If any given feature does not have a value in the ZONING column but all the rest of the features do, will the ZONING column not be attached to any feature objects that do not return a ZONING value, even if the WFS is returning all the columns? 
  
 Imagine there are only 500 features in the Parcels layer. Say half of them have ZONING column values. The rest are empty. It seems like Map Suite is not attaching column objects to feature objects where the values are empty perhaps. This is a real huge problem because this breeds a lot of inconsistency for many of the things I am doing but it seems like it is breaking Map Suite’s normal behavior too.  
  
 The reason I say that is because now that I have manually inserted the features into the GeoCache, which looks like it’s taking twice as long as letting it happen on it’s own, I am able to apply a ValueStyle to my MAP column because every record has a value for MAP. But when I try to do ZONING, there are a couple of features that do not contain values, either empty space or NULL. I can handle some of this on my end for checking for things like this before even creating a ValueStyle, but MapSuite is now throwing the error of ‘The given key was not present in the dictionary’ and I think it is because the column is not being found on a per feature basis if the value is empty. 
  
 Does this make sense?  This is the case it seems with or without cache being on at this point. It would be application-breaking if I can not apply a ValueBased rendering on a WFSFeatureLayer unless every feature has a value for the column of choice. I don’t think ShapeFileFeatureLayer works this way… 


Nelson,


About the GeoCache problem, the features can not be cached in the GetBoundingBox method. The features will be cached when you call winformMap.Refresh(). So if you want to cache all the features at the first time, you can write the code like this:
 


featureLayer.Open();

Collection<FeatureSourceColumn> columns = featureLayer.QueryTools.GetColumns();
foreach (FeatureSourceColumn column in columns)
{     
   featureLayer.ZoomLevelSet.ZoomLevel01.DefaultTextStyle.RequiredColumnNames.Add(column.ColumnName);
}

winformsMap1.CurrentExtent = featureLayer.GetBoundingBox();
winformsMap1.Refresh();


 
And about the columns problem, you said that some of the ZONING columns value is empty, you should make sure the ZONING column node is contained in every feature’s node like this:
 

and to resolve the this problem, we make some change to the WfsFeatureSource, you can have a try with the latest build.
 
Any more questions please let me know.
 
Thanks,
 
sun