[fdo-users] MPolygon <-> FDO

Gavin Cramer gavin.cramer at autodesk.com
Mon Mar 10 14:05:48 EDT 2008


I'm the man on vacation right now, too.  :-)  However, sometimes I just cannot resist checking email in case anyone is talking about geometry.

There are two ideals with respect to data integrity:  1) Path of least resistance; 2) Exchanging reliable data.

If these two, 1) yields the quickest results for the programmer and the user, while 2) yields data that can best be relied upon in later analysis.

With FDO and its various callers being where different data formats (with their different rules) meet, we do have to be prepared to cope with clashes between these ideals.  Most of the time, these differences are dealt with by the FDO Providers.  However, writing your own conversion code, this is also of your concern.  MPolygon, coming from AcDb, has a different origin than FDO's FGF format, which was done as closely as was practical to OGC's model of several years ago.  Thus, supporting datastores that ostensibly use the same model (Oracle, MySQL, WMS) encountered few, if any, of these issues.

Sending data to FDO actually has hardly any checks on geometry at all!  The FdoGeometry package is mostly just for datatyping and access, while FdoSpatial (which is not required to be used) has most of the real spatial logic.  You can get away with creating polygons with rings that self-intersect, intersect each other, make morally objectionable shapes, etc. -- they just cannot be open.  In fact, this is likely the only difference that you will encounter, other than the arc calculations that you have already done.

Testing rings for EXACT closedness in X and Y is one of the very few tests that would ever be applied when saving a geometry to an FDO datastore.  It remains there because it is cheap and is a good vanguard against further problems with rules that the targeted datastore may impose.  That is, it is protecting against much harder-to-detect problems later.  This check has been in place for four years now, with the only change being to not check the Z ordinate.

The MapGuide libraries don't actually check for closedness at all.  The Curve types all return false for IsClosed, while the Ring types all return true without checking.  There is some code overlap, and I hope to have the code refactored so that MapGuide geometry types just delegate to FdoGeometry where possible.  FdoGeometry has also had more performance tuning.  Since you already coded earlier with FDO, I recommend that you stick with that.

FDO and MapGuide are both open-source, so I encourage you to at least peruse the code, and perhaps even build the parts of them that you directly use.

Looking at your code, this block...

                                if (z == mPolygonLoop.Count - 1)
                                {
                                        pts[z] = pts[0];
                                }

...looks like it should already do as I recommended, which is to ensure that the last position of each ring matches the first.  However, maybe it is not doing what was intended -- i.e. you might have a plain old bug on your hands.  I do not have the correct environment set up to build your code, so I cannot tell for sure whether it is in your code or FDO.  I can say that I hit this same "open ring" issue during other development several times in the last year, and it was my new calling code that needed fixing every time.  :-)  Building FDO yourself and setting a debugger breakpoint in FdoFgfRing::FdoFgfRing is probably your most direct course of action.  You don't have to build the providers -- just the "FDO" component -- for this issue.

Best wishes,
Gavin



-----Original Message-----
From: fdo-users-bounces at lists.osgeo.org [mailto:fdo-users-bounces at lists.osgeo.org] On Behalf Of Jonio, Dennis (Aviation)
Sent: Monday, March 10, 2008 7:02 AM
To: FDO Users Mail List
Subject: RE: [fdo-users] MPolygon <-> FDO

Gavin,
Sunday night? ... you the man!!!

Obviously then, since my users and I are in no mood to move away from
Map3d, the issue: "MPolygon allows rings to be slightly open, while FDO
does not.", creates some real-world dissonance.

BTW, Saturday I coded up the same method using OSGeo.MapGuide, not one
of my thirteen(13) test cases produced a closed
MgCurveString(all produced valid mgcurvestrings). OSGeo.Geometry
produces nine(9) out of thirteen(13) rings!!! :-) Good thing I didn't
start with the MapGuide API's! It would seem the lesson I must learn
here is that MG's definition of "closed" is different from the other
two(2) and I have 3 definitions of "closedness".

Gee ... do ya think I've got a problem here?

Ok ... back to the real issue. Would you be kind enough to show me what
more I must do to make FDO curves work? I sincerely do not know what
else to do to make the start and end match to reach the FDO definition
of closedness.

I know it is redundant but I include the MapGuide code in case you are
interested. (and maybe I am doing it wrong)

public static MgGeometry MGGeometryInterfaceFromMPolygon(MPolygon mpoly)
{
        Document doc =
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActi
veDocument;
        Editor ed = doc.Editor;
        MgGeometry rtn_geometry = null;
        MgGeometryFactory mggf = new MgGeometryFactory();
        MgCurveRing mgExternalRing = null;
        MgCurveString mgcs_TestCase = null;
        MgArcSegment mgArcSeg;
        MgLinearSegment mgLinearSeg;
        MgCoordinateCollection mgCoorColl = new
MgCoordinateCollection();
        MgCurveSegmentCollection mgCurveSegColl = new
MgCurveSegmentCollection();
        MgCurveRingCollection mgCurveRingColl = new
MgCurveRingCollection();
        if (IsItACurvePolygon(mpoly) == true)
        {
                int loops = mpoly.NumMPolygonLoops;
                for (int i = 0; i < loops; i++)
                {
                        MPolygonLoop mPolygonLoop =
mpoly.GetMPolygonLoopAt(i);
                        Point2d[] pts = new Point2d[mPolygonLoop.Count];
                        double[] blgs = new double[mPolygonLoop.Count];
                        int z = 0;
                        for (; z < mPolygonLoop.Count; z++)
                        {
                                Point2d tmp_pt;
                                if (z == mPolygonLoop.Count - 1)
                                {
                                        pts[z] = pts[0];
                                }
                                else
                                {
                                        double X =
Math.Round(mPolygonLoop[z].Vertex.X, 10, MidpointRounding.AwayFromZero);
                                        double Y =
Math.Round(mPolygonLoop[z].Vertex.Y, 10, MidpointRounding.AwayFromZero);
                                        tmp_pt = new Point2d(X, Y);
                                        pts[z] = tmp_pt;
                                }
                                blgs[z] = mPolygonLoop[z].Bulge;
                        }

                        for (int j = 0; j < mPolygonLoop.Count - 1; j++)
                        {
                                mgArcSeg = null;
                                mgLinearSeg = null;
                                double theBulge = blgs[j];
                                if (theBulge != 0.0)
                                {
                                        CircularArc2d ca2d = new
CircularArc2d(pts[j], pts[j + 1], theBulge, false);
                                        MgCoordinate dpi_start =
mggf.CreateCoordinateXY(ca2d.StartPoint.X, ca2d.StartPoint.Y);
                                        MgCoordinate dpi_end =
mggf.CreateCoordinateXY(ca2d.EndPoint.X, ca2d.EndPoint.Y);
                                        Point2d midpoint =
ca2d.EvaluatePoint(0.5);
                                        double mp_X =
Math.Round(midpoint.X, 10, MidpointRounding.AwayFromZero);
                                        double mp_Y =
Math.Round(midpoint.Y, 10, MidpointRounding.AwayFromZero);
                                        midpoint = new Point2d(mp_X,
mp_Y);
                                        MgCoordinate dpi_center =
mggf.CreateCoordinateXY(midpoint.X, midpoint.Y);
                                        mgArcSeg =
mggf.CreateArcSegment(dpi_start, dpi_end, dpi_center);
                                }
                                else
                                {
                                        mgCoorColl.Clear();
                                        MgCoordinate seg_s =
mggf.CreateCoordinateXY(pts[j].X, pts[j].Y);
                                        MgCoordinate seg_e =
mggf.CreateCoordinateXY(pts[j + 1].X, pts[j + 1].Y);
                                        mgCoorColl.Add(seg_s);
                                        mgCoorColl.Add(seg_e);
                                        mgLinearSeg =
mggf.CreateLinearSegment(mgCoorColl);
                                }
                                if (mgLinearSeg != null)
                                        mgCurveSegColl.Add(mgLinearSeg);
                                else
                                        mgCurveSegColl.Add(mgArcSeg);
                        } // for vertex count

                        mgcs_TestCase =
mggf.CreateCurveString(mgCurveSegColl);
                        MgCurveRing ring = null;
                        if (mgcs_TestCase.IsClosed() == false)
                        {
                                ed.WriteMessage("\nNotClosed - Valid:{0}
Start:{1},{2} End:{3},{4}",
                                        mgcs_TestCase.IsValid(),
                                        mgcs_TestCase.StartCoordinate.X,
                                        mgcs_TestCase.StartCoordinate.Y,
                                        mgcs_TestCase.EndCoordinate.X,
                                        mgcs_TestCase.EndCoordinate.Y
                                        );
                                ring =
mggf.CreateCurveRing(mgCurveSegColl);
                        }
                        else
                        {
                                ring =
mggf.CreateCurveRing(mgCurveSegColl);
                        }
                        if (i == 0)
                        {
                                if (ring != null)
                                        mgExternalRing = ring;
                        }
                        else
                        {
                                if (ring != null)
                                        mgCurveRingColl.Add(ring);
                        }
                }// for each loop in the mpolygon
                if (mgcs_TestCase.IsClosed() == true)
                        rtn_geometry =
mggf.CreateCurvePolygon(mgExternalRing, mgCurveRingColl);
        }
        else
        {
                MgLinearRing mgExternalLinearRing = null;
                MgLinearRing mgLinearRingInner = null;

                MgLinearRingCollection mgLinearRingColl = new
MgLinearRingCollection();
                int loops = mpoly.NumMPolygonLoops;
                for (int i = 0; i < loops; i++)
                {
                        MPolygonLoop mPolygonLoop =
mpoly.GetMPolygonLoopAt(i);
                        mgCoorColl.Clear();
                        foreach (BulgeVertex bv in mPolygonLoop)
                        {
                                MgCoordinate coor_start =
mggf.CreateCoordinateXY(bv.Vertex.X, bv.Vertex.Y);
                                mgCoorColl.Add(coor_start);
                        }
                        if (i == 0)
                        {
                                mgExternalLinearRing =
mggf.CreateLinearRing(mgCoorColl);
                        }
                        else
                        {
                                mgLinearRingInner =
mggf.CreateLinearRing(mgCoorColl);
                                mgLinearRingColl.Add(mgLinearRingInner);
                        }
                }
                rtn_geometry = mggf.CreatePolygon(mgExternalLinearRing,
mgLinearRingColl);
        }
        return rtn_geometry;
}


dennis

-----Original Message-----
From: fdo-users-bounces at lists.osgeo.org
[mailto:fdo-users-bounces at lists.osgeo.org] On Behalf Of Gavin Cramer
Sent: Sunday, March 09, 2008 10:02 PM
To: FDO Users Mail List
Subject: RE: [fdo-users] MPolygon <-> FDO

Hi, Dennis.  MPolygon allows rings to be slightly open, while FDO does
not.  Your data contains rings whose starting and ending positions have
not been latched.  Closing any of these slightly open rings will be
required during conversion.  That is all that there needs to be to this
issue.

Your examples were useable to create CurveString instances.  However,
creating Ring values will require that the X and Y ordinates of the
start/end positions match.  There are lots of cases where datatype
conversion requires adaptations in conversion code.  In fact, your own
code, which computes the mid positions for arcs, is already such a case.

If you want to make a case for more flexibility should be allowed for
the reading of existing real-world data, you can propose it.  The
requirement for any Z ordinates to match at ring ends was removed in a
previous release.  While the FGF data format does redundantly store
starting and ending positions of rings, changing the matching
requirement could introduce defects in code that assumed the closedness
of rings, however.  Plus, some database formats may still require
correctly closed rings, even if FDO does not catch it.

Gavin

_______________________________________________
fdo-users mailing list
fdo-users at lists.osgeo.org
http://lists.osgeo.org/mailman/listinfo/fdo-users


More information about the fdo-users mailing list