Fast KML processing in Python
fastkml is a Python library for creating, parsing, and modifying Keyhole Markup Language (KML) documents. It provides a fast and efficient way to work with KML files, supporting KML 2.2 specification features like Placemarks, Paths, Polygons, and TimeStamp. The current stable version is 1.4.0, and it maintains an active development pace with regular updates.
Common errors
-
AttributeError: 'Point' object has no attribute 'kml_tag' (or similar geometry attribute error)
cause Attempting to assign a geometry object from a different library (e.g., `shapely.geometry.Point`) directly to a fastkml feature's `.geometry` attribute, instead of a `pygeoif` (fastkml's internal) geometry object.fixConvert your geometry to a `pygeoif` object first. For example, if you have a `shapely_point = shapely.geometry.Point(0, 0)`, create `fastkml_point = fastkml.geometry.Point(shapely_point.x, shapely_point.y)` and assign `placemark.geometry = fastkml_point`. -
fastkml.exceptions.KmlError: Not a valid KML document (or lxml.etree.XMLSyntaxError)
cause The input string or file provided to `kml.from_string()` or `kml.from_file()` is not valid KML XML, or it's empty, malformed, or has incorrect encoding.fixVerify the KML content for correctness and ensure it's a well-formed XML document conforming to the KML specification. Check for correct encoding (usually UTF-8) and ensure the file/string isn't empty. -
TypeError: expected string or bytes-like object, got _io.BufferedReader (or similar type error during parsing)
cause When using `kml.from_file()`, an opened file *object* should be passed, not a file path. When using `kml.from_string()`, a string or bytes object should be passed.fixFor files, ensure you're passing an opened file handle (e.g., `with open('my.kml', 'rb') as f: k.from_file(f)`). For strings, ensure the data is truly a string or bytes. If you have a file path, open it first.
Warnings
- gotcha fastkml uses `pygeoif` objects for geometries (e.g., `fastkml.geometry.Point`). Users accustomed to `shapely` geometries directly will need to convert them to `pygeoif` objects before assigning them to `fastkml` features. While `shapely` objects can often be used to create `pygeoif` objects, direct assignment of `shapely` types will fail.
- gotcha The KML specification requires XML namespaces. When creating KML elements (e.g., `Document`, `Placemark`), you must pass the correct namespace string, typically `{http://www.opengis.net/kml/2.2}`. Forgetting this can lead to malformed KML or issues when parsing with other tools.
- deprecated The `fastkml.alt_kml` module and its classes were deprecated in version 1.0.0. This module provided an alternative, less-standardized way to create KML elements and is no longer recommended or supported.
Install
-
pip install fastkml
Imports
- KML
from fastkml import KML
- Document
from fastkml.features import Document
- Placemark
from fastkml.features import Placemark
- Folder
from fastkml.features import Folder
- Point
from fastkml.geometry import Point
- LineString
from fastkml.geometry import LineString
- Polygon
from fastkml.geometry import Polygon
Quickstart
from fastkml import KML
from fastkml.features import Placemark, Document
from fastkml.geometry import Point
import io
# Create a KML object
k = KML()
ns = '{http://www.opengis.net/kml/2.2}' # KML namespace
# Create a Document and append it to KML
doc = Document(ns, 'mydoc', 'My Document', 'This is a sample KML document.')
k.append_child(doc)
# Create a Placemark and append it to the Document
p = Placemark(ns, 'myplacemark', 'My First Placemark', 'This is a point of interest.')
p.geometry = Point(0, 52.1) # (longitude, latitude)
doc.append_child(p)
# Serialize the KML object to a string
kml_string = k.to_string(prettyprint=True)
print(kml_string)
# To parse from a string:
# kml_parsed = KML()
# kml_parsed.from_string(kml_string)
# for feature in kml_parsed.features():
# if isinstance(feature, Document):
# for sub_feature in feature.features():
# if isinstance(sub_feature, Placemark):
# print(f"Parsed Placemark: {sub_feature.name} at {sub_feature.geometry.coords}")