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.

  1. 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.
  1. Two direct download links for files of format .PTS:
  1. 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.

Drag and drop a .PTS file here
  1. 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")
  1. 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");
}
  1. 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');
  1. 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;
}