Select to view content in your preferred language

Writing from/to Z values from fields

4971
12
Jump to solution
05-18-2014 04:01 PM
MikeLouwrens
Frequent Contributor
Hi all,

I have some Z-enabled lines that have no values currently set in the Z property, however I have FromLevel and ToLevel fields as attributes in the feature class.  I'd like to have a script that loops through all my lines and populates the Start Vertex with the value in the "FromLevel" field, and the End Vertex with the value in the "ToLevel" field.  Is this possible?  I started playing around with an UpdateCursor as I thought this would be the quickest/easiest way to get it done, but am unsure how to write to the Z property through the UpdateCursor.

I'm using ArcGIS Desktop 10.2.2

import arcpy, sys, string, os, fpformat  mxd = arcpy.mapping.MapDocument("CURRENT")  layers = arcpy.mapping.ListLayers(mxd)  for layer in layers:    if layer.name == "3dLine":     layerexists = 1          fields = ["SHAPE@Z","FromLevel","ToLevel"]      if layerexists == 1:       strLineFC = "3dLine"          lineRows = arcpy.da.UpdateCursor(strLineFC, fields)          lineRow = lineRows.next()          while lineRow:           linePointsArray = lineRow.Shape.getPart(0)           pt_count = linePointsArray.count           pt_begin = linePointsArray.next() # First Vertex on line            row[0] = row[1]           vertices = 2            while vertices < pt_count:             pt_end = linePointsArray.next()             vertices = vertices + 1                      pt_end = linePointsArray.next() # Last Vertex on line           row[0] = row[2]


This tells me there is no Shape property to get the vertex info from... (this script was butchered from another similar script, however that script writes to an attribute, not the Z property, and uses arcpy.updatecursor not arcpy.da.updatecursor.  Can I write to the Z property through the arcpy.updatecursor?

I'd appreciate any tips/suggestions to allow me to do this.

Cheers,
Mike.
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
RichardFairhurst
MVP Alum
Hi all,

I have some Z-enabled lines that have no values currently set in the Z property, however I have FromLevel and ToLevel fields as attributes in the feature class.  I'd like to have a script that loops through all my lines and populates the Start Vertex with the value in the "FromLevel" field, and the End Vertex with the value in the "ToLevel" field.  Is this possible?  I started playing around with an UpdateCursor as I thought this would be the quickest/easiest way to get it done, but am unsure how to write to the Z property through the UpdateCursor.

I'm using ArcGIS Desktop 10.2.2

import arcpy, sys, string, os, fpformat  mxd = arcpy.mapping.MapDocument("CURRENT")  layers = arcpy.mapping.ListLayers(mxd)  for layer in layers:    if layer.name == "3dLine":     layerexists = 1          fields = ["SHAPE@Z","FromLevel","ToLevel"]      if layerexists == 1:       strLineFC = "3dLine"          lineRows = arcpy.da.UpdateCursor(strLineFC, fields)          lineRow = lineRows.next()          while lineRow:           linePointsArray = lineRow.Shape.getPart(0)           pt_count = linePointsArray.count           pt_begin = linePointsArray.next() # First Vertex on line            row[0] = row[1]           vertices = 2            while vertices < pt_count:             pt_end = linePointsArray.next()             vertices = vertices + 1                      pt_end = linePointsArray.next() # Last Vertex on line           row[0] = row[2]


This tells me there is no Shape property to get the vertex info from... (this script was butchered from another similar script, however that script writes to an attribute, not the Z property, and uses arcpy.updatecursor not arcpy.da.updatecursor.  Can I write to the Z property through the arcpy.updatecursor?

I'd appreciate any tips/suggestions to allow me to do this.

Cheers,
Mike.


You cannot write to the coordinate properties of a polyline without rewriting the entire polyline geometry.  You have to convert the polyline to a point array.  You can modify the coordinate properties of the points in the array and write use that array to overwrite the polyline geometry.  Frankly there is no good help on how to do this and ESRI should provide a real example of how to modify a geometry other than a point geometry.

The code below is based on post number 6 of this post from Matthew Coyle and post number 4 of this post by Xander Bakker.  The code is untested and I am not sure if my method of attempting to find the first and last points is going to work.

import arcpy  infc = arcpy.GetParameterAsText(0) # the input line feature class  # Enter for loop for each feature # with arcpy.da.UpdateCursor(infc, ["SHAPE@", "FromLevel", "ToLevel"]) as update_cursor: for row in update_cursor:     array = arcpy.Array()     partNum = 0     firstpnt = row[0].firstPoint     lastpnt = row[0].lastPoint     pntnum = 0     for part in row[0]:         partArr = arcpy.Array()         for pnt in part:             if pnt:                 if partNum == 0 and firstpnt == pnt: # Modify Z value of the first point                    pnt.Z = row[1]                 if partNum == lastpart - 1 and lastpnt == pnt: # Z value of the last point                    pnt.Z = row[2]                 partArr.add(point)         array.add(partArr)         partNum += 1     new_line = arcpy.Polyline(array)     row[0] = new_line     update_cursor.updateRow(row)  del update_cursor

View solution in original post

0 Kudos
12 Replies
MikeLouwrens
Frequent Contributor
Apologies, a lot of the indentation has disappeared in the CODE block above.

Mike.
0 Kudos
RichardFairhurst
MVP Alum
Hi all,

I have some Z-enabled lines that have no values currently set in the Z property, however I have FromLevel and ToLevel fields as attributes in the feature class.  I'd like to have a script that loops through all my lines and populates the Start Vertex with the value in the "FromLevel" field, and the End Vertex with the value in the "ToLevel" field.  Is this possible?  I started playing around with an UpdateCursor as I thought this would be the quickest/easiest way to get it done, but am unsure how to write to the Z property through the UpdateCursor.

I'm using ArcGIS Desktop 10.2.2

import arcpy, sys, string, os, fpformat  mxd = arcpy.mapping.MapDocument("CURRENT")  layers = arcpy.mapping.ListLayers(mxd)  for layer in layers:    if layer.name == "3dLine":     layerexists = 1          fields = ["SHAPE@Z","FromLevel","ToLevel"]      if layerexists == 1:       strLineFC = "3dLine"          lineRows = arcpy.da.UpdateCursor(strLineFC, fields)          lineRow = lineRows.next()          while lineRow:           linePointsArray = lineRow.Shape.getPart(0)           pt_count = linePointsArray.count           pt_begin = linePointsArray.next() # First Vertex on line            row[0] = row[1]           vertices = 2            while vertices < pt_count:             pt_end = linePointsArray.next()             vertices = vertices + 1                      pt_end = linePointsArray.next() # Last Vertex on line           row[0] = row[2]


This tells me there is no Shape property to get the vertex info from... (this script was butchered from another similar script, however that script writes to an attribute, not the Z property, and uses arcpy.updatecursor not arcpy.da.updatecursor.  Can I write to the Z property through the arcpy.updatecursor?

I'd appreciate any tips/suggestions to allow me to do this.

Cheers,
Mike.


You cannot write to the coordinate properties of a polyline without rewriting the entire polyline geometry.  You have to convert the polyline to a point array.  You can modify the coordinate properties of the points in the array and write use that array to overwrite the polyline geometry.  Frankly there is no good help on how to do this and ESRI should provide a real example of how to modify a geometry other than a point geometry.

The code below is based on post number 6 of this post from Matthew Coyle and post number 4 of this post by Xander Bakker.  The code is untested and I am not sure if my method of attempting to find the first and last points is going to work.

import arcpy  infc = arcpy.GetParameterAsText(0) # the input line feature class  # Enter for loop for each feature # with arcpy.da.UpdateCursor(infc, ["SHAPE@", "FromLevel", "ToLevel"]) as update_cursor: for row in update_cursor:     array = arcpy.Array()     partNum = 0     firstpnt = row[0].firstPoint     lastpnt = row[0].lastPoint     pntnum = 0     for part in row[0]:         partArr = arcpy.Array()         for pnt in part:             if pnt:                 if partNum == 0 and firstpnt == pnt: # Modify Z value of the first point                    pnt.Z = row[1]                 if partNum == lastpart - 1 and lastpnt == pnt: # Z value of the last point                    pnt.Z = row[2]                 partArr.add(point)         array.add(partArr)         partNum += 1     new_line = arcpy.Polyline(array)     row[0] = new_line     update_cursor.updateRow(row)  del update_cursor
0 Kudos
RichardFairhurst
MVP Alum
This code will only work if your polylines have no true curves.  True curves would be destroyed by this code.
0 Kudos
TimBarnes
Frequent Contributor
You should also put in a test to check the assumption that the line only has 2 vertices. For lines with more than 2 vertices, the resultant 3D line with start and end Z values set will look very wonky since any intermediate vertices will have no Z value.
0 Kudos
MikeLouwrens
Frequent Contributor
You cannot write to the coordinate properties of a polyline without rewriting the entire polyline geometry.  You have to convert the polyline to a point array.  You can modify the coordinate properties of the points in the array and write use that array to overwrite the polyline geometry.  Frankly there is no good help on how to do this and ESRI should provide a real example of how to modify a geometry other than a point geometry.

The code below is based on post number 6 of this post from Matthew Coyle and post number 4 of this post by Xander Bakker.  The code is untested and I am not sure if my method of attempting to find the first and last points is going to work.

Hi Richard,
thanks for your reply.  I couldn't get your code to work, but you moved me in the right direction.  I rewrote my code based on yours and the two posts you linked, and got it to work.

import arcpy, fpformat
targetLayer = "MyLayer"
shapeFieldName = 'SHAPE'


cursor = arcpy.UpdateCursor(targetLayer)
for row in cursor:
  try:
    geom = row.getValue(shapeFieldName)
    fromLevel = fpformat.fix(row.FromLevel,2) # Round levels 
    toLevel = fpformat.fix(row.ToLevel,2) # Round levels 
    geomArr = arcpy.Array()
    for part in geom:
      partArr = arcpy.Array()
      pnt_count = part.count # Count Vertices
      pntNum = 0
      for pnt in part:
        if pnt:
          if pntNum == 0: # Start Point (first vertex)
            pntOut = arcpy.Point(pnt.X, pnt.Y, fromLevel)
            partArr.add(pntOut)
            pntNum = pntNum + 1
          elif pntNum == (pnt_count - 1): # End Point (last vertex)
            pntOut = arcpy.Point(pnt.X, pnt.Y, toLevel)
            partArr.add(pntOut)
            pntNum = pntNum + 1
          elif pntNum < (pnt_count - 1): # Any additional vertices
            pntOut = arcpy.Point(pnt.X, pnt.Y, pnt.Z)
            partArr.add(pntOut)
            pntNum = pntNum + 1


      geomArr.add(partArr)


    polyline = arcpy.Polyline(geomArr)
    row.setValue(shapeFieldName, polyline)
    cursor.updateRow(row)
  except:
    arcpy.AddMessage("Error " + str(row.objectid))

del row
del cursor


Many thanks,
Mike.
MikeLouwrens
Frequent Contributor
This code will only work if your polylines have no true curves.  True curves would be destroyed by this code.


Hopefully I don't 🙂  I don't know of any, but since most of my data comes from external sources there may be some.  I'll check though, thanks for the tip.

Cheers,
Mike.
0 Kudos
MikeLouwrens
Frequent Contributor
You should also put in a test to check the assumption that the line only has 2 vertices. For lines with more than 2 vertices, the resultant 3D line with start and end Z values set will look very wonky since any intermediate vertices will have no Z value.
Thanks Tim, I've put in a check for extra vertices and at this stage I'm only putting the Z onto the start/end, however if it becomes an issue I may need to interpolate the values in between.  That'll be my next problem I guess 😄

Cheers,
Mike.
0 Kudos
RichardFairhurst
MVP Alum
Hopefully I don't 🙂  I don't know of any, but since most of my data comes from external sources there may be some.  I'll check though, thanks for the tip.

Cheers,
Mike.


If the data comes as shapefiles there are no true curves.  True curves would only be created and preserved in a geodatabase.  But it is a potential gotcha if you ever do work with geodatabase true curves.
0 Kudos
RichardFairhurst
MVP Alum
Thanks Tim, I've put in a check for extra vertices and at this stage I'm only putting the Z onto the start/end, however if it becomes an issue I may need to interpolate the values in between.  That'll be my next problem I guess 😄

Cheers,
Mike.


Interpolating Z values should be done with caution.  The elevations at the end points of a line may have no relation to the elevations in the center if the feature is long enough and goes through an area with severe terrain changes.  if you have surface elevation data it is best to use 3D Analyst tools to assign Z values.  The code could still provide a way to traverse the Z assignments to identify unexpected terrain configurations affecting your lines, which may indicate misalignment between the terrain data and the lines.  You should at least consider using the 3D Analyst extension tool set if you are going to work with elevation very much, since it will probably make many tasks much easier.

Also if your field data is ever created from GPS points it is best to get each separate point with its XYZ coordiinates and build your lines from the raw GPS data rather than from fields associated with your line data and interpolation.
0 Kudos