ThinkGeo.com    |     Documentation    |     Premium Support

MultipolygonShape issues

I've created a multipolygon shape where some of the polygons overlap.  There are two issues.  The first is that the overlapping sections of the polygons do not paint (can see other layers behind them).  The second is that when I do a shape.Contains() call, I get the following exception:


GisSharpBlog.NetTopologySuite.Geometries.TopologyException occurred

  Message="side location conflict [ (-92.9437980441008, 44.9501328666182, NaN) ]"

  Source="NetTopologySuite"

  StackTrace:

       at GisSharpBlog.NetTopologySuite.GeometriesGraph.EdgeEndStar.PropagateSideLabels(Int32 geomIndex)

       at GisSharpBlog.NetTopologySuite.GeometriesGraph.EdgeEndStar.ComputeLabelling(GeometryGraph[] geom)

       at GisSharpBlog.NetTopologySuite.Operation.Relate.RelateComputer.LabelNodeEdges()

       at GisSharpBlog.NetTopologySuite.Operation.Relate.RelateComputer.ComputeIM()

       at GisSharpBlog.NetTopologySuite.Operation.Relate.RelateOp.get_IntersectionMatrix()

       at GisSharpBlog.NetTopologySuite.Operation.Relate.RelateOp.Relate(IGeometry a, IGeometry b)

       at GisSharpBlog.NetTopologySuite.Geometries.Geometry.Relate(IGeometry g)

       at GisSharpBlog.NetTopologySuite.Geometries.Geometry.Contains(IGeometry g)

       at ThinkGeo.MapSuite.Core.BaseShape.ContainsCore(BaseShape targetShape)

       at ThinkGeo.MapSuite.Core.BaseShape.Contains(BaseShape targetShape)

       at GTTCommon.MapSuiteControl.HitTest(Feature feature, Style style) in C:\CMS\GTTCommon\MapSuiteControl.cs:line 1972

 



I supply 4 polygons when creating the MultipolygonShape.  The vertices follow:



OuterRing X Y


Polygon 0:


V0 -92.9459 44.9509


V1 -92.9418 44.9510


V2 -92.9418 44.9515


V3 -92.9459 44.9515


V4 (repeat of V0, 1st 4 points provided for RingShape)


Polygon 1:


V0 -92.9414 44.9513


V1 -92.9414 44.9501


V2 -92.9422 44.9501


V3 -92.9422 44.9513


V4 (repeat of V0, 1st 4 points provided for RingShape)


Polygon 2:


V0 -92.9418 44.9504


V1 -92.9438 44.9504


V2 -92.9438 44.9499


V3 -92.9418 44.9499


V4 (repeat of V0, 1st 4 points provided for RingShape)


Polygon 3:


V0 -92.9437 44.9505


V1 -92.9426 44.9504


V2 -92.9427 44.9497


V3 -92.9439 44.9498


V4 (repeat of V0, 1st 4 points provided for RingShape)




Interesting exception! it seems GisSharpBlog.NetTopologySuite.Geometries has bug.

I can workaround the exception by calling Contains on the individual Polygons but can’t get by the lack of painting.  Could I get a copy of the fix when it’s ready?  Any estimates?

I’ll weigh in on this a little… we use NTS extensively for backend processing.   I can tell you that the version of NTS that is being used simply does not like invalid geometries (and you have said yours is invalid).   The NTS effort is an open source effort, and a version is being built that is more robust, with the performance of the less robust versions.    I cannot speak to when this version will be available though. 
  
 Can you consider making your geometry valid?   ie, union all of your individual polygons into a new geometry, so that you don’t have overlapping exterior rings?

That isn’t an option for me.  If I define a new shape containing multiple polygons, what would I have to override?  (Draw, Contains ???).

David & Ted 


Thanks for your post and sharing! I appreciate it very much.
 
I think there are 2 solutions for your requirements.
1) Use Polygons instead of MultiPolygons.
    I tried to add the polygons as following instead of adding one MultiPolygon, the overlapping intersections will be painted. And when you want to do Shape.Contains() call, just try to loop each Polygons(polygon1 ~ polygon4) to work around as you said.
 

//InMemoryFeatureLayer inMemoryLayer = new InMemoryFeatureLayer();
//inMemoryLayer.InternalFeatures.Add("Polygon1", new Feature(polygonShape1));
//inMemoryLayer.InternalFeatures.Add("Polygon2", new Feature(polygonShape2));
//inMemoryLayer.InternalFeatures.Add("Polygon3", new Feature(polygonShape3));
//inMemoryLayer.InternalFeatures.Add("Polygon4", new Feature(polygonShape4));


InMemoryFeatureLayer inMemoryLayer = new InMemoryFeatureLayer();
MultipolygonShape multiPolygonShape = new MultipolygonShape();
multiPolygonShape.Polygons.Add(polygonShape1);
multiPolygonShape.Polygons.Add(polygonShape2);
multiPolygonShape.Polygons.Add(polygonShape3);
multiPolygonShape.Polygons.Add(polygonShape4);
inMemoryLayer.InternalFeatures.Add(new Feature(multiPolygonShape));

 
2) Override your own InmemoryFeatureLayer and InmemoryFeatureSource to fix the problem of not painting when adding a MultiPolygonShape. Following is some codes you can take a reference.
 
For the problem of Contains, I think you still need to go around this by loop each PolygonShape.
 

// Create your own FeatureSource
public class CustomInmemoryFeatureSource : InMemoryFeatureSource
    {
        private CustomInmemoryFeatureSource()
            : this(new FeatureSourceColumn[] { }, new Feature[] { })
        { }

       
        public CustomInmemoryFeatureSource(IEnumerable<FeatureSourceColumn> featureSourceColumns)
            : this(featureSourceColumns, new Feature[] { })
        {
        }

        public CustomInmemoryFeatureSource(IEnumerable<FeatureSourceColumn> featureSourceColumns, IEnumerable<Feature> features)
            : base(featureSourceColumns,features)
        {
            
        }

        protected override Collection<Feature> GetFeaturesForDrawingCore(RectangleShape boundingBox, double screenWidth, double screenHeight, IEnumerable<string> returningColumnNames)
        {
            Collection<Feature> features = base.GetFeaturesForDrawingCore(boundingBox, screenWidth, screenHeight, returningColumnNames);
            Collection<Feature> returnFeatures = new Collection<Feature>();
            foreach (Feature feature in features)
            {
                BaseShape baseShape = feature.GetShape();
                if (baseShape is MultipolygonShape)
                {
                    MultipolygonShape multiPolygonShape = (MultipolygonShape)baseShape;

                    foreach (PolygonShape polygonShape in multiPolygonShape.Polygons)
                    {
                        Feature polygonFeature = new Feature(polygonShape, feature.ColumnValues);
                        returnFeatures.Add(polygonFeature);
                    }
                }
                else
                {
                    returnFeatures.Add(feature);
                }
            }

            return returnFeatures;

        }
    }


//Create your own FeatureLayer based on the new FeatureSource
public class CustomInmemoryFeatureLayer : InMemoryFeatureLayer
    {
       public CustomInmemoryFeatureLayer()
            : this(new FeatureSourceColumn[] { }, new Feature[] { })
        { }

        public CustomInmemoryFeatureLayer(IEnumerable<FeatureSourceColumn> featureSourceColumns, IEnumerable<Feature> features)
            : base()
        {
            FeatureSource = new CustomInmemoryFeatureSource(featureSourceColumns, features);
        }

        public CustomInmemoryFeatureLayer(IEnumerable<FeatureSourceColumn> featureSourceColumns, IEnumerable<BaseShape> shapes)
        {
            Collection<Feature> features = new Collection<Feature>();
            foreach (BaseShape shape in shapes)
            {
                features.Add(new Feature(shape));
            }

            FeatureSource = new CustomInmemoryFeatureSource(featureSourceColumns, features);
        }
    }

//Useage
CustomInmemoryFeatureLayer inMemoryLayer = new CustomInmemoryFeatureLayer();
            MultipolygonShape multiPolygonShape = new MultipolygonShape();
            multiPolygonShape.Polygons.Add(polygonShape1);
            multiPolygonShape.Polygons.Add(polygonShape2);
            multiPolygonShape.Polygons.Add(polygonShape3);
            multiPolygonShape.Polygons.Add(polygonShape4);
            inMemoryLayer.InternalFeatures.Add(new Feature(multiPolygonShape));

            inMemoryLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Country1;
            inMemoryLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle.OuterPen.Color = GeoColor.SimpleColors.Red;
            inMemoryLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

 
Any more questions just let me know.
Thanks.
 
Yale

David, I had to catach a plane and could not follow up.   I think Yale is right about the contains stuff.   Within our own application, I have abandoned using the NTS built-in geometry operations directly.   So, I never use code like "Geometry.Contains(point)".   Instead, we have written a geometry operations factory with a method Contains(IGeometry geometry, IPoint point).   Within that factory, if we test for a multi-geometry, and if found, recurse the parts, breaking on the first True result.   Same on buffering and other operations.   The MultiGeometry object is problematic.    So maybe you will have to do the same thing but operate on ThinkGeo shapes? 
  
 In a custom feture layer, another option (rather than creating a feature for each part) is to clean the topology on any multi-part features as discussed above… create a new feature by unioning all of the parts of the original feature.  This would preserve the same number of features in the feature collection as your original data source, and if using search tools, etc, you would find the complete feature, instead of potentially just finding individual parts.

Thanks for the input.  For my app I need to keep a rectangular shape and only one set of sides can be widened.  Creating other shapes would be problematic for me since I need to keep track of the original vertices (and don't want to keep them in ColumnValues somewhere).  Geometry always did make my mind blister.


Not sure which way I'm heading but at least I have a few more options ( a new class (shape as I mentioned or FeatureSource/Layer as Yale showed, or just adding the polygons one at a time).  Just a bit worried about what else I'm going to need to handle after the paint and Contains.



Yale, 
  
 Your suggestion worked for normal layers.  My problem now is that when the shape is moved to the EditShapesLayer the paint problem again shows up.  I’m using a custiom EditInteractiveOverlay and I tried creating a new EditShapesLayer property and setting that property to a CustomInMemoryFeatureLayer.  It doesn’t seem to work.  I can’t change the property directly since it is read-only.  Any thoughts?

David,


 I am sorry I did not notice you are using the Feature for editing.
 
Yes, I think you are right! Currently, the EditShapesLayer in EditOverlay is READ-ONLY, we will consider to make it writable in later version.  So you cannot accomplish by setting your created CustomInMemory layer to it now.But still we provide another way from scratch to go for this if you are really interested to
Achieve this.
 
Following is the code you can take a reference from:
 

public class MyCustomEditOverlay : EditInteractiveOverlay
    {
        public MyCustomEditOverlay()
            : base()
        { }

        protected override void DrawCore(GeoCanvas canvas)
        {
            // Draw 6 inmemoryLayers.
            Collection<SimpleCandidate> labelsInAllLayers = new Collection<SimpleCandidate>();
            EditShapesLayer.Open();
            Collection<Feature> backedUpFeatures = EditShapesLayer.QueryTools.GetAllFeatures(ReturningColumnsType.AllColumns);
            EditShapesLayer.InternalFeatures.Clear();
            foreach (Feature feature in backedUpFeatures)
            {
                BaseShape baseShape = feature.GetShape();
                if (baseShape is MultipolygonShape)
                {
                    MultipolygonShape multiPolygonShape = (MultipolygonShape)baseShape;

                    foreach (PolygonShape polygonShape in multiPolygonShape.Polygons)
                    {
                        Feature polygonFeature = new Feature(polygonShape, feature.ColumnValues);
                        EditShapesLayer.InternalFeatures.Add(polygonFeature);
                    }
                }
                else
                {
                    EditShapesLayer.InternalFeatures.Add(feature);
                }
            }
            EditShapesLayer.Draw(canvas, labelsInAllLayers);
            canvas.Flush();

            EditShapesLayer.InternalFeatures.Clear();
            foreach (Feature feature in backedUpFeatures)
            {
                EditShapesLayer.InternalFeatures.Add(feature);
            }

            //EditShapesLayer.Close();

            ExistingControlPointsLayer.Open();
            ExistingControlPointsLayer.Draw(canvas, labelsInAllLayers);
            canvas.Flush();
            //ExistingControlPointsLayer.Close();

            ResizeControlPointsLayer.Open();
            ResizeControlPointsLayer.Draw(canvas, labelsInAllLayers);
            canvas.Flush();
            //ResizingControlPointsLayer.Close();

            RotateControlPointsLayer.Open();
            RotateControlPointsLayer.Draw(canvas, labelsInAllLayers);
            canvas.Flush();
            //RotatingControlPointsLayer.Close();

            DragControlPointsLayer.Open();
            DragControlPointsLayer.Draw(canvas, labelsInAllLayers);
            canvas.Flush();
        }
    } 


// Useage
  winformsMap1.EditOverlay = new MyCustomEditOverlay();
  winformsMap1.EditOverlay.EditShapesLayer.InternalFeatures.Add(new Feature(multiPolygonShape));
winformsMap1.EditOverlay.CalculateAllControlPoints();

 

Any more problems just let me know. Sorry for the inconvenience now.
 
Thanks.
 
Yale


I placed the code in my custom edit overlay and now my polygons don't paint at all.  The control points paint.  I have set the DefaultAreaStyle.  Would that cause this?  Should we be checking IsDirty or is that done before calling DrawCore?



         ApproachInteractiveEditOverlay customEditOverlay = new ApproachInteractiveEditOverlay();
         customEditOverlay.ResizeMode = ResizeMode.OnePointFixed;
         customEditOverlay.Projection = m_projection;
         customEditOverlay.Map = m_map;
         m_map.EditOverlay = customEditOverlay;
         m_map.EditOverlay.CanAddVertex = false;
         m_map.EditOverlay.CanDrag = false;
         m_map.EditOverlay.CanRotate = false;
         m_map.EditOverlay.CanResize = false;
         m_map.EditOverlay.ResizeControlPointsLayer.IsVisible = false;
         m_map.EditOverlay.RotateControlPointsLayer.IsVisible = false;
         m_map.EditOverlay.EditShapesLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle =AreaStyles.CreateHatchStyle(
            GeoHatchStyle.LightDownwardDiagonal,
            GeoColor.FromArgb(200, GeoColor.StandardColors.Magenta),
            GeoColor.FromArgb(150, GeoColor.StandardColors.Magenta),
            GeoColor.FromArgb(200, GeoColor.StandardColors.DarkBlue),
            1, DashStyleToLineDashStyle(DashStyle.Dash), 0, 0);
         m_channelDLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

         m_map.EditOverlay.VertexAdding += new EventHandler<VertexAddingEditInteractiveOverlayEventArgs>(EditOverlay_VertexAdding);

 

 


Thanks for your help!



David, 
  
 I am sorry I cannot recreate your problem.  I guess there are a couple of reasons probably cause the problem: 
  
 1) I did not see you add the Multipolygon Feature. Are you sure you added this multiPolygonFeature to the EditShapesLayer in the EditOverlay? 
  
 2) Can you try to remove the projection? Comment out the set of the Projection for the Overlay? And try to the MultiPolygon shape posted at the beginning of the post. 
  
 3) Can you try not to use another ResizeMode? 
  
 If you still cannot paint for the feature, I can upload my demo for you. Or you can upload your demo for us. 
  
  
 Any more problems just let me know. 
  
 Thanks. 
  
 Yale 


Sorry for the late response.   In my attempt to modify my custom edit overlay (the read-only EditOverlay issue), I created a new EditShapesLayer property.  The draw method evidently doesn’t use this property and to old layer got drawn instead (I assume).  Working fine now after I backed that out. 
  
 Thanks for your help.

David, 
  
 That is fine! I am glad you fixed this problem! 
  
 Any more questions just let me know. 
  
 Thanks. 
  
 Yale