X

NV5 Geospatial Blog

Each month, NV5 Geospatial posts new blog content across a variety of categories. Browse our latest posts below to learn about important geospatial information or use the search bar to find a specific topic or author. Stay informed of the latest blog posts, events, and technologies by joining our email list!



Easily Share Workflows With the Analytics Repository

Easily Share Workflows With the Analytics Repository

10/27/2025

With the recent release of ENVI® 6.2 and the Analytics Repository, it’s now easier than ever to create and share image processing workflows across your organization. With that in mind, we wrote this blog to: Introduce the Analytics Repository Describe how you can use ENVI’s interactive workflows to... Read More >

Deploy, Share, Repeat: AI Meets the Analytics Repository

Deploy, Share, Repeat: AI Meets the Analytics Repository

10/13/2025

The upcoming release of ENVI® Deep Learning 4.0 makes it easier than ever to import, deploy, and share AI models, including industry-standard ONNX models, using the integrated Analytics Repository. Whether you're building deep learning models in PyTorch, TensorFlow, or using ENVI’s native model creation tools, ENVI... Read More >

Blazing a trail: SaraniaSat-led Team Shapes the Future of Space-Based Analytics

Blazing a trail: SaraniaSat-led Team Shapes the Future of Space-Based Analytics

10/13/2025

On July 24, 2025, a unique international partnership of SaraniaSat, NV5 Geospatial Software, BruhnBruhn Innovation (BBI), Netnod, and Hewlett Packard Enterprise (HPE) achieved something unprecedented: a true demonstration of cloud-native computing onboard the International Space Station (ISS) (Fig. 1). Figure 1. Hewlett... Read More >

NV5 at ESA’s Living Planet Symposium 2025

NV5 at ESA’s Living Planet Symposium 2025

9/16/2025

We recently presented three cutting-edge research posters at the ESA Living Planet Symposium 2025 in Vienna, showcasing how NV5 technology and the ENVI® Ecosystem support innovation across ocean monitoring, mineral exploration, and disaster management. Explore each topic below and access the full posters to learn... Read More >

Monitor, Measure & Mitigate: Integrated Solutions for Geohazard Risk

Monitor, Measure & Mitigate: Integrated Solutions for Geohazard Risk

9/8/2025

Geohazards such as slope instability, erosion, settlement, or seepage pose ongoing risks to critical infrastructure. Roads, railways, pipelines, and utility corridors are especially vulnerable to these natural and human-influenced processes, which can evolve silently until sudden failure occurs. Traditional ground surveys provide only periodic... Read More >

1345678910Last
«November 2025»
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456
16612 Rate this article:
3.3

Quickly Read and Display Data From a New File Format - Paleontology with IDL

Jim Pendleton

Over time, you may run across data sets that you'd like to read in IDL, file formats that lack any built-in utilities in the language.

Quite frequently I use the following technique for building custom data readers for IDL in my work for the Custom Solutions Group.

A recent article I ran across described a newly-minted repository of paleontological data, including 3D models of bones of long-lost species. MorphoSource.org, is a Duke University project funded in part by grants from the National Science Foundation. Commercial use of the provided media is strictly prohibited, according to their website.

My goal was to see how easy it would be to read some of this data and display it in an interactive IDL display. It was very simple, indeed!

 

Searching the database I found a number of interesting data types, including 3D models. One example, shown above and copyright the Florida Museum of Natural History, is the tooth of a Calippus elachistus, an early horse-like mammal that may have roamed the area in Florida during the Miocene epoch.

 

 

I requested a user account from MorphoSource.org and their robot sent me back the OK to grab some data, for instructional and non-commercial use only, of course.

Drilling down a little further I located some ASCII-format "Avizo PLY"-format files. I've not worked with this type of file before, and IDL doesn't have a built-in reader, but opening the file in a standard text editor shows that the format is straightforward.

Given this information, it's easy to build a utility in IDL that will both read the data and display the 3D object.

Though we won't attempt to parse all the data in the file, we can quickly locate the two major pieces of data we need for visualization, the list of vertex coordinates and the connectivity of those vertices.

The header begins with the following

ply
format ascii 1.0
obj_info Format: Avizo PLY
element vertex 249741
property float32 x
property float32 y
property float32 z
element face 495182
property list uint8 int32 vertex_indices

What this tells me is that somewhere in the file there are 249741 vertices with floating point (X,Y,Z) coordinates and 495182 "faces" or connectivity lists. A connectivity list tells us how the indices in the vertex list connect to one another.

(In other files in the MorphoSource data sets, you may also find data for vertex coloring, which adds greater interest!)

There are some additional lines in the header which we will ignore.  The header ends with an "end_header" line.

The lines immediately after the header are series of three floating point elements, for example

end_header
34.6223 25.3743 11.5595
34.5367 25.4449 11.5501
34.6183 25.4799 11.5998

Gosh, those look like vertex coordinates, don't they?  Now we can follow a hunch that if we look down the file another 249741 lines (the vertex count), we'll find data in a different format.  Sure enough, we find a transition.

23.9367 23.4851 43.5914
24.1271 23.4578 43.5783
24.0427 23.5348 43.5879
3 0 4 1 0
3 1 2 0 0
3 3 8 7 0
3 8 3 0 0

The connectivity list of each line starts with the number of vertices in a polygon's face.In this case they're all triangles, therefore the value is "3". Following the vertex count are the three zero-based indices of the vertices that make up each triangular facet.The last number in each line may indicate whether the polygon is open or closed. They all have a value of 0, so we'll just ignore the fifth value.

Not coincidentally, these vertex and connectivity lists are precisely in the format that IDL's IDLgrPolygon object uses.

After downloading the file to my desktop, I'll read the contents of the file into a string array variable named "val".

file = 'UF-53577_M5794-5222_Calippus_elachistus.ply'
n = file_lines(file)
val = strarr(n)
openr, lun, file, /get_lun
readf, lun, val
free_lun, lun

Locate the line that starts with the text "element vertex". We'll split that line on spaces and pull out the third element.  That's the vertex count.

vertexline = where(val.startswith('element vertex'))
vertexcount = long((val[vertexline[0]].split(' '))[2])
 

Repeat for the polygon connectivity count, which starts with "element face".

polygonline = where(val.startswith('element face'))
polygoncount = long((val[polygonline[0]].split(' '))[2])

Create IDL arrays in which to store the vertices and polygons.

vertices = fltarr(3, vertexcount)
poly = lonarr(4, polygoncount)

Remove the header from the string array so we're positioned at the first line of vertices.

last = (where(val.startswith('end_header')))[0]
val = val[last + 1: *]

Loop over the lines containing the vertex data, and extract the values into the vertices array.

for i = 0L,  vertexcount - 1 do $
  vertices[0, i] = float(((val[i]).trim()).split(' '))

Remove the vertices from the start of the string array and repeat the procedure for the connectivity.

val = val[vertexcount:*]
for i = 0L, polygoncount - 1 do $
  poly[0, i] = (long(((val[i]).trim()).split(' ')))[0:3]

For the display, we only need two lines of code.  Populate an IDLgrPolygon object with our data.

opolygon = idlgrpolygon(vertices, poly = poly, style = 2, color = !color.gold)

Finally, display the data in one of IDL's interactive tools, XOBJVIEW.

xobjview, opolygon

Did this horse have a cavity, or is the hole in the crown a spot that's been drilled for sampling?

Below is an example routine that incorporates the code shown above, and generates an MP4 animation in your temporary directory.  You will need to request a MorphoSource account to download your own PLY-format file as input. Feel free to generalize the code for a more robust PLY-format solution.

pro read_ply, dir, vertices
file = filepath('UF-53577_M5794-5222_Calippus_elachistus.ply')
n = file_lines(file)
val = strarr(n)
openr, lun, file, /get_lun
readf, lun, val
free_lun, lun
vertexline = where(val.startswith('element vertex'))
vertexcount = long((val[vertexline[0]].split(' '))[2])
polygonline = where(val.startswith('element face'))
polygoncount = long((val[polygonline[0]].split(' '))[2])
vertices = fltarr(3, vertexcount)
poly = lonarr(4, polygoncount)
last = (where(val.startswith('end_header')))[0]
val = val[last + 1: *]
for i = 0L,  vertexcount - 1 do $
vertices[0, i] = float(((val[i]).trim()).split(' '))
val = val[vertexcount:*]
for i = 0L, polygoncount - 1 do $
poly[0, i] = (long(((val[i]).trim()).split(' ')))[0:3]
;poly = reform(poly, poly.length, /overwrite)
om = idlgrmodel()
opolygon = idlgrpolygon(vertices, style = 2, poly = poly, color = !color.gold)
om->add, opolygon
mx = mean(vertices[0, *])
my = mean(vertices[1, *])
mz = mean(vertices[2, *])
om->translate, -mx, -my, -mz
xobjview, om, tlb = tlb, xsize = 256, ysize = 256
frames = list()
temppng = filepath(/tmp, 'frame.png')
for i = 0, 360, 3 do begin ; rotate
om->rotate, [1, 1, 0], 3
xobjview, refresh = tlb
xobjview_write_image, temppng, 'png'
frames.add, read_png(temppng)
file_delete, temppng
endfor
for i = 1, 100, 3 do begin ; zoom in
om->scale, 1.03, 1.03, 1.03
xobjview, refresh = tlb
xobjview_write_image, temppng, 'png'
frames.add, read_png(temppng)
file_delete, temppng
endfor
for i = 1, 100, 3 do begin ; zoom out
om->scale, 1/1.03, 1/1.03, 1/1.03
xobjview, refresh = tlb
xobjview_write_image, temppng, 'png'
frames.add, read_png(temppng)
file_delete, temppng
endfor
tempvid = filepath(/tmp, 'horsetooth.mp4')
ovid = IDLffVideoWrite(tempvid)
fs = (frames[0]).dim
vidstream = ovid.AddVideoStream(fs[1], fs[2], 24)
foreach frame, frames do $
t = ovid.put(vidstream, frame)
obj_destroy, ovid
spawn, tempvid
end
Please login or register to post comments.