Charley,
We will enhance the style so that it sorts the breaks. One reason we didn't so that in the beginning was for performance. If on every render we have to check the order of the breaks then it will add to the overhead. You might not think by much however we have run into people who have crazy things like 3 thousand class breaks etc. We advise them not to create their own style and handle the logic there, typically people who have that many really want something else and are trying to wedge their requirements into the general class break style.
The reason we do not use min and max anymore is because people found it very confusing and prone to error. We had min and max class break renderer in the beginning and there was so much support related to it we vowed to find a better way. I think the main problems were people having gaps in the min and max, overlapping values, not knowing if the values were inclusive or exclusive of the ranges, and all kids of stuff. With one number per break it is one have the problems it seems.
If you are coming from 2.x then the good news is that 3.x is super extensible. As the general concept around a class break renderer is simple writing a custom style is simple as well. Below is something I whipped up. I am not saying it is perfect but it uses the Min & Max concept. You can adjust it however you want and really customize it to work they way you require it to. I can also provide the code for the exisitng ClassBreakStyle as well if you need it.
David
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
namespace ThinkGeo.MapSuite.Core
{
[Serializable]
public class MinMaxClassBreakStyle : Style
{
private string columnName;
private BreakValueInclusion breakValueInclusion;
private Collection<MinMaxClassBreak> classBreaks;
// The next few methods are just some constructor overloads to make it easy to inline code
public MinMaxClassBreakStyle()
: this(string.Empty, BreakValueInclusion.IncludeValue) { }
public MinMaxClassBreakStyle(string columnName)
: this(columnName, BreakValueInclusion.IncludeValue) { }
public MinMaxClassBreakStyle(string columnName, BreakValueInclusion breakValueInclusion)
: this(columnName, breakValueInclusion, new Collection<MinMaxClassBreak>()){}
public MinMaxClassBreakStyle(string columnName, BreakValueInclusion breakValueInclusion, Collection<MinMaxClassBreak> classBreaks)
: base()
{
this.columnName = columnName;
this.breakValueInclusion = breakValueInclusion;
this.classBreaks = classBreaks;
}
// The next few items are properties. We ensure that all of the constructor parameters
// are parameters to you can use the constructor or call them by property
public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}
public BreakValueInclusion BreakValueInclusion
{
get { return breakValueInclusion; }
set { breakValueInclusion = value; }
}
public Collection<MinMaxClassBreak> ClassBreaks
{
get { return classBreaks; }
}
// Here is where we have some of the main logic. First we loop through all of the features.
// For each feature we get the field value and then we call another private method to find the right
// class break. If we find one then we call the drawing methods on the styles associated with the break.
protected override void DrawCore(IEnumerable<Feature> features, GeoCanvas canvas, Collection<SimpleCandidate> labelsInThisLayer, Collection<SimpleCandidate> labelsInAllLayers)
{
foreach (Feature feature in features)
{
double fieldValue = double.Parse(feature.ColumnValues[columnName].Trim(), CultureInfo.InvariantCulture);
MinMaxClassBreak classBreak = GetClassBreak(fieldValue);
if (classBreak == null) { continue; }
Feature[] tmpFeatures = new Feature[1] { feature };
if (classBreak.CustomStyles.Count == 0)
{
classBreak.DefaultAreaStyle.Draw(tmpFeatures, canvas, labelsInThisLayer, labelsInAllLayers);
classBreak.DefaultLineStyle.Draw(tmpFeatures, canvas, labelsInThisLayer, labelsInAllLayers);
classBreak.DefaultPointStyle.Draw(tmpFeatures, canvas, labelsInThisLayer, labelsInAllLayers);
classBreak.DefaultTextStyle.Draw(tmpFeatures, canvas, labelsInThisLayer, labelsInAllLayers);
}
else
{
foreach (Style style in classBreak.CustomStyles)
{
style.Draw(tmpFeatures, canvas, labelsInThisLayer, labelsInAllLayers);
}
}
}
}
// Here is where we determine if a value is in a class break. If you were to enhance this
// sample to do your own checking then this is where the bulk of your changes would be.
// In this example we just loop through the breaks and if the value is between the min and max
// then we accept it and stop checking. We also do a check if we are to include or exclude the min and
// max values themselves.
private MinMaxClassBreak GetClassBreak(double columnValue)
{
MinMaxClassBreak result = null;
if (breakValueInclusion == BreakValueInclusion.IncludeValue)
{
// In here we are including the values we we use the '<=' and >='
for (int i = 0; i < classBreaks.Count; i++)
{
if (columnValue <= classBreaks[i].MaxValue && columnValue >= classBreaks[i].MinValue)
{
result = classBreaks[i];
break;
}
}
}
else
{
// In here we are excluding the values we we use the '<' and >'
for (int i = 0; i < classBreaks.Count; i++)
{
if (columnValue < classBreaks[i].MaxValue && columnValue > classBreaks[i].MinValue)
{
result = classBreaks[i];
break;
}
}
}
return result;
}
// In here we examine all of the class breaks and get any fields that its style may
// require to render if it is chosen. More more infromation see the documentation for
// this method on the Style class.
protected override Collection<string> GetRequiredColumnNamesCore()
{
Collection<string> requiredFieldNames = new Collection<string>();
requiredFieldNames.Add(columnName);
foreach (MinMaxClassBreak classBreak in classBreaks)
{
foreach (Style style in classBreak.CustomStyles)
{
Collection<string> tmpCollection = style.GetRequiredColumnNames();
foreach (string name in tmpCollection)
{
if (!requiredFieldNames.Contains(name))
{
requiredFieldNames.Add(name);
}
}
}
Collection<string> fieldsInTextStyle = classBreak.DefaultTextStyle.GetRequiredColumnNames();
foreach (string fieldName in fieldsInTextStyle)
{
if (!requiredFieldNames.Contains(fieldName))
{
requiredFieldNames.Add(fieldName);
}
}
}
return requiredFieldNames;
}
}
// What I did here was to include the new break class. Normally you want each file to be
// a seperate class but to make posting easier I incuded it here.
// The class itself is really simple and is just a holder of styles and min & max values.
// We have included some properties and helpful constructor overloads for you.
[Serializable]
public class MinMaxClassBreak
{
private double minValue;
private double maxValue;
private PointStyle defaultPointStyle;
private LineStyle defaultLineStyle;
private AreaStyle defaultAreaStyle;
private TextStyle defaultTextStyle;
private Collection<Style> customStyles;
public MinMaxClassBreak()
: this(double.MinValue, double.MinValue, new AreaStyle(), new PointStyle(), new LineStyle(), new TextStyle(), new Collection<Style>())
{
}
public MinMaxClassBreak(double minValue, double maxValue, AreaStyle areaStyle)
: this(minValue, maxValue, areaStyle, new PointStyle(), new LineStyle(), new TextStyle(), new Collection<Style>())
{
}
public MinMaxClassBreak(double minValue, double maxValue, PointStyle pointStyle)
: this(minValue, maxValue, new AreaStyle(), pointStyle, new LineStyle(), new TextStyle(), new Collection<Style>())
{
}
public MinMaxClassBreak(double minValue, double maxValue, LineStyle lineStyle)
: this(minValue, maxValue, new AreaStyle(), new PointStyle(), lineStyle, new TextStyle(), new Collection<Style>())
{
}
public MinMaxClassBreak(double minValue, double maxValue, TextStyle textStyle)
: this(minValue, maxValue, new AreaStyle(), new PointStyle(), new LineStyle(), textStyle, new Collection<Style>())
{
}
public MinMaxClassBreak(double minValue, double maxValue, Collection<Style> customStyles)
: this(minValue, maxValue, new AreaStyle(), new PointStyle(), new LineStyle(), new TextStyle(), customStyles)
{
}
private MinMaxClassBreak(double minValue, double maxValue, AreaStyle areaStyle, PointStyle pointStyle, LineStyle lineStyle, TextStyle textStyle, Collection<Style> styles)
{
this.defaultAreaStyle = areaStyle;
this.defaultPointStyle = pointStyle;
this.defaultLineStyle = lineStyle;
this.defaultTextStyle = textStyle;
this.customStyles = styles;
this.minValue = minValue;
this.maxValue = maxValue;
}
public double MinValue
{
get { return minValue; }
set { minValue = value; }
}
public double MaxValue
{
get { return maxValue; }
set { maxValue = value; }
}
public AreaStyle DefaultAreaStyle
{
get { return defaultAreaStyle; }
set { defaultAreaStyle = value; }
}
public LineStyle DefaultLineStyle
{
get { return defaultLineStyle; }
set { defaultLineStyle = value; }
}
public PointStyle DefaultPointStyle
{
get { return defaultPointStyle; }
set { defaultPointStyle = value; }
}
public TextStyle DefaultTextStyle
{
get { return defaultTextStyle; }
set { defaultTextStyle = value; }
}
public Collection<Style> CustomStyles
{
get { return customStyles; }
}
}
}