One or more points shared the same XY location (even with different elevations).

One or more points shared the same XY location (even with different elevations).

shershahbacha9
Advocate Advocate
2,122 Views
7 Replies
Message 1 of 8

One or more points shared the same XY location (even with different elevations).

shershahbacha9
Advocate
Advocate

Hello !! i need to make topography from text file. Manually its making directly from it when i feed the text file having points. i am sharing the format of text file below. But when i try to do it using Revit API, its givin error "One or more points shared the same XY location (even with different elevations). This is not permitted for topography surfaces." . I am also sharing my code. I tried working in units and rounding but still gave the same error.

so here is my code piece:

 

List<string> lines = File.ReadAllLines(@"D:\SRTM.txt").ToList();

double cF = 1 / 0.3048; // conversion factor between meters and feets
int abc = lines.Count;
List<XYZ> points = new List<XYZ>();
for (int i = 0; i < abc; i++)
{
double[] values1 = lines.ElementAt(i).Split(',').Select(r => Convert.ToDouble(r)).ToArray();
XYZ pi = new XYZ(values1[0]*cF, values1[1]*cF, values1[2]*cF);
points.Add(pi);

}

Autodesk.Revit.DB.Architecture.TopographySurface.Create(doc, points);

2,123 Views
7 Replies
Replies (7)
Message 2 of 8

RPTHOMAS108
Mentor
Mentor

A topo can't have a point directly above a point therefore you need to filter out coincident XY values regardless of Z, this can be done with a equality comparer as below. Using Excel you'll find the following duplicate x,y points in your input file. Once these are eliminated and truncated as mentioned below then you can probably insert the file in the non-api way.

 

Capture.PNG

 

The coordinates need to be truncated as they are too large for what usually suits Revit. There is section below for deducting minimums from the coords. You then have to use shared coords to represent real world values (Revit relative coords to the real world).

 

I like to use text field parser from the VisualBasic.FileIO namespace as it is good with dealing with the following aspects:

Comment tokens, white spaces, fields enclosed in quotes i.e. "ObjA, ObjB","100" is treated as two fields and the quotes are automatically removed if they exist.

Also gives line numbers for dealing with errors.

 

 private class RT_PT : IEquatable<RT_PT>
        {
            public double X { get; set; }
            public double Y { get; set; }
            public double Z { get; set; }
            public bool Equals(RT_PT other)
            {
                if (this.X != other.X)
                    return false;
                if (this.Y != other.Y)
                    return false;
                //We don't compare Z in this case as a topo can't have a point directly above a point
                return true;
            }
            public XYZ AsXYZ()
            {
                return new XYZ(X / 0.3048, Y / 0.3048, Z / 0.3048);
            }
        }

        public Result TObj41(Autodesk.Revit.UI.ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
        {
            if (commandData.Application.ActiveUIDocument == null)
                return Result.Cancelled;
            Document D = commandData.Application.ActiveUIDocument.Document;
            Microsoft.Win32.OpenFileDialog OFD = new Microsoft.Win32.OpenFileDialog
            {
                Filter = "Text files (*.txt)|*.txt",
                Multiselect = false,
                DefaultExt = ".txt"
            };
            OFD.ShowDialog();

            if (string.IsNullOrEmpty(OFD.FileName))
                return Result.Cancelled;

            List<RT_PT> PointsCollected = new List<RT_PT>();

            using (Microsoft.VisualBasic.FileIO.TextFieldParser TFP = new Microsoft.VisualBasic.FileIO.TextFieldParser(OFD.FileName))
            {
                TFP.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
                TFP.Delimiters = new string[] { "," };
                TFP.TrimWhiteSpace = true;

                while (TFP.EndOfData == false)
                {
                    string[] Fds = TFP.ReadFields();
                    if (Fds.Length < 3)
                        continue;

                    double xx;
                    double yy;
                    double zz;

                    RT_PT NP = new RT_PT();
                    if (double.TryParse(Fds[0], out xx) == false)
                        continue;
                    if (double.TryParse(Fds[1], out yy) == false)
                        continue;
                    if (double.TryParse(Fds[2], out zz) == false)
                        continue;

                    NP.X = xx;
                    NP.Y = yy;
                    NP.Z = zz;

                    if (PointsCollected.Count > 0)
                    {
                        if (PointsCollected.Contains(NP) == false)
                        {
                            PointsCollected.Add(NP);
                        }
                    }
                    else
                    {
                        PointsCollected.Add(NP);
                    }
                }

                TFP.Close();
            }

            if (PointsCollected.Count == 0)
                return Result.Cancelled;

            //Truncate the large coords or expect a white screen with a tiny hard to find black dot
            //Use shared coord system to represent large real world values (the mins below represent offsets to the real world values)
            double MinX = PointsCollected.Min(j => j.X);
            double MinY = PointsCollected.Min(j => j.Y);
            double MinZ = PointsCollected.Min(j => j.Z);
            Int32 i;
            for (i = 0; i <= PointsCollected.Count - 1; i++)
            {
                PointsCollected[i].X -= MinX;
                PointsCollected[i].Y -= MinY;
                PointsCollected[i].Z -= MinZ;
            }

            List<XYZ> XYZLst = new List<XYZ>();
            for (i = 0; i <= PointsCollected.Count - 1; i++)
            {
                XYZLst.Add(PointsCollected[i].AsXYZ());
            }
            if (XYZLst.Count == 0)
                return Result.Failed;

            using (Transaction Tx = new Transaction(D, "Topo please"))
            {
                if (Tx.Start() == TransactionStatus.Started)
                {
                    Autodesk.Revit.DB.Architecture.TopographySurface.Create(D, XYZLst);
                    Tx.Commit();
                }
            }

            return Result.Succeeded;

        }

C#

Private Class RT_PT
        Implements IEquatable(Of RT_PT)

        Public Property X As Double
        Public Property Y As Double
        Public Property Z As Double

        Public Overloads Function Equals(other As RT_PT) As Boolean Implements IEquatable(Of RT_PT).Equals
            If Me.X <> other.X Then Return False Else 
            If Me.Y <> other.Y Then Return False Else 
            'We don't compare Z in this case as a topo can't have a point directly above a point
            Return True
        End Function

        Public Function AsXYZ() As XYZ
            Return New XYZ(X / 0.3048, Y / 0.3048, Z / 0.3048)
        End Function
    End Class

    Public Function TObj41(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData, _
                ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result

        If commandData.Application.ActiveUIDocument Is Nothing Then Return Result.Cancelled Else 
        Dim D As Document = commandData.Application.ActiveUIDocument.Document

        Dim OFD As New Microsoft.Win32.OpenFileDialog With {.Filter = "Text files (*.txt)|*.txt", .Multiselect = False, .DefaultExt = ".txt"}
        OFD.ShowDialog()

        If String.IsNullOrEmpty(OFD.FileName) Then Return Result.Cancelled Else 

        Dim PointsCollected As New List(Of RT_PT)

        Using TFP As New Microsoft.VisualBasic.FileIO.TextFieldParser(OFD.FileName)
            TFP.TextFieldType = FileIO.FieldType.Delimited
            TFP.Delimiters = New String() {","}
            TFP.TrimWhiteSpace = True

            While TFP.EndOfData = False
                Dim Fds() As String = TFP.ReadFields
                If Fds.Length < 3 Then Continue While Else 

                Dim NP As New RT_PT
                If Double.TryParse(Fds(0), NP.X) = False Then Continue While Else 
                If Double.TryParse(Fds(1), NP.Y) = False Then Continue While Else 
                If Double.TryParse(Fds(2), NP.Z) = False Then Continue While Else 

                If PointsCollected.Count > 0 Then
                    If PointsCollected.Contains(NP) = False Then
                        PointsCollected.Add(NP)
                    End If
                Else
                    PointsCollected.Add(NP)
                End If
            End While

            TFP.Close()
        End Using

        If PointsCollected.Count = 0 Then Return Result.Cancelled Else 

        'Truncate the large coords or expect a white screen with a tiny hard to find black dot
        'Use shared coord system to represent large real world values (the mins below represent offsets to the real world values)
        Dim MinX As Double = PointsCollected.Min(Function(j) j.X)
        Dim MinY As Double = PointsCollected.Min(Function(j) j.Y)
        Dim MinZ As Double = PointsCollected.Min(Function(j) j.Z)
        For i = 0 To PointsCollected.Count - 1
            PointsCollected(i).X -= MinX
            PointsCollected(i).Y -= MinY
            PointsCollected(i).Z -= MinZ
        Next

        Dim XYZLst As New List(Of XYZ)
        For i = 0 To PointsCollected.Count - 1
            XYZLst.Add(PointsCollected(i).AsXYZ)
        Next
        If XYZLst.Count = 0 Then Return Result.Failed Else 

        Using Tx As New Transaction(D, "Topo please")
            If Tx.Start = TransactionStatus.Started Then
                Autodesk.Revit.DB.Architecture.TopographySurface.Create(D, XYZLst)
                Tx.Commit()
            End If
        End Using

        Return Result.Succeeded

    End Function

VB

 

 

Message 3 of 8

shershahbacha9
Advocate
Advocate

Thank you so very much, i will go through your code now. But i have one question. The text file when i feed it manually in Revit creates topography surface and do not give error of presence of same points. The same file when i feed through API in programming give this error of same X,Y

0 Likes
Message 4 of 8

shershahbacha9
Advocate
Advocate

Thanks alot for providing this piece of code as i have no programming background and im beginner

0 Likes
Message 5 of 8

shershahbacha9
Advocate
Advocate

truncate will not work as i am writing this code for creating a topography surface of a house site and the points are too close which are differenciated by decimals most of the time.

0 Likes
Message 6 of 8

jeremytammik
Autodesk
Autodesk

You need to learn how to set up your Revit project properly in the user interface before you start programming.

 

Using a suitable transformation between global real world coordinates and local projects coordinates will enable you to convert to more manageable values.

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 7 of 8

shershahbacha9
Advocate
Advocate

jeremmy !! you are right, as i am new to programming. I created small plugins for creating walls, floors , rooms and room tags but topography is my 1st experience. what i dont understand is the same text file when i feed to Revit manually it creates topography surface but when i feed those values to Revit using API, then it gives problems

0 Likes
Message 8 of 8

RPTHOMAS108
Mentor
Mentor

You can't have a topo with two points having the same X,Y but differing values of Z. I suspect that if the UI is creating the topo from such points then it has an extra layer of code to filter out the points or is averaging them somehow. The topo surface is a smoothed surface so even abrupt changes of level would be smoothed out and therefore not represent the exact Z values as input. For abrupt changes such as basements and retaining walls you are supposed to make cuts with building pads (not try to model them with the topo points).

 

Topo co-ords need to be truncated or you get this even in the UI. It is telling you that your input has been truncated.

 

Capture1.PNG

 

The truncation example I gave was for demonstration and in the end it is expected that you will have a site location set up already in Revit. From this site location you will create a transform so that you can convert your input into a coordinate system that is closer to the internal origin.

 

In my example of the Equality function it is probably a better idea to compare distances between points on the XY plane rather than just comparing the X,Y values of the point (as I have done). You can then introduce a tolerance distance so that if two points are within the same distance one is eliminated.

0 Likes