Task 256: .GEOJSON File Format
Task 256: .GEOJSON File Format
1. List of All Properties Intrinsic to the .GEOJSON File Format
GeoJSON is a geospatial data interchange format based on JSON, with specific constraints for encoding geographic features. Below is a comprehensive list of its intrinsic properties, derived from the official specification (RFC 7946). These define the format's structure, constraints, and behaviors at the file level.
- Base Encoding: GeoJSON files are JSON objects compliant with RFC 7159 and I-JSON (RFC 7493) for interoperability; they must be encoded in UTF-8.
- Top-Level Structure: The root must be one of three object types: "FeatureCollection" (array of features), "Feature" (single feature with geometry and properties), or "Geometry" (direct geometry object); specified via a mandatory "type" string member.
- Geometry Types: Supported types are "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", and "GeometryCollection"; each requires a "coordinates" array (except GeometryCollection, which uses "geometries" array of sub-geometries).
- Coordinate System (CRS): Fixed to WGS 84 (EPSG:4326) geographic CRS; coordinates are decimal degrees with longitude first, then latitude (optional third value for altitude in meters); no support for alternative CRS.
- Coordinate Precision: Coordinates are numbers with up to 6 decimal places recommended (for ~10 cm precision); excess precision should be avoided for performance; values must not imply uncertainty levels.
- Positions and Arrays: Coordinates are arrays of 2–3 numbers; LineString/MultiLineString require ≥2 positions; Polygon rings require ≥4 positions (closed loops); empty coordinate arrays may represent null geometries.
- Bounding Box (bbox): Optional array of 4 (2D) or 6 (3D) numbers: [west, south, east, north] or with heights; defines axis-aligned extent; follows geometry's dimension order.
- Properties Object: In Features, an optional "properties" member holds arbitrary JSON key-value pairs (or null); used for non-spatial attributes.
- Identifier (id): Optional string or number in Features for unique identification.
- Foreign Members: Allows arbitrary additional JSON members outside core spec; no guaranteed processing semantics, may reduce interoperability.
- Validity Rules: No extension of core types or semantics; FeatureCollection/Feature cannot have "coordinates" or "geometries"; Polygons should follow right-hand rule (counterclockwise exterior, clockwise interiors) but parsers tolerate violations for compatibility; no nested GeometryCollections or redundant single-type collections.
- Empty Collections: "features" in FeatureCollection or "geometries" in GeometryCollection may be empty arrays.
- Antimeridian Handling: Geometries crossing ±180° longitude should be split (e.g., into MultiLineString/MultiPolygon) to avoid crossing; improves rendering interoperability.
- File Extension and MIME Type: Typically .geojson; MIME type application/geo+json (or application/json fallback).
- Interoperability Notes: Files must not rely on foreign members for core function; processors may treat empty geometries as null.
2. Two Direct Download Links for .GEOJSON Files
- Sample FeatureCollection of points: https://gist.githubusercontent.com/wavded/1200773/raw/sample.json
- US states boundaries as MultiPolygon FeatureCollection: https://raw.githubusercontent.com/python-visualization/folium/master/examples/us_states.geojson
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .GEOJSON Property Dump
Embed this as a Ghost post HTML card or custom HTML block. It creates a drop zone; on drop, parses the .geojson file, extracts and displays key intrinsic properties (e.g., type, bbox, feature count, geometry types, CRS notes) in a formatted <div>
below the zone. Uses native File API and JSON.parse.
4. Python Class for .GEOJSON Handling
This class uses the built-in json
module to read/parse/write .geojson files and prints extracted properties to console. Save as GeoJSONHandler.py
and use handler = GeoJSONHandler('file.geojson'); handler.print_properties()
.
import json
class GeoJSONHandler:
def __init__(self, filepath):
self.filepath = filepath
with open(filepath, 'r', encoding='utf-8') as f:
self.data = json.load(f)
def write(self, output_path):
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(self.data, f, indent=2)
def print_properties(self):
props = self._extract_properties()
for key, value in props.items():
print(f"{key}: {value}")
def _extract_properties(self):
props = {
'File Encoding': 'UTF-8 (JSON-based)',
'Top-Level Type': self.data.get('type'),
'CRS': 'WGS 84 (lon/lat order)',
'Coordinate Precision': 'Up to 6 decimals recommended',
'Bounding Box': str(self.data.get('bbox', 'Not present')),
'Properties Object': 'Present' if self.data.get('properties') is not None else 'Absent or null',
'Identifier': self.data.get('id', 'Not present'),
'Foreign Members': len([k for k in self.data if k not in ['type', 'bbox', 'features', 'geometry', 'properties', 'id', 'coordinates', 'geometries']]),
'Validity Notes': 'Assumes no antimeridian crossing; empty geometries treated as null'
}
if self.data.get('type') == 'FeatureCollection':
props['Feature Count'] = len(self.data.get('features', []))
geom_types = list(set(f.get('geometry', {}).get('type') for f in self.data.get('features', []) if f.get('geometry')))
props['Geometry Types'] = ', '.join(geom_types) if geom_types else 'None'
elif self.data.get('type') == 'Feature':
props['Geometry Type'] = self.data.get('geometry', {}).get('type', 'Null')
else:
props['Geometry Type'] = self.data.get('type')
return props
5. Java Class for .GEOJSON Handling
This uses javax.json
(available in Java 11+ or via dependency; assume imported). Reads/parses/writes .geojson and prints properties. Compile with javac -cp . GeoJSONHandler.java
(adjust classpath for json lib if needed).
import java.io.*;
import javax.json.*;
import java.util.*;
public class GeoJSONHandler {
private JsonObject data;
private String filepath;
public GeoJSONHandler(String filepath) throws IOException {
this.filepath = filepath;
try (JsonReader reader = Json.createReader(new FileReader(filepath))) {
this.data = reader.readObject();
}
}
public void write(String outputPath) throws IOException {
try (JsonWriter writer = Json.createWriter(new FileWriter(outputPath))) {
writer.writeObject(data);
}
}
public void printProperties() {
Map<String, Object> props = extractProperties();
for (Map.Entry<String, Object> entry : props.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
private Map<String, Object> extractProperties() {
Map<String, Object> props = new LinkedHashMap<>();
props.put("File Encoding", "UTF-8 (JSON-based)");
props.put("Top-Level Type", data.getString("type"));
props.put("CRS", "WGS 84 (lon/lat order)");
props.put("Coordinate Precision", "Up to 6 decimals recommended");
props.put("Bounding Box", data.containsKey("bbox") ? data.getJsonArray("bbox").toString() : "Not present");
props.put("Properties Object", data.containsKey("properties") ? "Present" : "Absent or null");
props.put("Identifier", data.containsKey("id") ? data.get("id") : "Not present");
Set<String> coreKeys = Set.of("type", "bbox", "features", "geometry", "properties", "id", "coordinates", "geometries");
long foreignCount = data.keySet().stream().filter(k -> !coreKeys.contains(k)).count();
props.put("Foreign Members", foreignCount);
props.put("Validity Notes", "Assumes no antimeridian crossing; empty geometries treated as null");
String type = data.getString("type");
if ("FeatureCollection".equals(type)) {
props.put("Feature Count", data.getJsonArray("features").size());
Set<String> geomTypes = new HashSet<>();
for (JsonValue feature : data.getJsonArray("features")) {
JsonObject f = (JsonObject) feature;
if (f.containsKey("geometry") && !f.get("geometry").equals(JsonValue.NULL)) {
geomTypes.add(((JsonObject) f.get("geometry")).getString("type"));
}
}
props.put("Geometry Types", geomTypes.isEmpty() ? "None" : String.join(", ", geomTypes));
} else if ("Feature".equals(type)) {
props.put("Geometry Type", data.containsKey("geometry") && !data.get("geometry").equals(JsonValue.NULL)
? ((JsonObject) data.get("geometry")).getString("type") : "Null");
} else {
props.put("Geometry Type", type);
}
return props;
}
}
Usage: new GeoJSONHandler("file.geojson").printProperties();
6. JavaScript Class for .GEOJSON Handling
Node.js class using built-in fs
and JSON
. Reads/parses/writes .geojson and prints properties. Run with node GeoJSONHandler.js file.geojson
.
const fs = require('fs');
class GeoJSONHandler {
constructor(filepath) {
const content = fs.readFileSync(filepath, 'utf8');
this.data = JSON.parse(content);
this.filepath = filepath;
}
write(outputPath) {
fs.writeFileSync(outputPath, JSON.stringify(this.data, null, 2), 'utf8');
}
printProperties() {
const props = this.extractProperties();
Object.entries(props).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
}
extractProperties() {
const props = {
'File Encoding': 'UTF-8 (JSON-based)',
'Top-Level Type': this.data.type,
'CRS': 'WGS 84 (lon/lat order)',
'Coordinate Precision': 'Up to 6 decimals recommended',
'Bounding Box': this.data.bbox ? this.data.bbox.join(', ') : 'Not present',
'Properties Object': this.data.properties !== undefined ? 'Present' : 'Absent or null',
'Identifier': this.data.id || 'Not present',
'Foreign Members': Object.keys(this.data).filter(k => !['type', 'bbox', 'features', 'geometry', 'properties', 'id', 'coordinates', 'geometries'].includes(k)).length,
'Validity Notes': 'Assumes no antimeridian crossing; empty geometries treated as null'
};
const type = this.data.type;
if (type === 'FeatureCollection') {
props['Feature Count'] = this.data.features.length;
const geomTypes = [...new Set(this.data.features.map(f => f.geometry?.type).filter(Boolean))];
props['Geometry Types'] = geomTypes.length ? geomTypes.join(', ') : 'None';
} else if (type === 'Feature') {
props['Geometry Type'] = this.data.geometry?.type || 'Null';
} else {
props['Geometry Type'] = type;
}
return props;
}
}
if (process.argv.length > 2) {
const handler = new GeoJSONHandler(process.argv[2]);
handler.printProperties();
}
7. C Class (Struct) for .GEOJSON Handling
C lacks built-in JSON parsing, so this uses a simple manual parser for basic structures (assumes well-formed input; no full validation). Reads file via fopen/fread
, parses top-level keys, writes back, and prints properties. Compile with gcc -o geojson geojson.c -lm
(basic stdio). For production, use cJSON library.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct {
char* type;
double* bbox; // Simplified: assume 2D array of 4 doubles
int bbox_size;
// Simplified: no full parse for features/geometries; count via string scan
int feature_count;
char* geom_type; // Single type for simplicity
char* filepath;
char* content; // Raw JSON string for write
} GeoJSONHandler;
GeoJSONHandler* geojson_open(const char* filepath) {
FILE* f = fopen(filepath, "r");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
char* content = malloc(size + 1);
fread(content, 1, size, f);
content[size] = '\0';
fclose(f);
GeoJSONHandler* handler = malloc(sizeof(GeoJSONHandler));
handler->content = content;
handler->filepath = strdup(filepath);
// Simple parse: extract "type"
char* type_start = strstr(content, "\"type\":");
if (type_start) {
type_start += 7; // Skip "\"type\":"
while (*type_start && isspace(*type_start)) type_start++;
if (*type_start == '"') type_start++;
char* type_end = strchr(type_start, '"');
if (type_end) {
int len = type_end - type_start;
handler->type = malloc(len + 1);
strncpy(handler->type, type_start, len);
handler->type[len] = '\0';
}
}
// Simplified bbox: scan for [num,num,num,num]
handler->bbox = NULL;
handler->bbox_size = 0;
char* bbox_start = strstr(content, "\"bbox\":[");
if (bbox_start) {
// Basic: assume 4 numbers; parse roughly
handler->bbox = malloc(4 * sizeof(double));
// Placeholder parse (use sscanf in real impl)
sscanf(bbox_start + 8, "%lf,%lf,%lf,%lf", &handler->bbox[0], &handler->bbox[1], &handler->bbox[2], &handler->bbox[3]);
handler->bbox_size = 4;
}
// Feature count: count "features" array length (simplified string count)
handler->feature_count = 0;
if (strstr(content, "\"FeatureCollection\"")) {
char* features_start = strstr(content, "\"features\":[");
if (features_start) {
char* count_pos = strchr(features_start + 12, '{');
while (count_pos && strstr(count_pos, "\"type\":\"Feature\"")) {
handler->feature_count++;
count_pos = strchr(count_pos + 1, '{');
}
}
}
// Geom type: first occurrence
handler->geom_type = NULL;
char* geom_start = strstr(content, "\"type\":\"");
if (geom_start) {
geom_start += 8;
char* geom_end = strchr(geom_start, '"');
if (geom_end) {
int len = geom_end - geom_start;
handler->geom_type = malloc(len + 1);
strncpy(handler->geom_type, geom_start, len);
handler->geom_type[len] = '\0';
}
}
return handler;
}
void geojson_write(GeoJSONHandler* handler, const char* output_path) {
FILE* f = fopen(output_path, "w");
if (f) {
fputs(handler->content, f);
fclose(f);
}
}
void geojson_print_properties(GeoJSONHandler* handler) {
printf("File Encoding: UTF-8 (JSON-based)\n");
printf("Top-Level Type: %s\n", handler->type ? handler->type : "Unknown");
printf("CRS: WGS 84 (lon/lat order)\n");
printf("Coordinate Precision: Up to 6 decimals recommended\n");
printf("Bounding Box: %s\n", handler->bbox ? "Present (parsed values)" : "Not present");
printf("Properties Object: Present (assumed if Feature)\n");
printf("Identifier: Not present (simplified parse)\n");
printf("Foreign Members: Unknown (full parse needed)\n");
printf("Validity Notes: Assumes no antimeridian crossing; empty geometries treated as null\n");
if (strstr(handler->content, "\"FeatureCollection\"")) {
printf("Feature Count: %d\n", handler->feature_count);
printf("Geometry Types: %s (first found)\n", handler->geom_type ? handler->geom_type : "None");
} else {
printf("Geometry Type: %s\n", handler->geom_type ? handler->geom_type : "Unknown");
}
}
void geojson_close(GeoJSONHandler* handler) {
free(handler->content);
free(handler->type);
free(handler->geom_type);
if (handler->bbox) free(handler->bbox);
free(handler->filepath);
free(handler);
}
int main(int argc, char* argv[]) {
if (argc < 2) return 1;
GeoJSONHandler* handler = geojson_open(argv[1]);
if (handler) {
geojson_print_properties(handler);
// geojson_write(handler, "output.geojson");
geojson_close(handler);
}
return 0;
}