how to properly use vtkCellDataToPointData - vtk Python library - python

Im trying to utilize the vtk Python library to write out some unstructured grids to legacy .vtk format
I am able to create the geometry, cell data and point data no problem and write them out to vtk.
The problem I have is I want to add the functionality to take cell data and convert it to point data.
See below code:
import vtk
#dfsu data
pts = df.mesh.generate_node_vertexes()
cls = df.mesh.generate_element_vertex_indexes()
cnts = df.mesh.generate_element_vertex_count()
#vtk instantiate
points = vtk.vtkPoints()
cell = vtk.vtkCellArray()
mesh_1 = vtk.vtkUnstructuredGrid()
#loop through points
for pt in pts:
points.InsertNextPoint(pt)
#set points
mesh_1.SetPoints(points)
#loop through cells
for cl, cnt in zip(cls, cnts):
if cnt == 3:
tri = vtk.vtkTriangle()
for i,j in enumerate(cl):
tri.GetPointIds().SetId(i, j)
mesh_1.InsertNextCell(tri.GetCellType(), tri.GetPointIds())
elif cnt == 4:
quad = vtk.vtkQuad()
for i,j in enumerate(cl):
quad.GetPointIds().SetId(i,j)
mesh_1.InsertNextCell(quad.GetCellType(), quad.GetPointIds())
#add cell data
arr = vtk.vtkDoubleArray()
arr.SetName('test1')
for i in range(len(cls)):
arr.InsertNextTuple([0.5])
arr1 = vtk.vtkDoubleArray()
arr1.SetName('test2')
for i in range(len(cls)):
arr1.InsertNextTuple([0.25])
mesh_1.GetCellData().AddArray(arr)
mesh_1.GetCellData().AddArray(arr1)
#convert to point data - THIS IS THE PART I CANT FIGURE OUT!
c2p = vtk.vtkCellDataToPointData()
c2p.SetInputData(arr)
#add point data
##here i want to add the converted point data
##
#write
writer = vtk.vtkUnstructuredGridWriter()
writer.SetFileName('test.vtk')
writer.SetInputData(mesh_1)
writer.Write()
I'm really new to VTK and it's a bit confusing. I can't figure out how to take my cell data and convert it to point data.
I've tried:
c2p.SetInputData(arr)
c2p.SetInputData(mesh_1.GetCellData().GetArray(0))
and a bunch of other random commands, really can't figure out how to do it.
any suggestions are appreciated - I've seen a ton of examples but were slightly different than what I am trying to do..

figured it out.. I had to actually pass the vtkUnstructuredGrid into the cell to point filter
c2p = vtk.vtkCellDataToPointData()
c2p.SetInputData(mesh_1)
c2p.Update()
ptdata = c2p.GetOutput()
this outputs another vtkUnstructuredGrid object with the cell data converted to point data, which I can then pass into the writer

Related

How to Construct a spherical nanoparticles from the large bulk structure using python3

Problem definition: I wanted to construct spherical nanoparticles of maghemite nanoparticles(gamma Fe2O3) with a radius of 40 angstrom (4nm). I have the lammps data file of large bulk system (replicate :10 10 10, 160000 atoms). I am a beginner in python but I have managed to write a code in python I tried deleting the x,y,z coordinates from the center of all three axis which is not within the radius distance but it is not working, only after looking at the output in VMD i understood i am doing things wrongly but i don't know how to cut a sphere out of a cube please some one help me in this. following is my python code.Thanks in advance.
import pandas as pd
import numpy as np
df = pd.read_csv("data.supercell443.txt",sep='\t',header=None)
optdf = pd.DataFrame([])
IL = 1
xmid = df[4].max()/2
ymid = df[5].max()/2
zmid = df[6].max()/2
xallowed_less = xmid+40
xallowed_more = xmid-40
yallowed_less = ymid+40
yallowed_more = ymid-40
zallowed_more = zmid-40
zallowed_less = zmid+40
for i,j,k,l,x,y,z in df.values:
if abs(xmid-x) = 40:
tdf = pd.DataFrame([IL,j,k,l,x,y,z])
optdf = optdf.append(tdf.T)
IL+=1
Input image from data file using VMD software
Output image of code
Is it necessary to do this in Python? This is easily done in LAMMPS itself using the 'group' command or using existing tools such as Atomsk.

How to plot data on a basemap using matplotlib basemap

Two sections of my code are giving me trouble, I am trying to get the basemap created in this first section here:
#Basemap
epsg = 6060; width = 2000.e3; height = 2000.e3 #epsg 3413. 6062
m=Basemap(epsg=epsg,resolution='l',width=width,height=height) #lat_ts=(90.+35.)/2.
m.drawcoastlines(color='white')
m.drawmapboundary(fill_color='#99ffff')
m.fillcontinents(color='#cc9966',lake_color='#99ffff')
m.drawparallels(np.arange(10,70,20),labels=[1,1,0,0])
m.drawmeridians(np.arange(-100,0,20),labels=[0,0,0,1])
plt.title('ICESAT2 Tracks in Greenland')
plt.figure(figsize=(20,10))
Then my next section is meant to plot the data its getting from a file, and plot these tracks on top of the Basemap. Instead, it creates a new plot entirely. I have tried rewording the secondary plt.scatter to match Basemap, such as m.scatter, m.plt, etc. But it only returns with “RuntimeError: Can not put single artist in more than one figure” when I do so.
Any ideas on how to get this next section of code onto the basemap? Here is the next section, focus on the end to see where it is plotting.
icesat2_data[track] = dict() # creates a sub-dictionary, track
icesat2_data[track][year+month+day] = dict() # and one layer more for the date under the whole icesat2_data dictionary
icesat2_data[track][year+month+day] = dict.fromkeys(lasers)
for laser in lasers: # for loop, access all the gt1l, 2l, 3l
if laser in f:
lat = f[laser]["land_ice_segments"]["latitude"][:] # data for a particular laser's latitude.
lon = f[laser]["land_ice_segments"]["longitude"][:] #data for a lasers longitude
height = f[laser]["land_ice_segments"]["h_li"][:] # data for a lasers height
quality = f[laser]["land_ice_segments"]["atl06_quality_summary"][:].astype('int')
# Quality filter
idx1 = quality == 0 # data dictionary to see what quality summary is
#print('idx1', idx1)
# Spatial filter
idx2 = np.logical_and( np.logical_and(lat>=lat_min, lat<=lat_max), np.logical_and(lon>=lon_min, lon<=lon_max) )
idx = np.where( np.logical_and(idx1, idx2) ) # combines index 1 and 2 from data quality filter. make sure not empty. if empty all data failed test (low quality or outside box)
icesat2_data[track][year+month+day][laser] = dict.fromkeys(['lat','lon','height']) #store data, creates empty dictionary of lists lat, lon, hi, those strings are the keys to the dict.
icesat2_data[track][year+month+day][laser]['lat'] = lat[idx] # grabbing only latitudes using that index of points with good data quality and within bounding box
icesat2_data[track][year+month+day][laser]['lon'] = lon[idx]
icesat2_data[track][year+month+day][laser]['height'] = height[idx]
if lat[idx].any() == True and lon[idx].any() == True:
x, y = transformer.transform(icesat2_data[track][year+month+day][laser]['lon'], \
icesat2_data[track][year+month+day][laser]['lat'])
plt.scatter(x, y, marker='o', color='#000000')
Currently, they output separately, like this:
Not sure if you're still working on this, but here's a quick example I put together that you might be able to work with (obviously I don't have the data you're working with). A couple things that might not be self-explanatory - I used m() to transform the coordinates to map coordinates. This is Basemap's built-in transformation method so you don't have to use PyProj. Also, setting a zorder in the scatter function ensures that your points are plotted above the countries layer and don't get hidden underneath.
#Basemap
epsg = 6060; width = 2000.e3; height = 2000.e3 #epsg 3413. 6062
plt.figure(figsize=(20,10))
m=Basemap(epsg=epsg,resolution='l',width=width,height=height) #lat_ts=(90.+35.)/2.
m.drawcoastlines(color='white')
m.drawmapboundary(fill_color='#99ffff')
m.fillcontinents(color='#cc9966',lake_color='#99ffff')
m.drawparallels(np.arange(10,70,20),labels=[1,1,0,0])
m.drawmeridians(np.arange(-100,0,20),labels=[0,0,0,1])
plt.title('ICESAT2 Tracks in Greenland')
for coord in [[68,-39],[70,-39]]:
lat = coord[0]
lon = coord[1]
x, y = m(lon,lat)
m.scatter(x,y,color='red',s=100,zorder=10)
plt.show()
I think you might need:
plt.figure(figsize(20,10))
before creating the basemap, not after. As it stands it's creating a map and then creating a new figure after that which is why you're getting two figures.
Then your plotting line should be m.scatter() as you mentioned you tried before.

Do anyone know how to extract Image coordinate from Marmot dataset?

Marmot is a document image dataset (http://www.icst.pku.edu.cn/cpdp/data/marmot_data.htm) where labelling several things such as document body, image area, table area, table caption and so on. This dataset specially use for document image analysis research purpose. They mentioned all coordinates in 16 digit hexa decimal with little endian format. Is there anyone how worked with this dataset and how to convert that 16 digit XY coordinate to human understandable format?
Finally I got the clue after analysis and posting here if anyone need to investigate this dataset. However, they mentioned the unit value in which way they convert the given coordinate into pixel value but it was difficult to trace out because they did not mentioned it in their manual/guideline. They mentioned another place as an annotation.
First you have to convert their 16 character hexadecimal value using IEEE 754 little endian format. For example, a given coordinates for a label is,
BBox=['4074145c00000005', '4074dd95999999a9', '4080921e74bc6a80', '406fb9999999999a']
Convert using python,
conv_pound = struct.unpack('!d', str(t).decode('hex'))[0]) for t in BBox]
You will get value in "pound" unit which is 1/72 inch. We usually use coordinate in pixel unit and we know 1 inch is 96 pixel. So,
conv_pound = [321.2724609375003, 333.8490234375009, 530.2648710937501, 253.8]
Then, divided each value by 72 and multiply with 96 to finally get corresponding pixel value which is,
in_pixel = [428.36328, 445.13203, 707.01983, 338.40000]
They started to count pixel position from bottom-left corner of the document image. If you consider from top-left corner (usually we consider in this way), you have to subtract 2nd and 4th value from image height. If we consider image [height, width] is [1123, 793] then we can represent the above coordinates in integer value as,
label_boundary = [428, 678, 707, 785]
After staring at the xmls for an hour, I've found the last missing piece in the answer by #MMReza:
You don't need to rely on the units of measure in (step number 3). There is an attribute called "CropBox" of the root element "Page". Use that one to scale the coordinates.
I have something along the following lines (also inverse y axis here):
px0, py1, px1, py0 = list(map(hex_to_double, page.get("CropBox").split()))
pw = abs(px1 - px0)
ph = abs(py1 - py0)
for table in page.findall(".//Composite[#Label='TableBody']"):
x0p, y1m, x1p, y0m = list(map(hex_to_double, table.get("BBox").split()))
x0 = round(imgw*(x0p - px0)/pw)
x1 = round(imgw*(x1p - px0)/pw)
y0 = round(imgh*(py1 - y0m)/ph)
y1 = round(imgh*(py1 - y1m)/ph)
In case anyone is trying to do this in Python 3 like I did, you only have to change step 2 of the other answer like this :
conv_pound = [struct.unpack('!d', bytes.fromhex(t))[0] for t in BBox]
I wanted to convert the coordinates as well as wanted to verify that my conversion actually worked. So, I made this script to read label file and respective image file then extract coordinates of table body(for eg) and visualize them on the images. It can be used to extract other fields in the similar manner. Comments explain it all
import glob
import struct
import cv2
import binascii
import re
xml_files = glob.glob("path_to_labeled_files/*.xml")
for i in xml_files:
# Open the current file and read everything
cur_file = open(i,"r")
content = cur_file.read()
# Find index of all occurrences of only needed portions (eg TableBody this case)
residxs = [l.start() for l in re.finditer('Label="TableBody"', content)]
# Read the image
img = cv2.imread("path_to_images_folder/"+i.split('/')[-1][:-3]+"jpg")
# Traverse over all occurences
for r in residxs[:-1]:
# List to store output points
coords = []
# Start index of an occurence
sidx = r
# Substring from whole file content
substr = content[sidx:sidx+400]
# Now find start index and end index of coordinates in this substring
sidx = substr.find('BBox="')
eidx = substr.find('" CLIDs')
# String containing only points
points = substr[sidx+6:eidx]
# Make the conversion (also take care of little and big endian in unpack)
bins = ''
for j in points.split(' '):
if(j == ''):
continue
coords.append(struct.unpack('>d', binascii.unhexlify(j))[0])
if len(coords) != 4:
continue
# As suggested by MMReza
for k in range(4):
coords[k] = (coords[k]/72)*96
coords[1] = img.shape[0] - coords[1]
coords[3] = img.shape[0] - coords[3]
# Print the extracted coordinates
print(coords)
# Visualize it on the image
cv2.rectangle(img, (int(coords[0]),int(coords[1])) , (int(coords[2]),int(coords[3])), (255, 0, 0), 2)
cv2.imshow("frame",img)
cv2.waitKey(0)

How to grid data points onto a shapefile (grid)?

So I am working with a shapefile and a csv file of data points. I have used a raster function to convert the shapefile to a grid based on latitudes/longitudes. Now I need to put data points onto the same grid so that I can see where they fall in comparison to the "shape" produced by the other file. I have opened the csv file using the below code, and removed where the latitude/longitudes are null and made two new numpy arrays of lat/lons. But now I am confused about the next step, so if anyone has done anything similar, how should I proceed?
x = list(csv.reader(open(places,'rb'),delimiter=','))
List1 = zip(*x)
dataDict1 = {}
for column in List1:
dataDict1[col[0]] = col[1:]
lats = np.array(dataDict1['Latitude'])
lons = np.array(dataDict1['Longitude'])

Paraview for Data Display Over Time

I am trying to use Paraview to display points as colored boxes that change scalar value over time. I have tried to write a programmable source and filter that adds box glyphs to data points and will change the scalar value of the data point based on the time point.
Specifically this script attempts to return an integer value for the current time point and then use this value to parse through a text file and choose the point data corresponding to that time point.
I can’t quite get it to work though and I have two big questions:
Within the Filter, how do I create glyph boxes for the data points?
How do I get the below script to work without getting "indices must be integers not float" error.
Many thanks,
Matt
UPDATE
solved problem by adding:
time = int(time)
after time = 0 in programmable filter
Programmable Source Script
import vtk
import numpy as np
coordinate = np.array([[0,0,0]])
node_file = open(‘…/pointWeights.txt', 'r')
data = node_file.readlines()
timePoint = 0
pointWeight = float(data[timePoint])
node_file.close()
output.Points = coordinate
output.Allocate(1)
output.PointData.append(pointWeight, "Point Weight")
Programmable Source RequestInformation
timeSteps = range(4)
outInfo = self.GetOutputInformation(0)
timeRange = [timeSteps[0], timeSteps[-1]]
outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_RANGE(), timeRange, 2)
outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_STEPS(), timeSteps, len(timeSteps))
Programmable Filter Script
import vtk
import numpy as np
outInfo = self.GetOutputInformation(0)
if outInfo.Has(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
time = outInfo.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP())
else:
time = 0
coordinate = np.array([[0,0,0]])
node_file = open(‘…/pointWeights.txt', 'r')
data = node_file.readlines()
pointWeight = float(data[time])
node_file.close()
output.Points = coordinate
output.Allocate(1)
output.PointData.append(pointWeight, "Point Weight")
pointWeights.txt
0
.33
.66
1

Categories

Resources