Task 569: .PRJ File Format
Task 569: .PRJ File Format
1. List of all the properties of this file format intrinsic to its file system
The .PRJ file format is used by ESRI software for defining spatial projection information. It is a plain text file containing a Well-Known Text (WKT) string that describes the coordinate system. The format is not binary; it is ASCII text, typically on a single line without extra spaces or line breaks. The properties are the WKT keywords and their associated parameters, which define the structure of the coordinate system. Based on ESRI's implementation of WKT1 (with some proprietary variations), the intrinsic properties (keywords) include:
- PROJCS: Defines the projected coordinate system name and its components.
- GEOGCS: Defines the geographic coordinate system name and its components.
- DATUM: Defines the datum name and spheroid.
- SPHEROID: Defines the spheroid name, semi-major axis, and inverse flattening.
- PRIMEM: Defines the prime meridian name and longitude.
- UNIT: Defines the unit name and conversion factor (used for both angular and linear units).
- PROJECTION: Defines the projection method name (e.g., "Lambert_Conformal_Conic").
- PARAMETER: Defines projection parameters (name and value, e.g., "False_Easting", "False_Northing", "Central_Meridian", "Standard_Parallel_1", "Standard_Parallel_2", "Latitude_Of_Origin").
- AUTHORITY: Defines the authority name and code (e.g., "EPSG", code).
- AXIS: Defines axis names and directions (optional in some ESRI .prj files).
- TOWGS84: Defines datum transformation parameters to WGS84 (optional).
- VERTCS: Defines vertical coordinate system (rare in standard .prj files, as they are mostly for horizontal projections).
These properties are nested in a bracketed structure, with values in double quotes for strings and numeric literals for numbers. The file system intrinsics are minimal since it's text: the file is UTF-8 or ASCII encoded, with no header or footer beyond the WKT string.
2. Two direct download links for files of format .PRJ
- https://raw.githubusercontent.com/qgis/QGIS/master/tests/testdata/bug5598.prj
- https://gis.pima.gov/data/about/project/azsp83cf.prj (Pima County Arizona State Plane .prj example; note: if 404, use right-click save as from the index page at https://gis.pima.gov/data/about/project/index.cfm)
3. Ghost blog embedded HTML JavaScript for drag and drop .PRJ file to dump properties
Here is a self-contained HTML page with embedded JavaScript that allows dragging and dropping a .PRJ file. It reads the file as text, parses the WKT string, extracts the properties, and dumps them to the screen in a tree-like format.
4. Python class for opening, decoding, reading, writing, and printing .PRJ properties
import re
class PrjFile:
def __init__(self, filepath=None):
self.filepath = filepath
self.wkt = ''
self.properties = {}
if filepath:
self.read()
def read(self):
with open(self.filepath, 'r') as f:
self.wkt = f.read().trim()
self.properties = self.parse_wkt(self.wkt)
def parse_wkt(self, wkt):
# Simple regex-based parser for WKT
stack = []
current = {}
root = current
tokens = re.finditer(r'([A-Z_]+)|"([^"]*)"|\[|\]|\(|\)|,|([0-9.-]+)', wkt)
key = None
for match in tokens:
if match.group(1): # Keyword
key = match.group(1)
current[key] = {}
stack.append(current)
current = current[key]
elif match.group(2): # String
if key:
current['name'] = match.group(2)
key = None
elif match.group(3): # Number
if key:
current[key] = float(match.group(3))
key = None
elif match.group(0) == '[' or match.group(0) == '(':
pass # Already handled by keyword
elif match.group(0) == ']' or match.group(0) == ')':
current = stack.pop()
elif match.group(0) == ',':
key = None
return root
def print_properties(self, properties=None, indent=''):
if properties is None:
properties = self.properties
for key, value in properties.items():
print(f"{indent}{key}:")
if isinstance(value, dict):
self.print_properties(value, indent + ' ')
else:
print(f"{indent} {value}")
def write(self, filepath=None):
if not filepath:
filepath = self.filepath
wkt = self.serialize_properties(self.properties)
with open(filepath, 'w') as f:
f.write(wkt)
def serialize_properties(self, properties, level=0):
wkt = ''
for key, value in properties.items():
wkt += key + '['
if isinstance(value, dict):
wkt += self.serialize_properties(value, level + 1)
else:
wkt += str(value)
wkt += ']'
if level > 0:
wkt += ','
return wkt.replace(',]', ']')
# Example usage:
# prj = PrjFile('example.prj')
# prj.print_properties()
# prj.write('output.prj')
5. Java class for opening, decoding, reading, writing, and printing .PRJ properties
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class PrjFile {
private String filepath;
private String wkt;
private Map<String, Object> properties;
public PrjFile(String filepath) {
this.filepath = filepath;
this.properties = new HashMap<>();
read();
}
public void read() {
try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
wkt = br.readLine().trim();
properties = parseWKT(wkt);
} catch (Exception e) {
e.printStackTrace();
}
}
private Map<String, Object> parseWKT(String wkt) {
Stack<Map<String, Object>> stack = new Stack<>();
Map<String, Object> root = new HashMap<>();
Map<String, Object> current = root;
String key = "";
String value = "";
boolean inString = false;
for (char charr : wkt.toCharArray()) {
if (charr == '"') {
inString = !inString;
} else if (inString) {
value += charr;
} else if (charr == '[' || charr == '(') {
if (!key.isEmpty()) {
current.put(key, new HashMap<String, Object>());
stack.push(current);
current = (Map<String, Object>) current.get(key);
key = "";
}
} else if (charr == ']' || charr == ')') {
if (!value.isEmpty()) {
current.put(key, value.trim());
value = "";
key = "";
}
if (!stack.isEmpty()) {
current = stack.pop();
}
} else if (charr == ',') {
if (!value.isEmpty()) {
current.put(key, value.trim());
value = "";
key = "";
}
} else {
value += charr;
if (Character.isLetter(charr)) {
if (key.isEmpty()) {
key = value;
value = "";
}
}
}
}
return root;
}
public void printProperties() {
printProperties(properties, "");
}
private void printProperties(Map<String, Object> props, String indent) {
for (Map.Entry<String, Object> entry : props.entrySet()) {
System.out.println(indent + entry.getKey() + ":");
if (entry.getValue() instanceof Map) {
printProperties((Map<String, Object>) entry.getValue(), indent + " ");
} else {
System.out.println(indent + " " + entry.getValue());
}
}
}
public void write(String filepath) {
try (PrintWriter pw = new PrintWriter(filepath)) {
pw.print(serializeProperties(properties));
} catch (Exception e) {
e.printStackTrace();
}
}
private String serializeProperties(Map<String, Object> props) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : props.entrySet()) {
sb.append(entry.getKey()).append("[");
if (entry.getValue() instanceof Map) {
sb.append(serializeProperties((Map<String, Object>) entry.getValue()));
} else {
sb.append(entry.getValue());
}
sb.append("],");
}
return sb.toString().replace(",]", "]");
}
// Example usage:
// public static void main(String[] args) {
// PrjFile prj = new PrjFile("example.prj");
// prj.printProperties();
// prj.write("output.prj");
// }
}
6. JavaScript class for opening, decoding, reading, writing, and printing .PRJ properties
Note: This is for Node.js, as browser JS can't directly open files without input. Use fs module.
const fs = require('fs');
class PrjFile {
constructor(filepath) {
this.filepath = filepath;
this.wkt = '';
this.properties = {};
if (filepath) {
this.read();
}
}
read() {
this.wkt = fs.readFileSync(this.filepath, 'utf8').trim();
this.properties = this.parseWKT(this.wkt);
}
parseWKT(wkt) {
const stack = [];
const root = {};
let current = root;
let key = '';
let value = '';
let inString = false;
for (let i = 0; i < wkt.length; i++) {
const char = wkt[i];
if (char === '"') {
inString = !inString;
} else if (inString) {
value += char;
} else if (char === '[' || char === '(') {
if (key) {
current[key] = {};
stack.push(current);
current = current[key];
key = '';
}
} else if (char === ']' || char === ')') {
if (value) {
current[key] = value.trim();
value = '';
key = '';
}
if (stack.length) {
current = stack.pop();
}
} else if (char === ',') {
if (value) {
current[key] = value.trim();
value = '';
key = '';
}
} else {
value += char;
}
}
return root;
}
printProperties(properties = this.properties, indent = '') {
for (const key in properties) {
console.log(`${indent}${key}:`);
if (typeof properties[key] === 'object') {
this.printProperties(properties[key], indent + ' ');
} else {
console.log(`${indent} ${properties[key]}`);
}
}
}
write(filepath = this.filepath) {
const wkt = this.serializeProperties(this.properties);
fs.writeFileSync(filepath, wkt);
}
serializeProperties(properties) {
let wkt = '';
for (const key in properties) {
wkt += `${key}[`;
if (typeof properties[key] === 'object') {
wkt += this.serializeProperties(properties[key]);
} else {
wkt += properties[key];
}
wkt += '],';
}
return wkt.replace(',]', ']');
}
}
// Example usage:
// const prj = new PrjFile('example.prj');
// prj.printProperties();
// prj.write('output.prj');
7. C class for opening, decoding, reading, writing, and printing .PRJ properties
This is C++, as C does not have classes natively. Uses std::map for properties.
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <stack>
#include <any>
using namespace std;
class PrjFile {
private:
string filepath;
string wkt;
map<string, any> properties;
map<string, any> parseWKT(const string& wkt) {
stack<map<string, any>*> stack;
map<string, any> root;
map<string, any>* current = &root;
string key = "";
string value = "";
bool inString = false;
for (char charr : wkt) {
if (charr == '"') {
inString = !inString;
} else if (inString) {
value += charr;
} else if (charr == '[' || charr == '(') {
if (!key.empty()) {
(*current)[key] = map<string, any>{};
stack.push(current);
current = &any_cast<map<string, any>&>((*current)[key]);
key = "";
}
} else if (charr == ']' || charr == ')') {
if (!value.empty()) {
(*current)[key] = value;
value = "";
key = "";
}
if (!stack.empty()) {
current = stack.top();
stack.pop();
}
} else if (charr == ',') {
if (!value.empty()) {
(*current)[key] = value;
value = "";
key = "";
}
} else {
value += charr;
}
}
return root;
}
void printProperties(const map<string, any>& props, const string& indent) {
for (const auto& entry : props) {
cout << indent << entry.first << ":" << endl;
if (entry.second.type() == typeid(map<string, any>)) {
printProperties(any_cast<const map<string, any>&>(entry.second), indent + " ");
} else if (entry.second.type() == typeid(string)) {
cout << indent << " " << any_cast<string>(entry.second) << endl;
}
}
}
string serializeProperties(const map<string, any>& props) {
string wkt = "";
for (const auto& entry : props) {
wkt += entry.first + "[";
if (entry.second.type() == typeid(map<string, any>)) {
wkt += serializeProperties(any_cast<const map<string, any>&>(entry.second));
} else if (entry.second.type() == typeid(string)) {
wkt += any_cast<string>(entry.second);
}
wkt += "],";
}
if (!wkt.empty()) wkt.pop_back(); // Remove last ,
return wkt.replace(wkt.find(",]"), 2, "]");
}
public:
PrjFile(const string& fp) : filepath(fp) {
read();
}
void read() {
ifstream file(filepath);
getline(file, wkt);
properties = parseWKT(wkt);
file.close();
}
void printProperties() {
printProperties(properties, "");
}
void write(const string& fp) {
ofstream file(fp);
file << serializeProperties(properties);
file.close();
}
};
// Example usage:
// int main() {
// PrjFile prj("example.prj");
// prj.printProperties();
// prj.write("output.prj");
// return 0;
// }