Convert 3D points to 3D lines (underground well data)

10156
17
Jump to solution
05-25-2015 01:37 PM
Naga_RaghuveerModala
New Contributor II

I am trying to convert the measured 3D points data into 3D lines. The data is underground vertical well data. When I am converting from points to line the conversion process is failing to capture the Z values and the lines are being drawn on the surface horizontally. I can understand the issue but I am not sure how to solve it. Any suggestions would be helpful.

Thank you.

Tags (1)
1 Solution

Accepted Solutions
XanderBakker
Esri Esteemed Contributor

This is the code that you could use to convert the csv into a 3D polyline:

def main():
    import csv
    import arcpy

    # input csv
    csv_path = r"C:\Forum\3Dpnts_3Dlines\SampleData_well.csv"
    fld_x = 'bh_long'
    fld_y = 'bh_lat'
    fld_z = 'Z'
    fld_order = 'measdpth'
    fld_lineid = 'api_wellno'

    # settings
    sr = arcpy.SpatialReference(4326) # WGS_1984
    arcpy.env.overwriteOutput = True
    feetinmeter = 0.3048

    # output featureclass (could also point to fc in fgdb)
    fc = r"C:\Forum\3Dpnts_3Dlines\shp\test01.shp"

    lineids = []
    dct = {}
    # read csv
    with open(csv_path, 'rb') as f:
        reader = csv.reader(f)
        cnt = 0
        for row in reader:
            cnt += 1
            if cnt == 1:
                # header
                header = row
            else:
                # read fields from data
                lineid = row[header.index(fld_lineid)]
                x = float(row[header.index(fld_x)])
                y = float(row[header.index(fld_y)])
                z = float(row[header.index(fld_z)]) * feetinmeter

                # create point object with XYZ
                pnt = arcpy.Point(x, y, z)

                # read order and create string to sort on (lineid + order)
                order = row[header.index(fld_order)]
                srt =  "{0}_{1}".format("%04d" % (int(lineid),),
                      "{0}".format(round(float(order), 1)).zfill(8))

                # create nested dictionary with points per line
                if lineid in dct:
                    dct[lineid][srt] = pnt
                else:
                    dct[lineid] = {}
                    dct[lineid][srt] = pnt

    # create polylines
    lst_polylines = []
    for lineid, dct2 in dct.items():
        lst_pnt = []
        for srt, pnt in sorted(dct2.items()):
            lst_pnt.append(pnt)
        polyline = arcpy.Polyline(arcpy.Array(lst_pnt), sr, True, False)
        lst_polylines.append(polyline)

    # write polylines to output featureclass
    arcpy.CopyFeatures_management(lst_polylines, fc)

if __name__ == '__main__':
    main()

View solution in original post

17 Replies
XanderBakker
Esri Esteemed Contributor

If the standard tool is not working, you could always use some Python code to create the 3D lines. The thread below shows a snippet to convert a 2D polygon into a 3D polygon (and later into a PolylineZM). Re: M for a XYZ polyline

I guess the code can be adjusted to read a 3D point featureclass and create 3D lines.

If you are willing to share some sample data I can adjust the code.

Naga_RaghuveerModala
New Contributor II

Howdy Bakker,

Thank you for your direction. I will first try to develop the python code based on your link. If I am unable to solve the issue, I am willing to share a sample dataset with you. Is there any way I can send the dataset directly ?

Thank you once again.

Raghuveer

0 Kudos
XanderBakker
Esri Esteemed Contributor

If your data is classified, and you can't share it, then don't share it. No problem. It will help in order to determine if there is a field that identifies which points below to which line, the order of the points and if other attributes should be considered (also if the geometry is 3D or the Z is stored as attribute).

In case you need further assistance please specify those details and the version of ArcGIS you have installed.

If you run into problems while coding, you can post back what you have done so far and me and/or other users can provide help in fixing the code.

0 Kudos
Naga_RaghuveerModala
New Contributor II

The data is collected at various depths vertically and we have Z value attribute and other attributes as well.

0 Kudos
Naga_RaghuveerModala
New Contributor II

I am using ArcGIS 10.3

0 Kudos
XanderBakker
Esri Esteemed Contributor

The code below seems to be working...

3Dpolyline.png

def main():
    import arcpy
    import os

    arcpy.env.overwriteOutput = True

    fc_pnt = r"D:\Xander\GeoNet\3Dpoints_3Dlines\gdb\test.gdb\points3D_v02"
    fc_z = r"D:\Xander\GeoNet\3Dpoints_3Dlines\gdb\test.gdb\polylineZ"

    fld_lineid = "LineID"
    fld_order = "Elevation"

    # create a new fc Z enabled, but no M values
    fc_ws, fc_name = os.path.split(fc_z)
    arcpy.CreateFeatureclass_management(fc_ws, fc_name, "POLYLINE", fc_pnt, "DISABLED", "ENABLED", fc_pnt)

    # write the geometries to the new fc
    sr = arcpy.Describe(fc_pnt).spatialReference
    # flds_in = ("SHAPE@", fld_lineid, fld_order)
    fld_shp_in = arcpy.Describe(fc_pnt).shapeFieldName
    sort_flds = "{0} A; {1} A".format(fld_lineid, fld_order)
    flds_in = "{0}; {1}; {2}".format(fld_shp_in, fld_lineid, fld_order)
    flds_out = ("SHAPE@", fld_lineid)

    lineid = "NotSet"
    lst_poly_z = []
    with arcpy.da.InsertCursor(fc_z, flds_out) as curs_out:
        curs = arcpy.SearchCursor(fc_pnt, fields=flds_in, sort_fields=sort_flds)
        for row in curs:
            pnt = row.getValue(fld_shp_in)
            prev_lineid = lineid
            lineid = row.getValue(fld_lineid)
            if prev_lineid == lineid:
                # add point to line
                lst_poly_z.append(pnt.firstPoint)
            else:
                # write previous line and start new one
                if len(lst_poly_z) > 1:
                    polyline_z = arcpy.Polyline(arcpy.Array(lst_poly_z), sr, True, False)
                    row_out = [polyline_z, prev_lineid]
                    curs_out.insertRow(tuple(row_out))
                lst_poly_z = []
                lst_poly_z.append(pnt.firstPoint)

        # write last polyline
        if len(lst_poly_z) > 1:
            polyline_z = arcpy.Polyline(arcpy.Array(lst_poly_z), sr, True, False)
            row_out = [polyline_z, lineid]
            curs_out.insertRow(tuple(row_out))
        del curs, row

if __name__ == '__main__':
    main()

  • line 7 and 8 hold the paths to input 3D points and output 3D lines
  • line 10 holds the name of the input field that identifies the lines (for each unique value in this field points will be combined into a single line)
  • line 11 holds the name of the input field that provides the order of the points when creating the line (I used the same Z value)
Naga_RaghuveerModala
New Contributor II

Bakker,

Finally I got permission to share some sample dataset

0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi Naga Raghuveer Modala ,

I will have a look at it and come back to you as soon as possible. If I have any questions I will post them here.

BTW, my first name is Xander. You can call me Xander.

Kind regards, Xander

0 Kudos
XanderBakker
Esri Esteemed Contributor

Some assumptions and questions:

  • api_wellno, contains the id that distinguishes each line (in this case there are two lines)
  • bh_long, longitude in decimal degrees WGS 1984
  • bh_lat, latitude in decimal degrees WGS 1984
  • Z, Z value of point (although there is a measdpth too) in what unit is this? cms?
  • what is the field that indicates the order of the points to connect them to a line?
0 Kudos