Skip to content

Investigate an unknown DXF

Someone hands you site-plan.dxf. No context, no legend, no idea what’s inside. This tutorial walks the four-tool workflow that turns an opaque drawing into answers, using only the pure-ezdxf tier — no FreeCAD needed.

The workflow: survey → annotate → measure → render. Each step narrows the next one.

Never guess layer patterns blind. Real civil drawings scatter linework across hundreds of layers, including bound-xref names like XREF-PLAN$0$C-PROP-LINE that no pattern you’d invent from scratch will match. Start with the inventory:

{
"tool": "survey_dxf_layers",
"operation": {
"file_path": "/data/site-plan.dxf",
"operation": "survey",
"parameters": { "top": 20 }
}
}

A response looks like:

{
"success": true,
"layers_total": 941,
"layers_with_geometry": 388,
"layers": [
{ "layer": "C-TOPO-MINR", "entities": 14210, "types": { "LWPOLYLINE": 14180, "LINE": 30 } },
{ "layer": "C-PROP-LINE", "entities": 58, "types": { "LWPOLYLINE": 58 } },
{ "layer": "XREF-PLAN$0$C-PROP-BNDY", "entities": 41, "types": { "LINE": 38, "ARC": 3 } }
]
}

Two things to notice: the busiest layer is usually topography noise, and the layers you care about (property lines, road edges) may hide behind bound-xref prefixes. The contains parameter filters by substring when you already know what you’re hunting:

{ "parameters": { "contains": "PROP" } }

Now read what the drawing says about itself. Lot labels, callouts, and notes live in five different places (modelspace, paperspace, block definitions, attribute values, and MTEXT embedded in multileaders), and extract_annotations walks all of them:

{
"tool": "extract_annotations",
"operation": {
"file_path": "/data/site-plan.dxf",
"operation": "annotate",
"parameters": { "pattern": "LOT\\s+\\d+" }
}
}
{
"success": true,
"annotations_found": 74,
"annotations": [
{
"text": "LOT 12\n2.31 AC",
"layer": "C-ANNO-LABL",
"container": "modelspace",
"insert": { "x": 154032.7, "y": 88210.4 }
}
]
}

The pattern regex is matched against both the raw text and a whitespace-normalized single-line form, so LOT\s+\d+ matches multi-line labels too. Drop the pattern entirely to see everything, but on large files set expectations: thousands of strings come back.

Also worth a call here: list_sheets, which enumerates paperspace layouts with paper sizes and title-block text — the fastest way to learn “sheet C4 is the grading plan” without rendering anything.

With the right layer names in hand, ask quantity questions:

{
"tool": "measure_by_layer",
"operation": {
"file_path": "/data/site-plan.dxf",
"operation": "measure",
"parameters": { "layer_pattern": "C-ROAD*" }
}
}
{
"success": true,
"units": "feet",
"rollups": [
{
"layer": "C-ROAD-EDGE",
"total_length": 28412.6,
"closed_area_sqft": 0.0,
"closed_area_acres": 0.0,
"dangling_segments": 12
}
]
}

Zero closed area with nonzero length is normal for open linework like centerlines — the dangling_segments count explains why nothing enclosed. When you do want enclosed area from boundary layers, this tool uses the same noding-and-polygonization pipeline as extract_parcels, so the two reconcile. See Why parcels need polygonization for what’s happening underneath.

Numbers are good; a picture confirms them. Render just the layers you identified:

{
"tool": "render_svg",
"operation": {
"file_path": "/data/site-plan.dxf",
"operation": "render",
"parameters": {
"layer_pattern": "C-PROP*",
"output_name": "property-lines"
}
}
}

The SVG lands in the server’s output directory, and the response reports path, size, and element count. Frozen layers that match your pattern are force-rendered — naming a pattern means you want to see it. A pattern that matches nothing returns an error with similar layer names as a hint, never a silently empty file.

Prefer narrow patterns: rendering a full sheet of a large civil file can take ~10 seconds and produce multi-megabyte SVGs.

An agent working a real file cycles these tools: survey to find candidate layers, render to sanity-check what a layer actually contains, annotate to attach names to geometry, measure to produce the numbers someone asked for. Each response is designed to feed the next call — the survey’s layer names are the render’s patterns, the render’s misses come back with suggestions.

Next: Extract parcels from plat linework, where the boundary layers you just found become closed polygons with acreage.