Task 341: .KML File Format

Task 341: .KML File Format

1. List of all the properties of the .KML file format intrinsic to its file system

Based on the specifications for the KML (Keyhole Markup Language) file format, which is an XML-based format for geographic data visualization, the intrinsic properties include file identification, structure, and metadata characteristics used by file systems and applications to recognize and handle .KML files. These are derived from official standards (e.g., OGC KML 2.2) and format descriptions. Note that KML is text-based (XML), so "intrinsic to its file system" refers to identifiers like extension, MIME type, signatures for file typing, versions, and structural traits that allow file systems to classify and process it (e.g., via magic numbers or content inspection). Here's the comprehensive list:

  • File Extension: .kml
  • MIME Type: application/vnd.google-earth.kml+xml
  • Signatures:
  • Filename extension: kml
  • NARA File Format Identifier: NF00222
  • PRONOM PUID: fmt/244
  • Wikidata Title ID: Q79587
  • Content Signature: Starts with XML declaration (e.g., ), followed by <kml xmlns="http://www.opengis.net/kml/2.2"> or similar namespace
  • Versions: Primarily 2.2 (OGC standard); later version 2.3 available but not separately specified in all registries
  • Structure: XML grammar with tag-based nested elements and attributes; unitary, text-based, structured, symbolic encoding
  • Coordinate Reference System (CRS): Fixed to LonLat84_5773 (geodetic longitude, geodetic latitude, altitude using WGS84 EGM96 Geoid)
  • Content Categories: Dataset, GIS (Geographic Information System)
  • Format Category: Encoding
  • Other Facets: Unitary file structure; supports dynamic remote data incorporation; extension mechanism for custom elements; often distributed uncompressed (or zipped as .KMZ)
  • Production Phase: Final delivery format for visualization in earth browsers (e.g., Google Earth)
  • Relationships to Other Formats: Defined via W3C XML Schema; affinity to GML (Geography Markup Language) for geometry; contained within KMZ (zipped variant)
  • Disclosure: Openly documented; OGC implementation standard
  • Transparency: High; readable with text editors; predictable XML structure
  • Self-Documentation: Supports embedded metadata via XML elements (e.g., <ExtendedData>)
  • External Dependencies: None
  • Key Elements and Attributes (core structural properties defining the XML schema):
  • <kml>: Root element; attributes: xmlns, hint
  • <Document>: Container; attributes: id
  • <Folder>: Grouping; attributes: id
  • <Placemark>: Feature; attributes: id
  • <Geometry>: Abstract; attributes: id (extended by Point, LineString, etc.)
  • <Point>: Geometry; attributes: id; elements: <coordinates>, <extrude>, <altitudeMode>
  • <LineString>: Geometry; attributes: id; elements: <coordinates>, <extrude>, <tessellate>, <altitudeMode>
  • <LinearRing>: Geometry; attributes: id; elements: <coordinates>
  • : Geometry; attributes: id; elements: <outerBoundaryIs>, <innerBoundaryIs>, <extrude>, <tessellate>, <altitudeMode>
  • <MultiGeometry>: Geometry; attributes: id; elements: Multiple <Geometry>
  • <Model>: Geometry; attributes: id; elements: <Location> (with <longitude>, <latitude>, <altitude>), <Orientation> (with <heading>, <tilt>, <roll>), <Scale> (with <x>, <y>, <z>), <Link>
  • <NetworkLink>: Feature; attributes: id; elements: <Link>
  • <GroundOverlay>: Feature; attributes: id; elements: <color>, <drawOrder>, <Icon>, <LatLonBox>
  • <PhotoOverlay>: Feature; attributes: id; elements: <Point>, <ViewVolume>
  • <ScreenOverlay>: Feature; attributes: id; elements: <Icon>, <overlayXY>, <screenXY>
  • <Style>: StyleSelector; attributes: id; elements: <iconstyle>, <labelstyle>, <linestyle>, <polystyle> </polystyle></linestyle></labelstyle></iconstyle>
  • <StyleMap>: StyleSelector; attributes: id; elements: <Pair>
  • <ColorStyle>: Abstract (extended by IconStyle, etc.); attributes: id; elements: <color>, <colorMode>
  • <IconStyle>: ColorStyle; elements: <scale>, <heading>, <Icon>
  • <LabelStyle>: ColorStyle; elements: <scale>
  • <LineStyle>: ColorStyle; elements: <width>
  • <PolyStyle>: ColorStyle; elements: <fill>, <outline>
  • <name>: String content
  • <description>: String/HTML content
  • <visibility>: Boolean
  • <open>: Boolean
  • : String
  • <phoneNumber>: String
  • <Snippet>: String; attributes: maxLines
  • <styleUrl>: anyURI
  • <Region>: Element; elements: <LatLonAltBox>, <Lod>
  • <ExtendedData>: Custom data; elements: , <SchemaData>
  • <TimePrimitive>: Abstract (TimeSpan, TimeStamp)
  • <TimeSpan>: elements: <begin>, <end>
  • <TimeStamp>: elements: <when>
  • <AbstractView>: Abstract (Camera, LookAt)
  • <Camera>: elements: <longitude>, <latitude>, <altitude>, <heading>, <tilt>, <roll>, <altitudeMode>
  • <LookAt>: elements: <longitude>, <latitude>, <altitude>, <heading>, <tilt>, <range>, <altitudeMode>
  • Google Extensions (gx: namespace): <gx:Track>, <gx:MultiTrack>, <gx:Tour>, <gx:Playlist>, <gx:FlyTo>, <gx:Wait>, <gx:AnimatedUpdate>, <gx:SoundCue>, <gx:altitudeMode> (e.g., clampToSeaFloor)
  • Sustainability and Adoption: Widely adopted for GIS visualization; supported in tools like Google Earth, ArcGIS; no licensing/patent issues

These properties enable file systems to identify, validate, and associate .KML files with appropriate applications.

3. Ghost blog embedded HTML JavaScript for drag-and-drop .KML file dump

Here's a self-contained HTML page with embedded JavaScript that can be embedded in a Ghost blog (or any HTML-supported platform). It allows dragging and dropping a .KML file, parses it as XML, and dumps all the properties (elements and attributes from the list above) found in the file to the screen in a structured format (e.g., element name, attributes, text content). It uses the browser's File API and DOMParser for parsing.

KML File Properties Dumper

Drag and Drop .KML File

Drop .KML file here


    

4. Python class for .KML handling

Here's a Python class using xml.etree.ElementTree to open, decode (parse), read, write, and print all properties (recursively dumps elements, attributes, and text from the XML structure).

import xml.etree.ElementTree as ET
import os

class KMLHandler:
    def __init__(self, filename):
        self.filename = filename
        self.tree = None
        self.root = None

    def read(self):
        """Decode and read the KML file."""
        if not os.path.exists(self.filename) or not self.filename.endswith('.kml'):
            raise ValueError("Invalid .KML file")
        self.tree = ET.parse(self.filename)
        self.root = self.tree.getroot()

    def print_properties(self):
        """Print all properties (elements, attributes, text)."""
        if self.root is None:
            raise ValueError("File not read yet")
        self._dump_properties(self.root, 0)

    def _dump_properties(self, node, indent):
        indent_str = '  ' * indent
        print(f"{indent_str}Element: {node.tag}")
        for attr, value in node.attrib.items():
            print(f"{indent_str}  Attribute: {attr} = \"{value}\"")
        text = node.text.strip() if node.text else ''
        if text:
            print(f"{indent_str}  Text: {text}")
        for child in node:
            self._dump_properties(child, indent + 1)

    def write(self, output_filename=None):
        """Write the KML (possibly modified) to file."""
        if self.tree is None:
            raise ValueError("No data to write")
        if output_filename is None:
            output_filename = self.filename
        self.tree.write(output_filename, encoding='utf-8', xml_declaration=True)

# Example usage:
# handler = KMLHandler('sample.kml')
# handler.read()
# handler.print_properties()
# handler.write('output.kml')

5. Java class for .KML handling

Here's a Java class using javax.xml.parsers.DocumentBuilder to open, decode (parse), read, write, and print all properties (recursively dumps elements, attributes, and text). Requires Java XML libraries (standard).

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;

public class KMLHandler {
    private String filename;
    private Document doc;

    public KMLHandler(String filename) {
        this.filename = filename;
    }

    public void read() throws Exception {
        if (!filename.endsWith(".kml")) {
            throw new IllegalArgumentException("Invalid .KML file");
        }
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        doc = builder.parse(new File(filename));
    }

    public void printProperties() {
        if (doc == null) {
            throw new IllegalStateException("File not read yet");
        }
        dumpProperties(doc.getDocumentElement(), 0);
    }

    private void dumpProperties(Node node, int indent) {
        String indentStr = "  ".repeat(indent);
        System.out.println(indentStr + "Element: " + node.getNodeName());
        if (node.hasAttributes()) {
            for (int i = 0; i < node.getAttributes().getLength(); i++) {
                Node attr = node.getAttributes().item(i);
                System.out.println(indentStr + "  Attribute: " + attr.getNodeName() + " = \"" + attr.getNodeValue() + "\"");
            }
        }
        String text = node.getTextContent().trim();
        if (!text.isEmpty()) {
            System.out.println(indentStr + "  Text: " + text);
        }
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                dumpProperties(child, indent + 1);
            }
        }
    }

    public void write(String outputFilename) throws Exception {
        if (doc == null) {
            throw new IllegalStateException("No data to write");
        }
        if (outputFilename == null) {
            outputFilename = filename;
        }
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new File(outputFilename));
        transformer.transform(source, result);
    }

    // Example usage:
    // public static void main(String[] args) throws Exception {
    //     KMLHandler handler = new KMLHandler("sample.kml");
    //     handler.read();
    //     handler.printProperties();
    //     handler.write("output.kml");
    // }
}

6. JavaScript class for .KML handling

Here's a JavaScript class (Node.js compatible, using fs and xmldom for parsing; install xmldom via npm if needed). It can open, decode (parse), read, write, and print all properties to console.

const fs = require('fs');
const DOMParser = require('xmldom').DOMParser;
const XMLSerializer = require('xmldom').XMLSerializer;

class KMLHandler {
    constructor(filename) {
        this.filename = filename;
        this.doc = null;
    }

    read() {
        if (!this.filename.endsWith('.kml')) {
            throw new Error('Invalid .KML file');
        }
        const data = fs.readFileSync(this.filename, 'utf8');
        const parser = new DOMParser();
        this.doc = parser.parseFromString(data, 'application/xml');
    }

    printProperties() {
        if (!this.doc) {
            throw new Error('File not read yet');
        }
        this._dumpProperties(this.doc.documentElement, 0);
    }

    _dumpProperties(node, indent) {
        const indentStr = '  '.repeat(indent);
        console.log(`${indentStr}Element: ${node.tagName}`);
        for (let i = 0; i < node.attributes.length; i++) {
            const attr = node.attributes[i];
            console.log(`${indentStr}  Attribute: ${attr.name} = "${attr.value}"`);
        }
        const text = node.textContent ? node.textContent.trim() : '';
        if (text) {
            console.log(`${indentStr}  Text: ${text}`);
        }
        for (let child = node.firstChild; child; child = child.nextSibling) {
            if (child.nodeType === 1) { // Element node
                this._dumpProperties(child, indent + 1);
            }
        }
    }

    write(outputFilename = null) {
        if (!this.doc) {
            throw new Error('No data to write');
        }
        if (!outputFilename) {
            outputFilename = this.filename;
        }
        const serializer = new XMLSerializer();
        const xmlStr = serializer.serializeToString(this.doc);
        fs.writeFileSync(outputFilename, xmlStr);
    }
}

// Example usage:
// const handler = new KMLHandler('sample.kml');
// handler.read();
// handler.printProperties();
// handler.write('output.kml');

7. C class for .KML handling

Assuming C++ (as "c class" likely implies object-oriented; pure C would use structs/functions). Uses libxml2 for XML parsing (common library; include via #include <libxml/parser.h> and link with -lxml2). The class can open, decode (parse), read, write, and print all properties to console.

#include <iostream>
#include <string>
#include <libxml/parser.h>
#include <libxml/tree.h>

class KMLHandler {
private:
    std::string filename;
    xmlDocPtr doc;

    void dumpProperties(xmlNodePtr node, int indent) {
        std::string indentStr(indent * 2, ' ');
        std::cout << indentStr << "Element: " << node->name << std::endl;

        for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
            std::string value(reinterpret_cast<const char*>(attr->children->content));
            std::cout << indentStr << "  Attribute: " << attr->name << " = \"" << value << "\"" << std::endl;
        }

        std::string text;
        for (xmlNodePtr child = node->children; child; child = child->next) {
            if (child->type == XML_TEXT_NODE && xmlNodeGetContent(child)) {
                text = reinterpret_cast<const char*>(xmlNodeGetContent(child));
                text.erase(0, text.find_first_not_of(" \t\n\r\f\v"));
                text.erase(text.find_last_not_of(" \t\n\r\f\v") + 1);
                if (!text.empty()) {
                    std::cout << indentStr << "  Text: " << text << std::endl;
                }
            }
        }

        for (xmlNodePtr child = node->children; child; child = child->next) {
            if (child->type == XML_ELEMENT_NODE) {
                dumpProperties(child, indent + 1);
            }
        }
    }

public:
    KMLHandler(const std::string& fn) : filename(fn), doc(nullptr) {}

    ~KMLHandler() {
        if (doc) xmlFreeDoc(doc);
    }

    void read() {
        if (filename.substr(filename.find_last_of(".") + 1) != "kml") {
            throw std::runtime_error("Invalid .KML file");
        }
        doc = xmlReadFile(filename.c_str(), nullptr, 0);
        if (!doc) {
            throw std::runtime_error("Failed to parse KML");
        }
    }

    void printProperties() {
        if (!doc) {
            throw std::runtime_error("File not read yet");
        }
        xmlNodePtr root = xmlDocGetRootElement(doc);
        dumpProperties(root, 0);
    }

    void write(const std::string& outputFilename = "") {
        if (!doc) {
            throw std::runtime_error("No data to write");
        }
        std::string out = outputFilename.empty() ? filename : outputFilename;
        xmlSaveFormatFileEnc(out.c_str(), doc, "UTF-8", 1);
    }
};

// Example usage:
// int main() {
//     try {
//         KMLHandler handler("sample.kml");
//         handler.read();
//         handler.printProperties();
//         handler.write("output.kml");
//     } catch (const std::exception& e) {
//         std::cerr << e.what() << std::endl;
//     }
//     return 0;
// }