ThinkGeo.com    |     Documentation    |     Premium Support

Remove lines from MultilineShape and refresh map

Hi guys,


I'm trying to remove lines from a multilineshape (starting from the end of the multilineshape and until it crosses the passed in point) and refresh the line layer overlay and the map. The multilineshape is a feature in the layers collection of the line layer overlay. My code is as follows:




public void RemoveLinesFromMultilineShapeLine(string lineLayerKey, string lineKey, Position intersectingPosition)
{
LayerOverlay lineLayerOverlay = (LayerOverlay)Map1.Overlays[DEFAULT_LINE_LAYER_OVERLAY_KEY];
InMemoryFeatureLayer lineLayer = null;

if (lineLayerOverlay.Layers.Contains(lineLayerKey))
{
if (lineLayerOverlay.Layers[lineLayerKey] is InMemoryFeatureLayer)
{
lineLayer = (InMemoryFeatureLayer)lineLayerOverlay.Layers[lineLayerKey];

if (lineLayer.InternalFeatures.Contains(lineKey))
{
MultilineShape existingMultiLineShape = (MultilineShape)lineLayer.InternalFeatures[lineKey].GetShape();
int index = existingMultiLineShape.Lines.Count - 1;
RectangleShape rectShape = GenerateMiniBoundingBox(new PointShape(intersectingPosition.Longitude, intersectingPosition.Latitude));
while (!existingMultiLineShape.Lines[index].Crosses(rectShape))
{
existingMultiLineShape.Lines.RemoveAt(index);
index--;
};
existingMultiLineShape.Lines.RemoveAt(index);
lineLayer.InternalFeatures[lineKey] = new Feature(existingMultiLineShape);
lineLayerOverlay.Refresh();
Map1.Refresh();
}
}
else
{
throw new Exception("Another type of overlay with the same key already exists.");
}

}
}
 


However, I get the following error when I refresh the line layer overlay using lineLayerOverlay.Refresh():


The given key was not present in the dictionary.


Stack trace:

   at System.ThrowHelper.ThrowKeyNotFoundException()

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

   at ThinkGeo.MapSuite.SilverlightCore.InMemoryFeatureSource.GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.SilverlightCore.FeatureSource.GetFeaturesForDrawingCore(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.SilverlightCore.FeatureSource.GetFeaturesForDrawing(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.SilverlightCore.FeatureLayer.DrawCore(GeoCanvas canvas, Collection`1 labelsInAllLayers)

   at ThinkGeo.MapSuite.SilverlightCore.Layer.Draw(GeoCanvas canvas, Collection`1 labelsInAllLayers)

   at ThinkGeo.MapSuite.SilverlightEdition.LayerOverlay.DrawCore(RectangleShape worldExtent)

   at ThinkGeo.MapSuite.SilverlightEdition.Overlay.Draw(RectangleShape worldExtent, OverlayDrawType overlayDrawType)

   at ThinkGeo.MapSuite.SilverlightEdition.Overlay.Refresh()

   at MapControl.RemoveLinesFromMultilineShape(String lineLayerKey, String lineKey, Position intersectingPosition)


What am I doing wrong?


Thanks,


Nirish



Hi Nirish,


The problem is caused by column values lost after the feature modified. Please try the following code:



 ......
 Dictionary<string,string> columnValues= lineLayer.InternalFeatures[lineKey].ColumnValues;
 lineLayer.InternalFeatures[lineKey] = new Feature(existingMultiLineShape, columnValues);
 ......


Any questions please let us know.


Thanks,


Johnny



Thanks Johnny! Much appreciated!

Nirish, 
 You are so welcome. Any question please let us know. 
  
 Johnny

Hi Johnny,


I found an issue with the using the position of a marker as the interesecting position when the marker is not right next to the street. In such cases, it wouldn't find the bounding box crossing the line segment (please see below). 



So I just got the closest point on the line from the intersecting point (marker position) and got the bounding box around it and used that one instead. Now it works regardless when where the intersecting point is.



PointShape closestPoint = existingMultiLineShape.GetClosestPointTo(new PointShape(intersectingPosition.Longitude, intersectingPosition.Latitude), GeographyUnit.DecimalDegree);
RectangleShape rectShape = GenerateMiniBoundingBox(closestPoint);
 

Thanks,


Nirish


 


 



Thank you for sharing that information. Can you tell us what you use this for in your real world scenario? If you don’t mind, we could create a Code Community project on that. Thank you.

Hi Val, 
  
 This is our scenario: We have a bunch of tickets that a driver has to attend to during his/her shift. We are using the TSP algorithm to calculate the optimised route through all those tickets. During the course of the day, more tickets might be added to the route. When a ticket is added, we calculate a new optimised route using the coordinates of the ticket the driver is currently heading to as the start position for the route and passing in the rest of the ticket coordinates as the stop points. Before drawing the newly generated route on the map, we first delete the line segments of the old route starting from the last point to the point we used as the start position. When we did that, we found that not all tickets are located exactly on the route line (as shown above). Some are within a big property, for example. Hence, the need to get the closest point from the ticket coordinates to the route line. 
  
 If it’s going to help other users, feel free to go ahead with the Code Community project. 
  
 Thanks, 
  
 Nirish

Hi guys,


Instead of looping through all the lines in the multilineshape:



MultilineShape existingMultiLineShape = (MultilineShape)lineLayer.InternalFeatures[lineKey].GetShape();
int index = existingMultiLineShape.Lines.Count - 1;
PointShape closestPoint = existingMultiLineShape.GetClosestPointTo(new PointShape(intersectingPosition.Longitude, intersectingPosition.Latitude), GeographyUnit.DecimalDegree);
RectangleShape rectShape = GenerateMiniBoundingBox(closestPoint);
while (index > 0 && !existingMultiLineShape.Lines[index].Crosses(rectShape))
{
existingMultiLineShape.Lines.RemoveAt(index);
index--;
};
existingMultiLineShape.Lines.RemoveAt(index);
Dictionary<string, string> columnValues = lineLayer.InternalFeatures[lineKey].ColumnValues;
lineLayer.InternalFeatures[lineKey] = new Feature(existingMultiLineShape, columnValues);
lineLayerOverlay.Refresh();
Map1.Refresh();

It might be easier to use the existing GetLineOnALine function in the multilineshape class:



MultilineShape existingMultiLineShape = (MultilineShape)lineLayer.InternalFeatures[lineKey].GetShape();
existingMultiLineShape = (MultilineShape)existingMultiLineShape.GetLineOnALine(StartingPoint.LastPoint, intersectingPosition.ToPointShape());
Dictionary<string, string> columnValues = lineLayer.InternalFeatures[lineKey].ColumnValues;
lineLayer.InternalFeatures[lineKey] = new Feature(existingMultiLineShape, columnValues);
lineLayerOverlay.Refresh();
Map1.Refresh();
 

Thanks,


Nirish



Nirish, 
  
 Thanks for sharing your experience. I think the scenario is very interesting, and we will make a code community project for sharing in a convenience time. 
  
 Thanks again. 
  
 Johnny