Quick Start Guide
This guide will get you up and running with KML ORM in just a few minutes.
Installation
Install KML ORM using pip:
pip install kmlorm
Requirements
Python 3.11+
lxml
Note
Working with Folders
KML files often organize placemarks inside folders. By default,
kml.placemarks.all() returns all the placemarks in a document
even though they may be nested below the top level document. Think of kml.placemarks.all()
as roughly equivalent to Django’s Placemark.objects.all() queryset. In Django, the
database is implied. To make a true parallel, the Django statement would be written,
database.Placemark.objects.all(). (Don’t. That won’t work in Django. We are just drawing
comparisons for understanding.)
To retrieve placemarks only at the “root” level, use kml.placemarks.children().
This changed in version 1.0.0. Prior to this version, to get the placemarks at the “root” level, the statement was kml.placemarks.all(), and to get all the placemarks in a file, the statement was kml.placemarks.all(flatten=True).
Document-level only:
kml.placemarks.children()All placemarks (including those in folders):
kml.placemarks.all()
Basic Usage
Loading KML Data
KML ORM can load KML data from files, strings, or URLs:
from kmlorm import KMLFile
# From file
kml = KMLFile.from_file('places.kml')
# From string
kml_string = """<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Placemark>
<name>Test Store</name>
<Point>
<coordinates>-76.5,39.3,0</coordinates>
</Point>
</Placemark>
</Document>
</kml>"""
kml = KMLFile.from_string(kml_string)
# From URL (example with localhost server)
kml = KMLFile.from_url('http://localhost:8000/data.kml')
Accessing Elements
Once loaded, use Django-style managers to access different element types:
# Get placemarks at document root level only
root_placemarks = kml.placemarks.children()
print(f"Found {len(root_placemarks)} root-level placemarks")
# Get ALL placemarks including those nested in folders
all_placemarks = kml.placemarks.all()
print(f"Found {len(all_placemarks)} total placemarks")
# Access different element types at the "root" level
folders = kml.folders.children()
paths = kml.paths.children()
polygons = kml.polygons.children()
points = kml.points.children()
multigeometries = kml.multigeometries.children()
# Or get ALL elements of each type using all()
all_folders = kml.folders.all()
all_paths = kml.paths.all()
all_polygons = kml.polygons.all()
all_points = kml.points.all()
all_multigeometries = kml.multigeometries.all()
# Note: For geometry elements (points, paths, polygons), .all() collects from:
# - Standalone geometries at any level
# - Geometries within Placemarks (e.g., placemark.point)
# - Geometries within MultiGeometry containers
KML Structure Example:
<kml>
<Document>
<Folder>
<Folder>...</Folder> <!-- Nested folder: found by all() -->
<Placemark>...</Placemark> <!-- Nested placemark: found by all() -->
<Path>...</Path> <!-- Nested path: found by all() -->
<MultiGeometry>...</MultiGeometry> <!-- Nested multigeometry: found by all() -->
</Folder>
<Folder>...</Folder> <!-- Direct child folder: found by children() -->
<Placemark>...</Placemark> <!-- Direct child placemark: found by children() -->
<Path>...</Path> <!-- Direct child path: found by children() -->
<MultiGeometry>...</MultiGeometry> <!-- Direct child multigeometry: found by children() -->
</Document>
</kml>
Basic Queries
Filter elements using Django-style query methods:
# Filter by name (root-level placemarks only)
capital_stores = kml.placemarks.filter(name__icontains='capital')
# Filter ALL placemarks including those in folders
all_capital_stores = kml.placemarks.all().filter(name__icontains='capital')
# Exclude items (root-level only)
not_capital = kml.placemarks.exclude(name__icontains='capital')
# Exclude ALL placemarks including those in folders
all_not_capital = kml.placemarks.all().exclude(name__icontains='capital')
# Get a single item (searches root-level only)
store = kml.placemarks.get(name='Capital Electric - Rosedale')
# Get from ALL placemarks including folders
store = kml.placemarks.all().get(name='Capital Electric - Rosedale')
# Check if items exist (root-level only)
has_stores = kml.placemarks.filter(name__icontains='store').exists()
# Check ALL placemarks including folders
has_any_stores = kml.placemarks.all().filter(name__icontains='store').exists()
Note
Important: Searching All vs. Root-Level Elements
The query methods like filter(), exclude(), get(), and exists() operate on the manager’s current elements:
kml.placemarks.children().filter()- Searches only root-level placemarkskml.placemarks.all().filter()- Searches ALL placemarks including nested ones
Since most real-world KML files organize elements in folders, you’ll typically want to use all() before applying filters to search the entire document.
Working with Coordinates
Access coordinate data from placemarks:
for placemark in kml.placemarks.all():
if placemark.coordinates:
print(f"{placemark.name}: {placemark.longitude}, {placemark.latitude}")
Spatial Calculations
Calculate distances, bearings, and midpoints between geographic locations:
from kmlorm.spatial import DistanceUnit
# Get two placemarks
store1 = kml.placemarks.get(name='Store A')
store2 = kml.placemarks.get(name='Store B')
# Calculate distance (default: kilometers)
distance_km = store1.distance_to(store2)
print(f"Distance: {distance_km:.1f} km")
# Calculate in different units
distance_miles = store1.distance_to(store2, unit=DistanceUnit.MILES)
print(f"Distance: {distance_miles:.1f} miles")
# Calculate bearing (compass direction)
bearing = store1.bearing_to(store2)
print(f"Direction: {bearing:.1f}°")
# Find midpoint between locations
midpoint = store1.midpoint_to(store2)
print(f"Midpoint: {midpoint.longitude:.4f}, {midpoint.latitude:.4f}")
# Distance to specific coordinates (tuple or list)
baltimore = (-76.6, 39.3)
distance = store1.distance_to(baltimore)
print(f"Distance to Baltimore: {distance:.1f} km")
Geospatial Queries
Find elements based on location:
# Find placemarks near Baltimore (within 25 km)
nearby = kml.placemarks.near(-76.6, 39.3, radius_km=25)
# Find placemarks within a bounding box
in_area = kml.placemarks.within_bounds(
north=39.5, south=39.0,
east=-76.0, west=-77.0
)
# Only placemarks with coordinates
with_location = kml.placemarks.has_coordinates()
Chaining Queries
Combine multiple query methods for complex filtering:
# Complex query - searches ALL placemarks including nested
result = (kml.placemarks.all() # Use .all() to search nested folders!
.filter(name__icontains='electric')
.near(-76.6, 39.3, radius_km=50)
.has_coordinates()
.order_by('name')
)
for placemark in result:
print(f"- {placemark.name}")
Important
Query methods like filter(), exclude(), get() operate on direct children only by default.
Use .all() first to search nested elements. See Query Method Behavior Reference for details.
Complete Example
Here’s a complete example that demonstrates common usage patterns:
from kmlorm import KMLFile
from kmlorm.core.exceptions import KMLParseError, KMLElementNotFound
def analyze_kml_file(file_path):
try:
# Load the KML file
kml = KMLFile.from_file(file_path)
print(f"Document: {kml.document_name}")
print(f"Description: {kml.document_description}")
print()
# Show element counts
counts = kml.element_counts()
for element_type, count in counts.items():
print(f"{element_type.title()}: {count}")
print()
# Find stores near Baltimore
nearby_stores = (kml.placemarks
.filter(name__icontains='store')
.near(-76.6, 39.3, radius_km=30)
.order_by('name')
)
print(f"Stores near Baltimore ({nearby_stores.count()}):")
for store in nearby_stores:
distance = calculate_distance_to_baltimore(store)
print(f"- {store.name} ({distance:.1f} km away)")
# Get a specific store
try:
rosedale_store = kml.placemarks.get(name__contains='Rosedale')
print(f"\nRosedale store: {rosedale_store.address}")
except KMLElementNotFound:
print("\nNo Rosedale store found")
except KMLParseError as e:
print(f"Error parsing KML file: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
def calculate_distance_to_baltimore(placemark):
# Using built-in spatial calculations
if placemark.coordinates:
# Baltimore coordinates: -76.6, 39.3
baltimore_coord = (-76.6, 39.3)
return placemark.distance_to(baltimore_coord)
return 0
# Run the analysis
if __name__ == "__main__":
analyze_kml_file('example.kml')
Error Handling
Always handle potential errors when working with KML data:
from kmlorm.core.exceptions import (
KMLParseError,
KMLElementNotFound,
KMLMultipleElementsReturned
)
try:
kml = KMLFile.from_file('data.kml')
store = kml.placemarks.get(name='My Store')
except KMLParseError:
print("Invalid KML file")
except KMLElementNotFound:
print("Store not found")
except KMLMultipleElementsReturned:
print("Multiple stores found, be more specific")
Next Steps
Read the Tutorial for more detailed examples
Explore the API Reference for complete API documentation
Check out Examples for real-world use cases