Convert AB-lines from Trimble to AgOpenGPS

Looking for a method to convert AB lines from Trimble to AgOpenGPS.

A company that works for us has an AB line in their system for all our plots. To use the same AB lines we want to convert this data from their Trimble system to AgOpenGPS.

Made an export from Trimble to IsoXML, which is easy to read and open in Google Earth.

In the IsoXML the AB lines are described by means of two GPS coordinates.

When I look in the AgOpenGPS files I find two relevant files: ABLines.txt and Field.kml.

ABLines.txt contains a value of an angle and no coordinates.
Field.xml contains two coordinates and no angle.

If I enter the two coordinates and no angle, because I don’t know it, AgOpenGPS changes those two coordinates to two completely different values ​​when I have opened and closed the program.

If I try to find the corresponding angle using “bearing angle” it doesn’t get the same value as AG does.

Does anyone know how I can best go about this?

Of couse you may look into the sourcecode. The class is most likely in CABcurve.cs.

But maybe try-and-error is easier: do you have the chance to generate datasets for the same AB-line in both system and share them with us?

You have to convert the lat longitude to easting, northing.
There are tools to convert this, maybe someone can post a link.
Or google: lat long to utm converter
Then subtract the offsets in the field.txt, and then calculate the angle (simple trigonometry) between the two pts.
In the ABlines,txt you enter:
name, heading, easting, northing

Of course you can code this to be all made in AOG.

Edit
You can build an excel sheet with the 2 pts easting, northing and the field offset to return the heading and start pt!

This is what I have. So far okay? How to proceed with the calculation?


ABLines.txt:
256.7° West 02:30:41,256.68536782,3.058,0.335

Field.kml
4.6631447,52.5127294,0 4.6344716,52.5085892,0

Field.txt
$Offsets
611892,5819115,31
Convergence
0.0228354918597221
StartFix
52.510662,4.6487695


Converting to Easting and Norting gives:

4.6631447,52.5127294 → Easting: 612862.68, Northing: 5819367.82, Zone: 31U
4.6344716,52.5085892 → Easting: 610927.47, Northing: 5818862.93, Zone: 31U

Subtract the offsets:
612862.68 (-611892), 5819367.82 (-5819115) → 970.68, 252.82
610927.47 (-611892), 5818862.93 (-5819115) → -964.53, -252.07

For the heading it would be:
tan^-1((easting.2ndPt - easting.1stPt)/(northing.2ndP - northing.1stPt)) = angle
if (northing.2ndP - northing.1stPt) < 0 add 180deg to angle
if (northing.2ndP - northing.1stPt) > 0 and (easting.2ndPt - easting.1stPt) < 0 add 360deg to angle

But all this will be useless I think:
When I pass the StartFix of some of my fields the easting/northing are always off up to 1 meter.
For yours it give me: 611892.5 and 5819115.55 so .5 and .24 off
So I think we have to pass trough the AOG code and use this internal calculation.

Shouldn’t you be able to import the same way we do the .ags file. A small bit of editing in the file reader and let AOG convert it.

Yeah
That’s the way to go. with an import button in the ABline box. Like the kml for boundary.

in Opengrade I wrote:

ConvertAgd2Utm(lat * 0.01745329251994329576923690766743, lon * 0.01745329251994329576923690766743)

based on

GeoUTMConverterXY(latitude * 0.01745329251994329576923690766743,
                                longitude * 0.01745329251994329576923690766743);

Probably similar in AOG

1 Like

Import function for AB lines would be very nice, just like fields with a kml file. But not a solution for the short term.

Still have the silent hope that someone has an Excel sheet that calculates the values.

In any case, thanks to everyone for their input!

AOG does the math when it imports boundaries and such. Post the ABFile that you need converted and I’ll see if we can modify the reader to get it converted. Or, Build a text file that has the lat and long of each end of the ABLine.

Number of Lines followed by LatP1, LongP1, LatP2, LongP2
4
xx.xxxxx, yy.yyyyy, xx.xxxxx, yy.yyyyy
xx.xxxxx, yy.yyyyy, xx.xxxxx, yy.yyyyy
xx.xxxxx, yy.yyyyy, xx.xxxxx, yy.yyyyy
xx.xxxxx, yy.yyyyy, xx.xxxxx, yy.yyyyy

ablines2.txt (717 Bytes)

Thanks in advance!

This is what I came up with. The angle appears to be off from the field when loaded in Google earth. This is individual lines for each field. Here is it in Google earth:

image

Here is the code for the reader. Someone may be able to tweak the angles or the calculations for the coordinates.

        public void FileLoadABLines2()
        {
            ABLine.moveDistance = 0;

            //make sure at least a global blank AB Line file exists
            string dirField = fieldsDirectory + currentFieldDirectory + "\\";
            string directoryName = Path.GetDirectoryName(dirField).ToString(CultureInfo.InvariantCulture);

            if ((directoryName.Length > 0) && (!Directory.Exists(directoryName)))
            { Directory.CreateDirectory(directoryName); }

            string filename = directoryName + "\\ablines2.txt";

            if (!File.Exists(filename))
            {
                using (StreamWriter writer = new StreamWriter(filename))
                {
                }
            }

            if (!File.Exists(filename))
            {
                TimedMessageBox(2000, gStr.gsFileError, gStr.gsMissingABLinesFile);
            }
            else
            {
                using (StreamReader reader = new StreamReader(filename))
                {
                    try
                    {
                        string line;
                        ABLine.numABLines = 0;
                        ABLine.numABLineSelected = 0;
                        ABLine.lineArr?.Clear();

                        //read all the lines
                        for (int i = 0; !reader.EndOfStream; i++)
                        {

                            double Lat1 = 0;
                            double Long1 = 0;
                            double Lat2 = 0;
                            double Long2 = 0;
                            

                            line = reader.ReadLine();
                            string[] words = line.Split(',');

                            if (words.Length != 5) break;

                            ABLine.lineArr.Add(new CABLines());

                            ABLine.lineArr[i].Name = words[0];
                            Lat1 = double.Parse(words[1], CultureInfo.InvariantCulture);
                            Long1 = double.Parse(words[2], CultureInfo.InvariantCulture);
                            Lat2 = double.Parse(words[3], CultureInfo.InvariantCulture);
                            Long2 = double.Parse(words[4], CultureInfo.InvariantCulture);
                            pn.DecDeg2UTM(Lat1, Long1);
                            double est1 = pn.DecDeg2UTM(Lat1, Long1)[0] - pn.utmEast;
                            double nrt1 = pn.DecDeg2UTM(Lat1, Long1)[1] - pn.utmNorth;
                            pn.DecDeg2UTM(Lat2, Long2);
                            double est2 = pn.DecDeg2UTM(Lat2, Long2)[0] - pn.utmEast;
                            double nrt2 = pn.DecDeg2UTM(Lat2, Long2)[1] - pn.utmNorth;
                            double abHead = Math.Atan2(est2 - est1,nrt2 -nrt1);
                            if (abHead < 0) abHead += glm.twoPI;

                            

                            double headingCalc = abHead + glm.PIBy2;

                            
                            ABLine.lineArr[i].heading = abHead;
                            ABLine.lineArr[i].origin.easting = est1;
                            ABLine.lineArr[i].origin.northing = nrt1;

                            ABLine.lineArr[i].ref1.easting = est1;
                            ABLine.lineArr[i].ref1.northing = nrt1;

                            ABLine.lineArr[i].ref2.easting = est2;
                            ABLine.lineArr[i].ref2.northing = nrt2;
                            ABLine.numABLines++;
                        }
                    }
                    catch (Exception er)
                    {
                        var form = new FormTimedMessage(2000, gStr.gsABLineFileIsCorrupt, "Please delete it!!!");
                        form.Show();
                        WriteErrorLog("FieldOpen, Loading ABLine, Corrupt ABLine File" + er);
                    }
                }
                FileSaveABLines();
            }

            if (ABLine.numABLines == 0) ABLine.numABLineSelected = 0;
            if (ABLine.numABLineSelected > ABLine.numABLines) ABLine.numABLineSelected = ABLine.numABLines;
        }

Here is the field folder, Just save the file and drop the .txt on the end of it. Remember that the field saves the offsets.

heaky test 2020.Oct.01 16_02.zip.txt (4.6 KB)

Edit: In reviewing the code above, I’m calling the DecDeg2Utm routine too many times. Should have built a vec2 for both points and only call it twice. Will that make a difference in the outcome? I don’t know, but I don’t know why it would. Also, failed to mention that I edited the ablines2.txt to eliminate the number of lines. The reader above simply reads the name, lat1,long1,lat2,long2. Five items per line separated by commas. The reader then splits the lines, and sends the lat and long into the position designer routine to get back the easting and northing. In AOG, ABLines are stored as an origin and heading. Using the dy/dx, get the heading, set the origin and ref1 to point1, set ref2 to point2. Upon saving the ab file and reloading it into the normal ab reader (next time you open the field) AOG takes care of the rest. This includes making the line longer. Probably a thousand better ways to do it, but??? Since these lines now have easting and northing values should be able to adjust them to the current field offsets. I set the simulator coordinates to the first coordinate of the ABLine file. That will effect the value of the easting and northing.

1 Like

I found the problem: Works well now.

image

public void FileLoadABLines2()
        {
            ABLine.moveDistance = 0;

            //make sure at least a global blank AB Line file exists
            string dirField = fieldsDirectory + currentFieldDirectory + "\\";
            string directoryName = Path.GetDirectoryName(dirField).ToString(CultureInfo.InvariantCulture);

            if ((directoryName.Length > 0) && (!Directory.Exists(directoryName)))
            { Directory.CreateDirectory(directoryName); }

            string filename = directoryName + "\\ablines2.txt";

            if (!File.Exists(filename))
            {
                using (StreamWriter writer = new StreamWriter(filename))
                {
                }
            }

            if (!File.Exists(filename))
            {
                TimedMessageBox(2000, gStr.gsFileError, gStr.gsMissingABLinesFile);
            }
            else
            {
                using (StreamReader reader = new StreamReader(filename))
                {
                    try
                    {
                        string line;
                        ABLine.numABLines = 0;
                        ABLine.numABLineSelected = 0;
                        ABLine.lineArr?.Clear();

                        //read all the lines
                        for (int i = 0; !reader.EndOfStream; i++)
                        {

                            double Lat1 = 0;
                            double Long1 = 0;
                            double Lat2 = 0;
                            double Long2 = 0;


                            line = reader.ReadLine();
                            string[] words = line.Split(',');

                            if (words.Length != 5) break;

                            ABLine.lineArr.Add(new CABLines());

                            ABLine.lineArr[i].Name = words[0];
                            Lat1 = double.Parse(words[1], CultureInfo.InvariantCulture);
                            Long1 = double.Parse(words[2], CultureInfo.InvariantCulture);
                            Lat2 = double.Parse(words[3], CultureInfo.InvariantCulture);
                            Long2 = double.Parse(words[4], CultureInfo.InvariantCulture);
                            double[] xy = pn.DecDeg2UTM(Lat1, Long1);
                            double est1 = xy[0] - pn.utmEast;
                            double nrt1 = xy[1] - pn.utmNorth;
                            xy = pn.DecDeg2UTM(Lat2, Long2);
                            double est2 = xy[0] - pn.utmEast;
                            double nrt2 = xy[1] - pn.utmNorth;
                            double east = est1;
                            double nort =nrt1;
                            est1 = (Math.Cos(-pn.convergenceAngle) * east) - (Math.Sin(-pn.convergenceAngle) * nort);
                            nrt1 = (Math.Sin(-pn.convergenceAngle) * east) + (Math.Cos(-pn.convergenceAngle) * nort);
                            east = est2;
                            nort = nrt2;
                            est2 = (Math.Cos(-pn.convergenceAngle) * east) - (Math.Sin(-pn.convergenceAngle) * nort);
                            nrt2 = (Math.Sin(-pn.convergenceAngle) * east) + (Math.Cos(-pn.convergenceAngle) * nort);

                            double abHead = Math.Atan2(est2 - est1, nrt2 - nrt1);


                            if (abHead < 0) abHead += glm.twoPI;



                            


                            ABLine.lineArr[i].heading = abHead;
                            ABLine.lineArr[i].origin.easting = est1;
                            ABLine.lineArr[i].origin.northing = nrt1;

                            ABLine.lineArr[i].ref1.easting = est1;
                            ABLine.lineArr[i].ref1.northing = nrt1;

                            ABLine.lineArr[i].ref2.easting = est2;
                            ABLine.lineArr[i].ref2.northing = nrt2;
                            ABLine.numABLines++;
                        }
                    }
                    catch (Exception er)
                    {
                        var form = new FormTimedMessage(2000, gStr.gsABLineFileIsCorrupt, "Please delete it!!!");
                        form.Show();
                        WriteErrorLog("FieldOpen, Loading ABLine, Corrupt ABLine File" + er);
                    }
                }
                FileSaveABLines();
            }

            if (ABLine.numABLines == 0) ABLine.numABLineSelected = 0;
            if (ABLine.numABLineSelected > ABLine.numABLines) ABLine.numABLineSelected = ABLine.numABLines;
        }

I was missing the convergence angle.

heaky test do over.zip.txt (4.6 KB)

2 Likes

I just saw that the field 0 easting/northing are rounded in AOG

@heaky if we calculate the angle convergence correction we should be able to manually calculate the pts

After substracting the offsets, correct the azimuth

//fix the azimuth error
                                        easting = (Math.Cos(-mf.pn.convergenceAngle) * east) - (Math.Sin(-mf.pn.convergenceAngle) * nort);
                                        northing = (Math.Sin(-mf.pn.convergenceAngle) * east) + (Math.Cos(-mf.pn.convergenceAngle) * nort);

pt1 970.781,252.433
pt2 -964.63, -251.686

tan^-1(-1935.411/-504.119) =75.40049
angle = 75.4004935887 +180 because deltaNorthing < 0
angle = 255.4004935887

so you can try
name, 255.40049359, 970.781, 252.433

Will see if it’s the same line with @KentStuff 's code. A lot of value to make an error!

Edit: I think all these calculations might be wrong, I think I messed up with degrees and radians!

Yes, the same lines were missing in the reader.

So all of this got me back wondering, can you import a path from GE and click the martian and drive it. Yes you can. Arbitrary field driven in AOG. Then loaded the field file and drew the white path. Back to AOG and import the path, and click the martian. This is the result.

image

2 Likes

Looking good @KentStuff ! Am very happy with it, can only test on location next week. Keep in touch! Thanks!

Hello does anyone know what the StartFix is?

  • Is it the point where the Field the first time was created from KML
  • Is it the starting point of the REC Funktion when recording the Field
  • Is this StartFix relevant for “Drive In” Funktion?

BR, Peter

The startFix is the position of the GPS (or pivot axle, I don’t know) when you create the field.
When field is created from .kml, I don’t know if it’s the tractor position or some other coordinate from the .kml.
Yes, as far as I know “Drive in” just gives the distance to the “startFix” of each field.