ThinkGeo.com    |     Documentation    |     Premium Support

Problem with GetLabelingCandidates

Hello all,


I'm having an issue using the GetLabelingCandidates method in the PositionStyle class, and I'm not terribly sure why.  I'm using this in a custom style, so I'm sure it's something I'm not doing correctly. Upon using the method, I always get an error, "Value cannot be null. Parameter name: key".  At first I thought it might be the Feature parameter, but the Column Values that are needed are included in the Feature. So that leaves me with the GeoCanvas parameter, and I have absolutley no idea what the method is trying to pull from there. Is there any place I can find out what properties of the GeoCanvas this method is using, so that I can make sure they're correctly filled?  Thanks in advance!


-Dustin


EDIT: I failed to mention that I am using a GeoImage to draw directly to (not unlike the WMS example included with the Services Edition), and as such, I am not using the MapEngine.



As an update, I have given the whole Map Engine thing a whirl, and am getting the same error on this particular custom style. I was thinking the GetLabelingCandidates method was looking for keys for the layers, so I assigned them as I was adding them to the map engine, but it appears that didn’t solve the problem. 
 -Dustin

Dustin, 
  
 I think it’s not because GeoCanvas is null, if that the error message should be “The parameter you supplied may not be null.” And I check our validators system, there is no "Value cannot be null. Parameter name: key" threw by our core, so it maybe a unhandled exception. PositionStyle is an abstract class and GetLabelingCandidates is an protected method which can not use it directly. Could you give me a sample code to show how you call this method with instance parameters, and also it’s better you can provide a stack-trace for the error. 
  
 Thanks, 
 James 


Hi James,


It's not an error being thrown by the core, for sure. It's being thrown by the MS library System.Collections for something trying to access an invalid key/value in a Dictionary, but I'm not sure what it's trying to find. The only Dictionary I can see in either the canvas or feature objects is in the Feature, and that is ColumnValues, though the one column key/value that I require is present.  My custom style also inherits from PositionStyle, so that is how I'm able to call the GetLabelingCandidates method. There is a lot of code that goes into the class, so I'm not sure exactly what to include for you. It would be great if I could find out which Dictionary the method is trying to access, and which key/value it's trying to find. 


Thanks so much for your help.


-Dustin


Stack trace follows:


   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)

   at System.Collections.Generic.Dictionary`2.ContainsKey(TKey key)

   at ThinkGeo.MapSuite.Core.PositionStyle.GetLabelingCandidateCore(Feature feature, GeoCanvas canvas)

   at ThinkGeo.MapSuite.Core.PositionStyle.GetLabelingCandidates(Feature feature, GeoCanvas canvas)

   at WmsServer.ScaledStyle.DrawCore(IEnumerable`1 features, GeoCanvas canvas, Collection`1 labelsInThisLayer, Collection`1 labelsInAllLayers) in C:\Projects\WmsServer\WmsServer\ScaledStyle.cs:line 82

   at ThinkGeo.MapSuite.Core.Style.Draw(IEnumerable`1 features, GeoCanvas canvas, Collection`1 labelsInThisLayer, Collection`1 labelsInAllLayers)

   at ThinkGeo.MapSuite.Core.ZoomLevel.DrawCore(GeoCanvas canvas, IEnumerable`1 features, Collection`1 currentLayerLabels, Collection`1 allLayerLabels)

   at ThinkGeo.MapSuite.Core.ZoomLevel.Draw(GeoCanvas canvas, IEnumerable`1 features, Collection`1 currentLayerLabels, Collection`1 allLayerLabels)

   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.Core.MapEngine.6Bw=(Layer 6Rw=, Object 6hw=)

   at ThinkGeo.MapSuite.Core.MapEngine.zxw=(IEnumerable`1 3Bw=, GeoImage 3Rw=, GeographyUnit 3hw=, Boolean 3xw=)

   at ThinkGeo.MapSuite.Core.MapEngine.DrawStaticLayers(GeoImage image, GeographyUnit mapUnit)

   at WmsServer.WmsServer.GetMap(GetMapRequest MapRequest) in C:\Projects\WmsServer\WmsServer\WmsServer.aspx.cs:line 96

   at WmsServer.WmsServer.Page_Load(Object sender, EventArgs e) in C:\Projects\WmsServer\WmsServer\WmsServer.aspx.cs:line 29

   at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)

   at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)

   at System.Web.UI.Control.OnLoad(EventArgs e)

   at System.Web.UI.Control.LoadRecursive()

   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)



Dustin, 
  
 You need to assign a value to TextColumnName and use it as key to add KeyValuePair to input feature.ColumnValues, otherwise it could get labeling candidate, for example, you set TextColumnName = "CountryName", and set feature.ColumnValues = "USA". 
  
 Let me know if you solve the problem or not. 
  
 Thanks, 
 James

Thanks for the reply James.


I've actually already had both of those things set, hence my confusion.  My custom style is essentially a ValueBreak style that compares the current map scale to the assigned value of the break, instead of a column value. So, the setup of the class goes something like this.


 



ShapeFileFeatureLayer CanHwyLayer = new ShapeFileFeatureLayer(this.MapPath("") + @"\App_Data\canhwy.shp");
CanHwyLayer.Name = "CanHwy";
                    
ScaledStyleBreak s3 = new ScaledStyleBreak(4500000, LineStyles.CreateSimpleLineStyle(GeoColor.FromArgb(255, 201, 176, 57), 1, GeoColor.FromArgb(255, 193, 167, 108), 3, false));
s3.DefaultTextStyle = TextStyles.CreateSimpleTextStyle("NAME", "Arial", 8, DrawingFontStyles.Regular, GeoColor.SimpleColors.Black);
ScaledStyle ScaledStyle = new ScaledStyle(new Collection<ScaledStyleBreak>() { s3 });                            CanHwyLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(ScaledStyle);

CanHwyLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

So here you can see that the TextColumn field is set. Also, when the ScaledStyle is created, the TextColumn is added to the RequiredFields, which propagates to the Features. When DrawCore is called, I loop through the features of the layer, all of which have the "NAME" key/value pair, and try to call the method in question on the feature and the canvas provided as a parameter to the DrawCore method. Apologies for the formatting, it wasn't really cooperating.




  protected override void DrawCore(IEnumerable<Feature> features, GeoCanvas canvas, System.Collections.ObjectModel.Collection<SimpleCandidate> labelsInThisLayer, System.Collections.ObjectModel.Collection<SimpleCandidate> labelsInAllLayers)
        {
            ScaledStyleBreak CurrentScaledStyleBreak = new ScaledStyleBreak();
            double CurrentScale = ExtentHelper.GetScale(canvas.CurrentWorldExtent, canvas.Width, GeographyUnit.DecimalDegree);
            for (int i = 0; i < ScaledStyleBreaks.Count; i++)
            {
                if ((i + 1 >= ScaledStyleBreaks.Count && CurrentScale >= ScaledStyleBreaks.OrderBy(s => s.MinimumScale).ElementAt(i).MinimumScale) || (i + 1 < ScaledStyleBreaks.Count && CurrentScale < ScaledStyleBreaks.OrderBy(s => s.MinimumScale).ElementAt(i + 1).MinimumScale && CurrentScale >= ScaledStyleBreaks.OrderBy(s => s.MinimumScale).ElementAt(i).MinimumScale))
                {
                    CurrentScaledStyleBreak = ScaledStyleBreaks.OrderBy(s => s.MinimumScale).ElementAt(i);
                    break;
                }
            }
            if (CurrentScaledStyleBreak != null)
            {
                foreach (Feature feature in features)
                {
if (CurrentScaledStyleBreak.DefaultTextStyle != null)
                    {
                        //Error happens here 
                        Collection<LabelingCandidate> LabelingCandidates = GetLabelingCandidates(feature, canvas);
                        foreach (LabelingCandidate LabelingCandidate in LabelingCandidates)
                        {
//do stuff here
}
}
}
}


Dustin, 
  
 From your code, I can see you set s3.DefaultTextStyle.TextColumnName to “NAME” from code s3.DefaultTextStyle = TextStyles.CreateSimpleTextStyle(“NAME”, …, but I don’t see you set it to ScaledStyle which I guess is inherit from PositionStyle, in overrided DrawCore method, you can check TextColumnName again before calling GetLabelingCandidates, such like “Debug.WriteLine(TextColumnName )”  
  
 Thanks, 
 James

Thanks James! 
 That was it exactly. I was intending to use the ScaledStyle class as simply a collection that holds ScaledStyleBreaks, so it never occured to me that the method was going to be looking in the ScaledStyle for properties. Looks like I’ve got a bit of reworking to do! Thanks again! 
 -Dustin

Dustin, 
  
 You’re welcome, let me know if you have more questions. 
  
 Thanks, 
 James