ThinkGeo.com    |     Documentation    |     Premium Support

Point Feature Symbol Conflict Resolution

Hello,


Quick problem description. I have an application that tracks incidents along a route. One incident (location) can have multiple events that I have to symbolize. eg car fire, 911 police response, 911 fire response, towed vehicle, property damage. So for a single point I may have to draw 1 to (n) symbols for that specific point. I can deal with this OK right now using ValueStyles and offsets. The problem is that if I have two incidents that occur in close proximity, or if I even have two (or more) incidents that occur at the same location. When this happens all the symbols draw over themselves and I end up with visually unusable data.


I was hoping there was some sort of methodology included in the SDK to provide an automated manner in resolving symbol conflict. Specifically regarding point symbols. I figured I could write some possibly complex code to do spatial conflict resolution and offset calculation, but was hoping for an easier approach leveraged by existing functionality.


Any pointers here? Thanks for your time and effort. It is always well appreciated.


 


Eric S.



 I understand very well the problem you are facing to display points that are near each others and I don't  think there is a magic bullet to resolve that, but I do have a suggestion beyond what you have tried so far:


-You could use a ClusterPointStyle. For example, if you have various points close to each others related to the same incident, you could use that Style to represent a composite symbol showing the type of incident and the number of vehicles for example. This would work well if you zoom out and you don't want to have the points all on top of each others while still getting some info on the incident. (The center of gravity of all the points of the same incident is used to place the cluster point). When you zoom in and there is enough space in between the points, you could switch the style and use regular styles.


 There is a Code Community sample that shows how to do that. It is using volcanoes as example, but I think that you should get the idea. Please, check it out, ClusterPointStyle wiki.thinkgeo.com/wiki/Map_Suite_Wp...es_Samples.



Val,


Thanks for the info. Although the cluster point style seems interesting, it does not exactly solve my problem. It may however give me some ideas for creating my own custom style. The problem I have is the same at any scale. I need to display multiple symbols for a single location (incident) and in some cases I may have two incidents at the same location. It has to be visual apparent what happened at each incident based on the displayed images. So, I really need the opposite of what cluster does.


I do have a continuation question/issue I'd like to bring up. As I mentioned I am already able to display multiple symbols for the same location. I am using a ValueStyle to do this. I would like to be able to define multiple ValueItems  with the same value. As I add the ValueItems for a specific incident Id I simple increment the styles offset so the images do not overlap. It only draws the first occurrence of the ValueItem it finds. Is this by design?


I have worked around but it is not very desirable as I have to add another point for each unique ValueItem I want drawn. SO I end up with (n) number of point for a single incident based on the (n) number of events that occur at that incident and need to be symbolized.


So why can I add duplicate values, with different styles in a ValueStyle, but they do not draw. Any insight is appreciated. Thanks.


Eric S.


 



It is a little difficult to follow with a verbal description only what your scenario exactely is and what you are trying to accomplish. I am a little bit lost on what you are doing exactely with the multiple ValueItems, the offsets etc. The best would be to have from you a self contained sample app. It would give us a better representation of what is happening in your scenario. Can you isolate in a little sample app with a simple example to show what is happening with your code? And I think that from there we should be in a more convenient situation to help you concretely. Thank you.

Val, sorry for the confusion. The whole issue here is about drawing multiple symbols for a single geographic location. My first issue was conflict resolution, which I have a ways to go on. The second being the use of ValueStyle. I went ahead a verbalized my problem with code, This should clear things up.


As you can see I simply add two point features to my map. I try to symbolize the first point with three separate valueitems. It only draws the first. On order to draw all three I need a separate point for each valueitem.


Another question is why doesn't the use of CustomPointStyle work. Am I doing something wrong. I thought I should just be able to add the pointsymbol for each symbol as a CustomPointStyle, but it would never draw.


Here is my code that you simple put into a form load event of a form with a mapcontrol. Hope this helps illustrate my issues with Valuestyle. Thanks.


 


Eric S.



private void Form1_Load(object sender, EventArgs e)
        {
            // somewhere in Virginia
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
            winformsMap1.CurrentExtent = ExtentHelper.GetDrawingExtent(new RectangleShape(-78, 39, -76, 38), winformsMap1.Width, winformsMap1.Height);
            //winformsMap1.CurrentExtent = new RectangleShape(-78,39,-76,38);
            


            InMemoryFeatureLayer eventLayer = new InMemoryFeatureLayer();
            eventLayer.Open();
            eventLayer.Columns.Add(new FeatureSourceColumn("EventId"));
            eventLayer.Close();

            ValueStyle EventValueStyle = new ValueStyle();
            EventValueStyle.ColumnName = "EventId";


            eventLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(EventValueStyle);
            //eventLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = new PointStyle(PointSymbolType.Square,
            //                                                                       new GeoSolidBrush(
            //                                                                           GeoColor.StandardColors.Red),
            //                                                                       new GeoPen(
            //                                                                           GeoColor.StandardColors.Red), 20);
            eventLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            eventLayer.Name = "Events";


            // create a feature to add to the  map
            Feature feature1 = new Feature(-77,38.5,"1");
            feature1.ColumnValues["EventId"] = "1001";
            

            // need to symbolize the feature with three symbols
            PointStyle symbol1 = new PointStyle();
            symbol1.PointType = PointType.Symbol;
            //symbol1.CustomPointStyles.Add(new PointStyle(PointSymbolType.Square, new GeoSolidBrush(GeoColor.StandardColors.Red), new GeoPen(GeoColor.StandardColors.Red), 20));
            symbol1.SymbolType = PointSymbolType.Circle;
            symbol1.SymbolSize = 20;
            symbol1.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Red);
            symbol1.SymbolPen = new GeoPen(GeoColor.StandardColors.Red);
            symbol1.XOffsetInPixel = 0;

            PointStyle symbol2 = new PointStyle();
            symbol2.PointType = PointType.Symbol;
            //symbol2.CustomPointStyles.Add(new PointStyle(PointSymbolType.Cross, new GeoSolidBrush(GeoColor.StandardColors.Blue), new GeoPen(GeoColor.StandardColors.Blue), 20));
            symbol2.SymbolType = PointSymbolType.Cross;
            symbol2.SymbolSize = 20;
            symbol2.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Blue);
            symbol2.SymbolPen = new GeoPen(GeoColor.StandardColors.Blue);
            symbol2.XOffsetInPixel = 21;

            PointStyle symbol3 = new PointStyle();
            symbol3.PointType = PointType.Symbol;
            //symbol3.CustomPointStyles.Add(new PointStyle(PointSymbolType.Diamond, new GeoSolidBrush(GeoColor.StandardColors.Green), new GeoPen(GeoColor.StandardColors.Green), 20));
            symbol3.SymbolType = PointSymbolType.Diamond;
            symbol3.SymbolSize = 20;
            symbol3.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Green);
            symbol3.SymbolPen = new GeoPen(GeoColor.StandardColors.Green);
            symbol3.XOffsetInPixel = 21;


            // Problem Area!!
            // Now try to add the three styles to one value. Shouldn't this draw all three symbols for the one value
            // This does now work it only draws the first style
            EventValueStyle.ValueItems.Add(new ValueItem("1001", symbol1));
            EventValueStyle.ValueItems.Add(new ValueItem("1001", symbol2));
            EventValueStyle.ValueItems.Add(new ValueItem("1001", symbol3));


            // in order to get multiple symbols to display for a single point I have to duplicate the point for each symbol
            Feature feature2 = new Feature(-76.5, 38.75, "2");
            feature2.ColumnValues["EventId"] = "1002";
            Feature feature3 = new Feature(-76.5, 38.75, "3");
            feature3.ColumnValues["EventId"] = "1003";
            Feature feature4 = new Feature(-76.5, 38.75, "4");
            feature4.ColumnValues["EventId"] = "1004";

            // and add a seperate valueitem for each duplicate feature
            // this is not desirable but only way to get it to work
            EventValueStyle.ValueItems.Add(new ValueItem("1002", symbol1));
            EventValueStyle.ValueItems.Add(new ValueItem("1003", symbol2));
            EventValueStyle.ValueItems.Add(new ValueItem("1004", symbol3));


            eventLayer.Open();
            eventLayer.EditTools.BeginTransaction();
            eventLayer.EditTools.Add(feature1);
            eventLayer.EditTools.Add(feature2);
            eventLayer.EditTools.Add(feature3);
            eventLayer.EditTools.Add(feature4);
            eventLayer.EditTools.CommitTransaction();
            eventLayer.Close();



            LayerOverlay eventOverlay = new LayerOverlay();
            eventOverlay.Name = "Events";
            eventOverlay.Layers.Add("EventsLayer", eventLayer);
            winformsMap1.Overlays.Add("EventsOverlay", eventOverlay);


            winformsMap1.Refresh();

        }


Eric,


Thanks for your post and questions.
I can understand your problem very easily with the help of the sample code provided, it is very convenient to show us the code snippet, I appreciate it very much.
I think for the ValueStyle, we will normally match one ValueItem for one feature, while in your case, you are trying to match more than one at a time, so it does not work. One way I can figure out to solve this problem is trying to add multiple styles for the layer, see following updated version code snippet.

// somewhere in Virginia
winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
winformsMap1.CurrentExtent = ExtentHelper.GetDrawingExtent(new RectangleShape(-78, 39, -76, 38), winformsMap1.Width, winformsMap1.Height);
//winformsMap1.CurrentExtent = new RectangleShape(-78,39,-76,38);
 
 
InMemoryFeatureLayer eventLayer = new InMemoryFeatureLayer();
eventLayer.Open();
eventLayer.Columns.Add(new FeatureSourceColumn("EventId"));
eventLayer.Close();
 
ValueStyle EventValueStyle = new ValueStyle();
EventValueStyle.ColumnName = "EventId";
 
 
eventLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
eventLayer.Name = "Events";
 
 
// create a feature to add to the map
Feature feature1 = new Feature(-77, 38.5, "1");
feature1.ColumnValues["EventId"] = "1001";
 
 
// need to symbolize the feature with three symbols
PointStyle symbol1 = new PointStyle();
symbol1.PointType = PointType.Symbol;
//symbol1.CustomPointStyles.Add(new PointStyle(PointSymbolType.Square, new GeoSolidBrush(GeoColor.StandardColors.Red), new GeoPen(GeoColor.StandardColors.Red), 20));
symbol1.SymbolType = PointSymbolType.Circle;
symbol1.SymbolSize = 20;
symbol1.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Red);
symbol1.SymbolPen = new GeoPen(GeoColor.StandardColors.Red);
symbol1.XOffsetInPixel = 0;
 
PointStyle symbol2 = new PointStyle();
symbol2.PointType = PointType.Symbol;
//symbol2.CustomPointStyles.Add(new PointStyle(PointSymbolType.Cross, new GeoSolidBrush(GeoColor.StandardColors.Blue), new GeoPen(GeoColor.StandardColors.Blue), 20));
symbol2.SymbolType = PointSymbolType.Cross;
symbol2.SymbolSize = 20;
symbol2.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Blue);
symbol2.SymbolPen = new GeoPen(GeoColor.StandardColors.Blue);
symbol2.XOffsetInPixel = 21;
 
PointStyle symbol3 = new PointStyle();
symbol3.PointType = PointType.Symbol;
//symbol3.CustomPointStyles.Add(new PointStyle(PointSymbolType.Diamond, new GeoSolidBrush(GeoColor.StandardColors.Green), new GeoPen(GeoColor.StandardColors.Green), 20));
symbol3.SymbolType = PointSymbolType.Diamond;
symbol3.SymbolSize = 20;
symbol3.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Green);
symbol3.SymbolPen = new GeoPen(GeoColor.StandardColors.Green);
symbol3.XOffsetInPixel = 21;
 
ValueStyle valueStyle1 = new ValueStyle();
valueStyle1.ColumnName= "EventId";
valueStyle1.ValueItems.Add(new ValueItem("1001",symbol1));
 
ValueStyle valueStyle2 = new ValueStyle();
valueStyle2.ColumnName = "EventId";
valueStyle2.ValueItems.Add(new ValueItem("1001", symbol2));
 
ValueStyle valueStyle3 = new ValueStyle();
valueStyle3.ColumnName = "EventId";
valueStyle3.ValueItems.Add(new ValueItem("1001", symbol3));
 
eventLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(valueStyle1);
eventLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(valueStyle2);
eventLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(valueStyle3);
 
 
eventLayer.Open();
eventLayer.EditTools.BeginTransaction();
eventLayer.EditTools.Add(feature1);
//eventLayer.EditTools.Add(feature2);
//eventLayer.EditTools.Add(feature3);
//eventLayer.EditTools.Add(feature4);
eventLayer.EditTools.CommitTransaction();
eventLayer.Close();
 
LayerOverlay eventOverlay = new LayerOverlay();
eventOverlay.Name = "Events";
eventOverlay.Layers.Add("EventsLayer", eventLayer);
winformsMap1.Overlays.Add("EventsOverlay", eventOverlay);
 
winformsMap1.Refresh();

Any more questions please feel free to let me know.
Thanks.
Yale

Thanks for the feedback Val!


This might be a workable solution for me. Except, the challenge here is that each incident can have 0->n events. I  have my code working so that if it doesn't seem a valueitem in the valuestyle for an event image, it adds it. I'll now have to manage multiple ValueStyles per incident. Probably doable. 


 My question here is how much will this degrade map performance. I can have 1->n incidents per map and each incident can have 0->n events. It can start to add up quickly depending on the area of interest being analyzed. Say 50 incidents with 4-5 events each/ Will the use of a valuestyle for each event affect map refresh performance?


Also, I didn't get any feedback as to the problem with my use of CustomPointStyle in my sample. Why does this not work?


As always thanks for your time and feedback. Always appreciated.


 


Eric S.



Eric,


Thanks for your response.


I do not think the solution to add multiple ValueStyles suggested in my previous post will degrade the performance compare to your solution, because the majority of time is spent on the drawing staff instead of searching out the match ValueItem.


About the not drawing issue when using the CustomPointStyles, I think it can be considered as a minor bug because it always consider the symbol1 as transparent if we do not set the color for the SymbolPen and SymbolSolidBrush, causing the skip of drawing, following way do the trick.



// need to symbolize the feature with three symbols
PointStyle symbol1 = new PointStyle();
symbol1.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors. Yellow);
symbol1.CustomPointStyles.Add(new PointStyle(PointSymbolType.Circle, new GeoSolidBrush(GeoColor.StandardColors.Red), new GeoPen(GeoColor.StandardColors.Red), 20));
//symbol1.SymbolType = PointSymbolType.Circle;
//symbol1.SymbolSize = 20;
//symbol1.SymbolSolidBrush = new GeoSolidBrush(GeoColor.StandardColors.Red);
//symbol1.SymbolPen = new GeoPen(GeoColor.StandardColors.Red);
//symbol1.XOffsetInPixel = 0;
ValueItem valueItem1 = new ValueItem();
valueItem1.Value = "1001";
valueItem1.CustomStyles.Add(symbol1);
 
ValueStyle valueStyle1 = new ValueStyle();
valueStyle1.ColumnName = "EventId";
            
valueStyle1.ValueItems.Add(valueItem1);

Any more questions or ideas please feel free to let me know.


Thanks.


Yale