When I have a multipolygonfeature containing at least two overlapping polygon shapes, and I click on one of them, the following code throws an TopologyException, side location conflict:
Collection<feature> features = MyFeatureOverlay.TrackShapeLayer.FeatureSource.GetFeaturesNearestTo(new PointShape(e.WorldX, e.WorldY), map.MapUnit, 1, columnNames, distanceBuffer, DistanceUnit.Meter);</feature>
This function is supposed to return me the clicked on feature, and it works completely fine with overlapping multiline features.
What can I do about this error?
Side location conflict: TopologyException unhandled
Hi F5k,
The "side location conflict" always happens when there are some shapes are treated as "invalid" by NTS. So, I guess there are some invalid shapes in the TrackShapeLayer. I think you can call makeValid method before insert into the TrackShapeLayer. Some codes like the below.
Feature polygonFeature = new Feature(polygonShape);
if (polygonFeature.CanMakeValid)
{
polygonFeature = polygonFeature.MakeValid();
}
Here is a similar thread which might help: thinkgeo.com/forums/MapSuite/tabid/143/aft/11618/Default.aspx
Thanks,
Troy
Hi,
Thank you for the help, it solved the side location conflict, but causes some other problems in my code.
This is because when I use MakeValid() on a feature with multipolygonshape containing 2 overlapping polygons, the two polygons are somehow merged. The multipolygonshape now has a polygon count of 1 instead of 2. I’d like to keep separately drawn polygons belonging to the same multipolygon separately selectable, even if they geographically overlap.
Is there any way to achieve that?
Hi F5k,
Can I know you how to generate the MultiPolyonShape? In my eyes, the “invalid” multipolygon should be not easy to generate and less. Most of them are just a mistake, just like this thread shows thinkgeo.com/forums/MapSuit…fault.aspx
But if this is very common in your project, would you mind send us some of the polygons WKT for us?
Thanks,
Troy
It happens whenever I create a new
feature with a multipolygonshape based on overlapping polygons drawn by the
user.
My setup is as follows:
The user can press a button which
activates TrackMode.Polygon. Then, in the Track_Ended event, the polygon is
added to a feature as part of its multipolygonshape.
private
void MyDrawTrackInteractiveOverlay_TrackEnded(object sender, TrackEndedTrackInteractiveOverlayEventArgs
e) {
bool FeatureBeingReplaced = true;
Feature
oldFeature = MyFeatureOverlay.TrackShapeLayer.InternalFeatures[0];
PolygonShape
drawnPolygon = MyDrawTrackInteractiveOverlay.GetTrackingShape() as PolygonShape;
MultipolygonShape MultiPolygon
= oldFeature.GetShape() as MultipolygonShape;
if (MultiPolygon
== null) {
MultiPolygon
= new MultipolygonShape();
FeatureBeingReplaced = false;
}
MultiPolygon.Polygons.Add(drawnPolygon);
Feature newFeature = new Feature(MultiPolygon,
oldFeature.ColumnValues);
if (newFeature.CanMakeValid) {
newFeature =
newFeature.MakeValid();
}
if (FeatureBeingReplaced) {
MyFeatureOverlay.TrackShapeLayer.InternalFeatures.Remove(oldFeature);
}
MyFeatureOverlay.TrackShapeLayer.InternalFeatures.Add(newFeature);
}
An example would be that the user
draws one polygon with WKT before MakeValid()):
MULTIPOLYGON(((1435.36380823687
-52.9166380916825,1428.74922847541 257.96861069695,2076.97804509852
264.58319045841,2083.59262485998 -46.3020583302222,1759.47821654843
-231.510291651109,1435.36380823687 -52.9166380916825)))
This
feature doesn't change when MakeValid is used. So far, so good.
Then the
user draws another polygon that is partly on top of the first with WKT before
MakeValid()):
MULTIPOLYGON(((1435.36380823687 -52.9166380916825,1428.74922847541
257.96861069695,2076.97804509852 264.58319045841,2083.59262485998
-46.3020583302222,1759.47821654843 -231.510291651109,1435.36380823687
-52.9166380916825)),((1812.39485464011 105.833276183364,1799.16569511719
615.155917815804,2282.03001770379 621.770497577264,2282.03001770379
112.447855944824,1812.39485464011 105.833276183364)))
See the figure. I've numbered all the corners according to the WKT
order. This order is exactly how the shape was drawn, and I'd like to keep it
this way. However, if I keep the feature like this I get the TopologyException.
So I use
MakeValid as you suggested, but then the shape changes. It looks different; the overlapping part is no longer part of either
polygon. (Earlier I said that MakeValid() caused the polygons to merge, but
that's not actually the case.)
After MakeValid():
MULTIPOLYGON(((2080.27542698081
109.606241990982,2282.03001770379 112.447855944826,2282.03001770379
621.770497577266,1799.16569511719 615.155917815804,1808.34267965111
261.842013259967,2076.97804509852 264.583190458411,2080.27542698081
109.606241990982)),((1759.47821654843 -231.510291651105,2083.59262485998
-46.3020583302204,2080.27542698081 109.606241990982,1812.39485464011
105.833276183367,1808.34267965111 261.842013259967,1428.74922847542
257.968610696949,1435.36380823688 -52.9166380916792,1759.47821654843
-231.510291651105)))
This makes
things very complicated for me, because the feature's columnvalues contains
specific information for each corner (something I did not include in the code
shown above, to keep it simple), and now some of these corners no longer
exists, while other corners have appeared. Additionally, it's rather strange
from the user's perspective that they don't get the same shape they've drawn.
So my question is if there is any way by which
I can keep the overlapping features the way they are, without running into
TopologyExceptions.
If there
isn't, can I maybe prevent the user from drawing overlapping polygons? This
would also mean that the user shouldn't be allowed to drag control points from
an existing polygon over to another polygon.
Hi F5k,
Thanks for the details, I have recreated it easily with it. But back to your case, I am afraid such mulitipolygon whose polygons can overlap each other is still not allowed in GIS. We can use the "Union" method rather than like "MultiPolygon.Polygons.Add(drawnPolygon);" to avoid such multipolygon, but I guess this multi polygon is not like what you want.
So, I think the only way is to prevent the user to draw the overlap polygon. Here is the way to do that by defining a CustomTrackInteractiveTrackOverlay:
internal
class
CustomTrackInteractiveTrackOverlay : TrackInteractiveOverlay
{
protected
override
InteractiveResult MouseDownCore(InteractionArguments interactionArguments)
{
double
xInWorld = interactionArguments.WorldX;
double
yInWorld = interactionArguments.WorldY;
if
(
this
.TrackMode == TrackMode.Polygon)
{
this
.TrackShapeLayer.Open();
if
(
this
.TrackShapeLayer.QueryTools.GetFeaturesContaining(
new
PointShape(xInWorld, yInWorld), ReturningColumnsType.NoColumns).Count > 0)
{
MessageBox.Show(
"Not allow to overlap."
);
return
new
InteractiveResult();
}
}
return
base
.MouseDownCore(interactionArguments);
}
}
and use it in map with:
winformsMap1.TrackOverlay =
new
CustomTrackInteractiveTrackOverlay();
Hope it helps.
Thanks,
Troy
Thank you for the code, it works great!
Users are no longer able to draw overlapping polygons. However, I also need something to prevent users from editing already existing features in such a way that they come to overlap.
In my program the user can 'select' one(!) polygon from an earlier drawn multipolygon feature by clicking on it.
This means that when a polygon is clicked:
- The original feature is removed from my main TrackShapeLayer
- A temporary new feature is created (tempClickedPolygonFeature) that has a polygon shape that was clicked on. This tempClickedPolygonFeature is added to the EditTrackLayer and its control points are calculated
- Another temporary new feature that has a multipolygon shape containing the rest of the polygons (all but the one clicked on) is created (tempRestOfFeature). This feature is copied to a special layer (MyHighlightedOverlay.TrackShapeLayer.InternalFeatures) so that it shows up highlighted (to let the user know its related, but he/she won't be able to edit it)
When the user is done editting, tempClickedPolygonFeature and tempRestOfFeature will be merged, so that there's only one feature left, with a multipolygon shape containing the editted and non-editted polygons.
Now I need some way to make sure that when the user drags, rotates, resizes, moves/adds/deletes vertices of tempFeature, this feature does not come to overlap with tempRestOfFeature.
What would be the most robust way of doing this?
I've tried overriding the MouseUpCore events of my CustomEditInteractiveOverlay with code based on your sample.
protected
override
InteractiveResult MouseUpCore(InteractionArguments interactionArguments) {
if
(MapC.MyEditOverlay.EditShapesLayer.InternalFeatures.Count == 1 && MapC.MyEditOverlay.EditShapesLayer.InternalFeatures[0].GetShape()
as
PolygonShape !=
null
) {
double
xInWorld = interactionArguments.WorldX;
double
yInWorld = interactionArguments.WorldY;
MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.Open();
Collection<Feature> overlappingFeatures = MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.QueryTools.GetFeaturesOverlapping(MapC.MyEditOverlay.EditShapesLayer.InternalFeatures[0], ReturningColumnsType.NoColumns);
if
(overlappingFeatures.Count > 0 && ((overlappingFeatures[0].GetShape()
as
MultipolygonShape) !=
null
)) {
MessageBox.Show(
"Areas within the same collection are not allowed to overlap"
);
return
new
InteractiveResult();
}
}
return
base
.MouseDownCore(interactionArguments);
}
This stops me from being allowed to edit the feature – instead I always end up dragging the map around.
Hi F5k,
I think I find out a solution for the Edit case. But we need to make sure the edit feature is a multipolygon (I think you have done this in the trackend event). After that, in the edit mode, we can say the drag,rotate, resize won’t result the polygons overlap each other, but just take care of the vertex moving. So, we can do the validation by registering the VertexMoving event in EditOverlay.
The attach is my test codes and I also took a video show it. screencast.com/t/j1rNHEpIE
Thanks,
Troy
002_001_TrackAndEditShapes.zip (3.39 KB)
Hi Troy,
Thank you for the vertex moving code.
I’m still worried about the re-sizing/dragging/rotating bit though. In your video, you’re re-sizing/rotating/dragging the entire feature. As I tried to explain in my last code, my feature temporarily gets split up into two features: tempClickedPolygonFeature and tempRestOfFeature.
when the user re-sizes/drags/rotates tempClickedPolygonFeature, it may come to overlap one of the shapes in tempRestOfFeature, which lies in another layer. This causes problems afterwards, when they’re merged back into one feature.
Hi F5k,
I guess I might be misunderstand your case, I didn’t notice the two features are from two difference layer. If yes, I think it should be simple, we can register the
FeatureResizing, FeatureRotating and FeatureDragging event and in those events, we do the validation to judge if the modified tempClickedPolygonFeature is overlap the tempRestOfFeature. (this part work is similar with the codes I applied before in EditOverlay_VertexMoving event). If they are overlapped, then we cancel the operation by setting the event parameter as cancel.
Hope we are at the correct direction and if still have any question, don’t be hesitate to let me know.
Thank,
Troy
Hi Troy,
Thanks for
your continued efforts, and I think you understand the problem correctly now.
Looking at the code from
002_001_TrackAndEditShapes.zip that you gave me, I wonder why you override
MoveVertexCore and handle EditOverlay_VertexMoving?
Wouldn't it be enough with just one of these two?
Anyways, I've generalised the principle and
come up with the following override:
protected
override
Feature MoveVertexCore(Feature sourceFeature, PointShape sourceControlPoint, PointShape targetControlPoint) {
if
(MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.InternalFeatures.Count == 1 && MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.InternalFeatures[0].GetShape()
is
MultipolygonShape) {
Feature tempFeature =
base
.MoveVertexCore(sourceFeature, sourceControlPoint, targetControlPoint);
if
(tempFeature.GetShape().Intersects(MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.InternalFeatures[0].GetShape())) {
return
sourceFeature;
}
}
return
base
.MoveVertexCore(sourceFeature, sourceControlPoint, targetControlPoint);
}
I've then
used this same code in DragFeatureCore, RotateFeatureCore and
ResizeFeatureCore, by replacing base.MoveVertexCore by the appropriate
functions. It's not the prettiest solution, because when the user attempts to
make the features overlap, the feature immediately snaps back to its original
shape, rather than to the shape it had just before things started to overlap
(just like in your code). Is there any
way by which I can get a shape that's closer to what the user is trying the
draw, so for example the shape from just before it started to overlap?
I've also
had to adjust the code you posted on 05-13 a bit to make it work with my
situation and to make sure that it prevents features from overlapping, rather
than that it only prevents the user from adding an overlapping vertex.
protected
override
InteractiveResult MouseDownCore(InteractionArguments interactionArguments) {
if
(
this
.TrackMode == TrackMode.Polygon && MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.InternalFeatures.Count == 1) {
double
xInWorld = interactionArguments.WorldX;
double
yInWorld = interactionArguments.WorldY;
MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.Open();
this
.TrackShapeLayer.Open();
BaseShape trackingshape =
this
.GetTrackingShape();
Collection<feature> overlappingFeatures = MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.QueryTools.GetFeaturesContaining(
new
PointShape(xInWorld, yInWorld), ReturningColumnsType.NoColumns);
if
((trackingshape !=
null
&& MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.InternalFeatures[0].GetShape().Intersects(trackingshape)) || overlappingFeatures.Count > 0) { MessageBox.Show(
"Areas within the same collection are not allowed to overlap"
);
return
new
InteractiveResult();
}
//}
}
return
base
.MouseDownCore(interactionArguments);
}
However,
when the trackingshape only has only two vertexes outside of the
multipolygonfeature's shape forming a line that crosses the multipolygonfeature's
shape, the features do not return true for intersect.
So, for
example, the following two shapes supposedly do not intersect according to the
code.
trackingshape.GetWellKnownText() "POLYGON((-1746.24905702551
-2050.51972605268,-502.70806187098 -6442.6006876623,-1746.24905702551 -2050.51972605268,-1746.24905702551
-2050.51972605268))"
MapC.MyHighlightedFeatureOverlay.TrackShapeLayer.InternalFeatures[0].GetShape().GetWellKnownText() "MULTIPOLYGON(((-2351.6
-5450.2,-2839.2 -5906,-2968.9 -3766.8,1132.5 -2412.4,-2351.6 -5450.2)))"
However, no
matter where the next vertex is placed, the resulting feature will overlap, but
by then it's too late to correct for it.
So my
second question is, how can I check for
the above situation?
Hi F5k,
For the second question, seems the polygon with only two vertexs does have the issue like you mentioned can’t judge if overlap each other. But I find a workaround by convert the polygon to multilineshape, then the Intersects function works fine.
PolygonShape p1 = new PolygonShape(“POLYGON((-1746.24905702551 -2050.51972605268,-502.70806187098 -6442.6006876623,-1746.24905702551 -2050.51972605268,-1746.24905702551 -2050.51972605268))”);
MultilineShape m1 = p1.ToMultiLineShape();
As for the first question, I am trying to use the previous mouse position to replace the targetControlPoint if the shapes overlap each other, but encountered some issue, the below is my codes and I will continue to figure out the reason when I have enough time.
PointShape previousPosition;
protected
override
InteractiveResult MouseMoveCore(InteractionArguments interactionArguments)
{
InteractiveResult result =
base
.MouseMoveCore(interactionArguments);
previousPosition =
new
PointShape(interactionArguments.WorldX, interactionArguments.WorldY);
return
result;
}
protected
override
Feature MoveVertexCore(Feature sourceFeature, PointShape sourceControlPoint, PointShape targetControlPoint)
{
List<Feature> otherFeatures =
this
.EditShapesLayer.InternalFeatures.Where(f => f.Id != sourceFeature.Id).ToList();
foreach
(var item
in
otherFeatures)
{
var tempFeature =
base
.MoveVertexCore(sourceFeature, sourceControlPoint, targetControlPoint);
if
(item.GetShape().Intersects(tempFeature))
{
//MessageBox.Show(“Not allow to overlap.”);
//return sourceFeature;
if
(previousPosition!=
null
)
{
targetControlPoint = previousPosition;
}
else
{
return
sourceFeature;
}
}
}
return
base
.MoveVertexCore(sourceFeature, sourceControlPoint, targetControlPoint);
}
Thanks,
Troy
Converting the polygon to a multiline works! Thanks.
Keep me up to dated on how it’s going with the other problem :-)!
Hi,
We will update if we get any progress.
Regards,
Don