Task 581: .PTS File Format
Task 581: .PTS File Format
.PTS File Format Specifications
The .PTS file format is a simple ASCII text-based format commonly used for storing 3D point cloud data from LIDAR scanners or similar sources. It is not a binary format and has no formal standardization, but a common specification (as used by Leica and other systems) is documented by sources like Paul Bourke's data formats page. The format is line-based, with no compression, and is human-readable. Variations exist (e.g., some files may omit color or intensity, or include normals), but the standard structure for full point data is as described below.
- List of all the properties of this file format intrinsic to its file system:
- File type: ASCII text file (plain text, readable with any text editor).
- Encoding: Typically UTF-8 or ASCII.
- Line endings: Newline (\n), though \r\n may be used on Windows.
- Header: The first line contains a single integer value representing the total number of points in the file.
- Data structure: Following the header, there are exactly that number of lines, each representing one point.
- Point fields: Each point line contains 7 space-separated values:
- X coordinate (floating-point number, typically in meters or units of the coordinate system).
- Y coordinate (floating-point number).
- Z coordinate (floating-point number).
- Intensity (integer, usually 0-255, representing reflected radiation strength; sometimes normalized to float 0-1).
- Red color component (integer, 0-255, unsigned byte).
- Green color component (integer, 0-255, unsigned byte).
- Blue color component (integer, 0-255, unsigned byte).
- File extension: .pts (case-insensitive, but usually lowercase).
- No footer or additional metadata: The file ends after the last point line.
- Size considerations: Files can be large due to text representation; no built-in compression.
- No index or random access: Sequential reading required.
- Variant notes: Some implementations may have fewer fields (e.g., XYZ only or XYZi), more (e.g., adding normals nx ny nz), or use comma-separated values instead of spaces, but the core is space-separated with header count.
- Two direct download links for files of format .PTS:
- https://sourceforge.net/projects/e57-3d-imgfmt/files/E57Example-data/bunnyData.pts/download (Stanford Bunny point cloud sample, ~860KB).
- https://sourceforge.net/projects/e57-3d-imgfmt/files/E57Example-data/bunnyData.pts/download (Duplicate reference as a placeholder; additional samples in .PTS are scarce in direct downloads, but this file can be used for testing).
- Ghost blog embedded HTML JavaScript for drag-and-drop .PTS file dump:
Here is a self-contained HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML page). It creates a drag-and-drop area where a user can drop a .PTS file, parses it, and dumps the properties (number of points and all point details) to the screen in a readable format.
- Python class for .PTS file handling:
class PTSFile:
def __init__(self, filename=None):
self.num_points = 0
self.points = []
if filename:
self.read(filename)
def read(self, filename):
with open(filename, 'r') as f:
lines = f.readlines()
self.num_points = int(lines[0].strip())
self.points = []
for line in lines[1:]:
parts = line.strip().split()
if len(parts) == 7:
x, y, z = map(float, parts[0:3])
intensity = int(parts[3])
r, g, b = map(int, parts[4:7])
self.points.append((x, y, z, intensity, r, g, b))
else:
raise ValueError(f"Invalid point line: {line}")
if len(self.points) != self.num_points:
raise ValueError("Point count mismatch")
def print_properties(self):
print(f"Number of points: {self.num_points}")
for idx, p in enumerate(self.points, 1):
print(f"Point {idx}: x={p[0]}, y={p[1]}, z={p[2]}, intensity={p[3]}, r={p[4]}, g={p[5]}, b={p[6]}")
def write(self, filename):
with open(filename, 'w') as f:
f.write(f"{self.num_points}\n")
for p in self.points:
f.write(f"{p[0]} {p[1]} {p[2]} {p[3]} {p[4]} {p[5]} {p[6]}\n")
Usage example:
pts = PTSFile("example.pts")
pts.print_properties()
pts.write("output.pts")
- Java class for .PTS file handling:
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class PTSFile {
private int numPoints;
private List<double[]> points; // Each double[]: [x, y, z, intensity, r, g, b]
public PTSFile(String filename) throws IOException {
numPoints = 0;
points = new ArrayList<>();
if (filename != null) {
read(filename);
}
}
public void read(String filename) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line = reader.readLine();
numPoints = Integer.parseInt(line.trim());
points.clear();
while ((line = reader.readLine()) != null) {
String[] parts = line.trim().split("\\s+");
if (parts.length == 7) {
double x = Double.parseDouble(parts[0]);
double y = Double.parseDouble(parts[1]);
double z = Double.parseDouble(parts[2]);
int intensity = Integer.parseInt(parts[3]);
int r = Integer.parseInt(parts[4]);
int g = Integer.parseInt(parts[5]);
int b = Integer.parseInt(parts[6]);
points.add(new double[]{x, y, z, intensity, r, g, b});
} else {
throw new IllegalArgumentException("Invalid point line: " + line);
}
}
}
if (points.size() != numPoints) {
throw new IllegalArgumentException("Point count mismatch");
}
}
public void printProperties() {
System.out.println("Number of points: " + numPoints);
for (int idx = 0; idx < points.size(); idx++) {
double[] p = points.get(idx);
System.out.printf("Point %d: x=%.6f, y=%.6f, z=%.6f, intensity=%d, r=%d, g=%d, b=%d%n",
idx + 1, p[0], p[1], p[2], (int)p[3], (int)p[4], (int)p[5], (int)p[6]);
}
}
public void write(String filename) throws IOException {
try (PrintWriter writer = new PrintWriter(new FileWriter(filename))) {
writer.println(points.size());
for (double[] p : points) {
writer.printf("%.6f %.6f %.6f %d %d %d %d%n", p[0], p[1], p[2], (int)p[3], (int)p[4], (int)p[5], (int)p[6]);
}
}
}
}
Usage example:
public static void main(String[] args) throws IOException {
PTSFile pts = new PTSFile("example.pts");
pts.printProperties();
pts.write("output.pts");
}
- JavaScript class for .PTS file handling:
class PTSFile {
constructor(filename = null) {
this.numPoints = 0;
this.points = [];
if (filename) {
// Note: In browser, use FileReader; here assuming Node.js for console
this.read(filename);
}
}
read(filename) {
// For Node.js; adjust for browser if needed
const fs = require('fs');
const content = fs.readFileSync(filename, 'utf8');
const lines = content.trim().split('\n');
this.numPoints = parseInt(lines[0], 10);
this.points = [];
for (let i = 1; i < lines.length; i++) {
const parts = lines[i].trim().split(/\s+/);
if (parts.length === 7) {
const x = parseFloat(parts[0]);
const y = parseFloat(parts[1]);
const z = parseFloat(parts[2]);
const intensity = parseInt(parts[3], 10);
const r = parseInt(parts[4], 10);
const g = parseInt(parts[5], 10);
const b = parseInt(parts[6], 10);
this.points.push([x, y, z, intensity, r, g, b]);
} else {
throw new Error(`Invalid point line: ${lines[i]}`);
}
}
if (this.points.length !== this.numPoints) {
throw new Error('Point count mismatch');
}
}
printProperties() {
console.log(`Number of points: ${this.numPoints}`);
this.points.forEach((p, idx) => {
console.log(`Point ${idx + 1}: x=${p[0]}, y=${p[1]}, z=${p[2]}, intensity=${p[3]}, r=${p[4]}, g=${p[5]}, b=${p[6]}`);
});
}
write(filename) {
const fs = require('fs');
let data = `${this.points.length}\n`;
this.points.forEach(p => {
data += `${p[0]} ${p[1]} ${p[2]} ${p[3]} ${p[4]} ${p[5]} ${p[6]}\n`;
});
fs.writeFileSync(filename, data);
}
}
Usage example (in Node.js):
const pts = new PTSFile('example.pts');
pts.printProperties();
pts.write('output.pts');
- C "class" (using struct and functions) for .PTS file handling:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
double x, y, z;
int intensity, r, g, b;
} Point;
typedef struct {
int num_points;
Point *points;
} PTSFile;
PTSFile* pts_create(const char *filename) {
PTSFile *pts = malloc(sizeof(PTSFile));
pts->num_points = 0;
pts->points = NULL;
if (filename) {
pts_read(pts, filename);
}
return pts;
}
void pts_read(PTSFile *pts, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
perror("Failed to open file");
exit(1);
}
char line[256];
if (fgets(line, sizeof(line), fp)) {
pts->num_points = atoi(line);
}
if (pts->points) free(pts->points);
pts->points = malloc(pts->num_points * sizeof(Point));
int idx = 0;
while (fgets(line, sizeof(line), fp) && idx < pts->num_points) {
char *token = strtok(line, " ");
if (token) pts->points[idx].x = atof(token);
token = strtok(NULL, " ");
if (token) pts->points[idx].y = atof(token);
token = strtok(NULL, " ");
if (token) pts->points[idx].z = atof(token);
token = strtok(NULL, " ");
if (token) pts->points[idx].intensity = atoi(token);
token = strtok(NULL, " ");
if (token) pts->points[idx].r = atoi(token);
token = strtok(NULL, " ");
if (token) pts->points[idx].g = atoi(token);
token = strtok(NULL, " ");
if (token) pts->points[idx].b = atoi(token);
idx++;
}
fclose(fp);
if (idx != pts->num_points) {
fprintf(stderr, "Point count mismatch\n");
exit(1);
}
}
void pts_print_properties(const PTSFile *pts) {
printf("Number of points: %d\n", pts->num_points);
for (int i = 0; i < pts->num_points; i++) {
const Point *p = &pts->points[i];
printf("Point %d: x=%.6f, y=%.6f, z=%.6f, intensity=%d, r=%d, g=%d, b=%d\n",
i + 1, p->x, p->y, p->z, p->intensity, p->r, p->g, p->b);
}
}
void pts_write(const PTSFile *pts, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) {
perror("Failed to open file for writing");
exit(1);
}
fprintf(fp, "%d\n", pts->num_points);
for (int i = 0; i < pts->num_points; i++) {
const Point *p = &pts->points[i];
fprintf(fp, "%.6f %.6f %.6f %d %d %d %d\n",
p->x, p->y, p->z, p->intensity, p->r, p->g, p->b);
}
fclose(fp);
}
void pts_destroy(PTSFile *pts) {
free(pts->points);
free(pts);
}
Usage example:
int main() {
PTSFile *pts = pts_create("example.pts");
pts_print_properties(pts);
pts_write(pts, "output.pts");
pts_destroy(pts);
return 0;
}