02CadGis
Architecture & Design
Overview & Design Philosophy
02CadGis converts CAD and GIS exchange files into clean, styled GeoPackage layers. It is built for planning, cadastral, and municipal workflows where AutoCAD DWG/DXF, Netcad NCZ/NCA, KML/KMZ, GML, GeoJSON, CSV/TSV, SpatiaLite, GPX, DGN, FileGDB, and Personal GDB (.mdb) files must become QGIS data quickly and without data loss.
The plugin rests on five principles:
- No silent data loss. Every readable feature is written. Problematic geometries are repaired, not dropped. Empty entities are filtered with a logged warning, never a fatal error.
- Selective conversion. The layer tree shows what is in the file before conversion. Unchecked layers cost zero decode time.
- Format-native parsers where GDAL falls short. The plugin carries its own NCZ parser, DGN v8 reader, CSV sniffer, and MS Access reader — each activating when the corresponding GDAL driver is absent or incomplete.
- Turkish planning data is a first-class citizen. The CRS detector knows every EPSG code in the Turkish survey projection families. The PlanGML engine applies the official Ministry e-Plan symbology. The mojibake repairer recovers Turkish characters from mis-encoded CAD layers.
- Progressive disclosure. The dock shows only the controls relevant to the selected input type. Drag-and-drop auto-detects format and switches tabs.
Three-Engine Architecture
| Engine | Formats | Core Module | Parser |
|---|---|---|---|
| CAD | DXF, DWG, DGN | core/cad_engine.py | GDAL/OGR with CadCleanupEngine post-processing. DWG: 3-tier fallback (GDAL → ODA → LibreDWG). DGN: GDAL → pure-Python fallback. |
| GIS | KML, KMZ, GML, GeoJSON, CSV/TSV, SpatiaLite, GPX, FileGDB, Personal GDB | core/gis_engine.py | GDAL/OGR with format-specific pre-processing. CSV sniffer for delimited text. KMZ multi-document extraction. GDB live zero-copy mode. MS Access pure-Python reader. |
| NCZ v2 | NCZ, NCA | core/ncz_engine/v2/ | Independent block-oriented binary decoder: lazy catalog → selective decode → index cache. Five cooperating modules: binary reader, parser, geometry decoder, attribute decoder, block catalogue. |
Dock Panel & Drag-and-Drop
The dock has three stacked panels: CAD & GIS Converter (source selection, layer tree, output options), Netcad NCZ/NCA Importer (multi-file, metadata, CRS, PlanGML), and CAD & GIS Exporter (DXF/KML/KMZ output). Dropping any supported file onto the dock auto-detects the format and switches to the correct panel. Session preferences — target path, CRS, cleanup options, last-used folders — persist via QSettings.
Theoretical Background
CAD-to-GIS Conversion Theory
The conversion of data between Computer-Aided Design (CAD) and Geographic Information Systems (GIS) environments constitutes a well-recognised interoperability challenge. CAD systems model physical space through geometric primitives — points, lines, arcs, polylines, circles, and blocks — organised into named layers that often encode functional categories implicitly. GIS, by contrast, represents geographic phenomena as features with explicitly typed attribute schemas, governed by a coordinate reference system and stored in geo-relational or object-relational databases [Zhu et al. 2018; Liu et al. 2017].
The fundamental discrepancy arises from divergent data modelling paradigms. CAD employs a drawing-centric model in which geometry is the primary artefact and attributes (colours, line weights, layer names, text annotations) are decoration. GIS employs a feature-centric model in which each spatial object carries a structured set of properties and belongs to a formally typed geometry class. Bridging these paradigms requires solving four sub-problems:
- Geometry extraction — reading coordinate sequences from binary or text formats and constructing valid OGC Simple Features geometries.
- Layer/feature mapping — assigning CAD entities to GIS feature classes based on layer names, block references, or attribute tables.
- Coordinate transformation — resolving the source CRS (often implicit or locally defined in CAD) and transforming to a target CRS suitable for GIS analysis.
- Semantic enrichment — populating GIS attribute tables from CAD annotations, block attributes, embedded tables, or external catalogues.
The C2G framework proposed by Al-Sabban et al. (2022) formalises this as a five-stage pipeline: Pre-processing, Geometry Conversion, Attribute Mapping, Quality Assurance, and Post-processing. 02CadGis implements each stage, with the quality-assurance stage embodied in the collinear simplification engine and the feature-count verification gates that prevent silent data loss [Al-Sabban et al. 2022].
Let \(C = \{(\gamma_i, \ell_i, \alpha_i)\}\) be a CAD drawing, where \(\gamma_i\) is a geometric primitive, \(\ell_i \in L\) its layer assignment from the set of drawing layers \(L\), and \(\alpha_i\) its attribute vector (colour, line weight, text). A CAD-to-GIS converter is a function \(f: C \to G\) where \(G = \{(F_j, A_j)\}\) is a set of GIS feature classes \(F_j\) (each carrying a homogeneous geometry type and a CRS) with attribute schemas \(A_j\), such that every \(\gamma_i\) is assigned to exactly one \(F_j\) and its geometric fidelity is preserved within floating-point tolerance \(\varepsilon\).
Coordinate Reference Systems in CAD/GIS Conversion
Coordinate Reference Systems (CRS) present the most consequential failure mode in CAD-to-GIS conversion. A CAD drawing stores coordinates as absolute numeric values in a local Cartesian space, often with no explicit CRS metadata. When the drawing represents a real-world site, those coordinates are in some projected coordinate system — but identifying which one requires external knowledge.
In Turkish planning practice, the situation is especially acute. The national surveying infrastructure uses two datums (ED50 and TUREF/ITRF96) with two projection conventions (Transverse Mercator with 500,000 m false easting, and 3-degree Gauss-Krüger with zone-prefixed easting), spanning seven central meridians (27° through 45°). This yields 28 possible EPSG codes for projected data, plus UTM variants. A silent wrong CRS shifts data by 100–200 metres — enough to place buildings in the wrong parcel, or a municipal boundary on the wrong side of a river.
The PROJ library formalises coordinate operations as transformation pipelines: chains of elementary steps (inverse projection, datum shift via Helmert or grid, forward projection) each consuming the output of its predecessor [Knudsen & Evers 2017; PROJ Contributors 2024]. The plugin's CRS detector operates before this pipeline, determining the source CRS from drawing metadata and coordinate statistics.
Given a set of \(n\) coordinate pairs \(\{(x_k, y_k)\}_{k=1}^n\) drawn from a Turkish survey projection, and optionally a projection text \(T\) from drawing metadata, determine the unique EPSG code \(e^*\) such that the datum (ED50 or TUREF), projection type (TM or GK), and central meridian \(m \in \{27, 30, 33, 36, 39, 42, 45\}\) are correctly identified. The problem is underspecified when \(|T| = 0\) (no metadata) and the easting does not encode the zone (TM form, 6-digit easting), because easting alone cannot determine the central meridian.
Symbology & Cartographic Standards
The visual styling of geospatial data is governed by the OGC Symbology Encoding (SE) standard (OGC 05-077r4), which defines an XML grammar for specifying how features and coverages are portrayed on maps [OGC 2006]. SE describes five symbolizer types — PointSymbolizer, LineSymbolizer, PolygonSymbolizer, TextSymbolizer, and RasterSymbolizer — each applying a graphic (stroke, fill, marker, font) to a feature filtered by OGC Filter Encoding expressions.
The Turkish Ministry of Environment, Urbanisation and Climate Change defines an official symbology catalogue for spatial plans (the e-Plan gösterim kataloğu), encoded as Styled Layer Descriptor (SLD) files served from a GeoServer instance [T.C. ÇŞB 2024]. Each rule in this catalogue specifies, per plan type (UIP 1/1000, NIP 1/5000, CDP 1/25,000+):
- Fill — RGB colour with optional opacity for polygon tabaka.
- Stroke — line colour, width, and dash pattern.
- Tarama (hatch pattern) — a raster tile applied as a repeating fill pattern, at its native pixel resolution, per the Ministry's registered tile images.
- Label — font, size, placement for textual annotations.
The PlanX Adaptive Symbology Engine (PASE) compiles this catalogue offline into symbology-style.db (888 KB, shipped with the plugin) and matches CAD tabaka names against it at import time. The matching algorithm is longest-token-subsequence: a tabaka name is normalised (uppercase, Turkish character mapping, tokenisation) and compared against all catalogue keys, with plan-type fallback (UIP → NIP → CDP). When an official rule is found, it is applied with full fidelity; when none matches, the legacy PASE keyword catalogue provides sensible defaults. A tabaka that matches nothing receives the empty-code-cell policy (cells left blank, upper group set to DİĞER PLAN ALANLARI) rather than an invented code.
CAD vs. GIS Data Models
The semantic gap between CAD and GIS data models has been extensively studied in the context of BIM/GIS integration [Zhu et al. 2018; Sani & Abdul Rahman 2018]. The table below summarises the key structural differences that a converter must bridge.
| Aspect | CAD (DXF/DWG/DGN/NCZ) | GIS (GeoPackage/Shapefile/PostGIS) |
|---|---|---|
| Geometry model | Drawing primitives: LINE, CIRCLE, ARC, ELLIPSE, SPLINE, TEXT, MTEXT, INSERT (block), POLYLINE, 3DFACE, HATCH | OGC Simple Features: Point, LineString, Polygon, Multi* variants. Curved geometries (CircularString, CurvePolygon) only in extended profiles. |
| Coordinate space | Local Cartesian; CRS information is optional, often absent or vendor-specific (e.g. Netcad SRS id, not EPSG). | Explicit CRS via EPSG/SRID codes. Every geometry carries its CRS or inherits from the layer. |
| Layers as semantics | Layers are drawing organisational tools. A single layer may contain points, lines, polygons, and text simultaneously. Functional meaning is encoded in layer names. | Layers (feature classes) are homogeneous by geometry type. Meaning is encoded in attribute columns with formal types (integer, real, text, date). |
| Attributes | Implicit: colour (ACI/ARGB), line weight, linetype, layer name, block attributes, extended entity data (XDATA). | Explicit: user-defined schema of typed columns, populated per feature. Null values are legal and meaningful. |
| Text | Independent entities with insertion point, rotation, height, and content. Not linked to any geometry. | Labels derived from attribute columns or expressions; rendered by the symbology engine, not stored as geometry. |
| Blocks / Cells | Reusable geometry groups inserted at multiple locations with individual scale, rotation, and attributes. | No direct equivalent. Must be decomposed into constituent geometries or stored as a related table with point references. |
| Topology | Optional; polylines may be open or closed by convention. No enforcement of planar partition. | Optional but well-supported; topology rules (must not overlap, must not have gaps) are enforceable. |
Coordinate Operations and Transformation Accuracy
Coordinate transformation between projected CRS is performed by the PROJ library, which implements geodetic coordinate operations as transformation pipelines: chains of elementary steps (inverse projection → datum transformation → forward projection) that are composed at runtime [Knudsen & Evers 2017]. The accuracy of the transformation depends on the quality of the datum transformation parameters.
In Turkish practice, two datum transformation scenarios are prevalent:
- ED50 to TUREF/ITRF96. These datums differ by approximately 180–200 metres over Turkish territory. The transformation requires a 7-parameter Helmert transformation (3 translations, 3 rotations, 1 scale factor) or a grid-based correction. PROJ provides Helmert parameters for ED50-to-WGS84 and ITRF-to-WGS84 chains; the compound ED50-to-TUREF transformation is computed by chaining through WGS 84 as the pivot datum. Grid-based transformations (e.g. using the Turkish national geoid model TG-03) provide higher accuracy but require the grid file to be installed in PROJ's grid directory.
- 3-degree TM to 3-degree GK (same datum). These are mathematically equivalent projections differing only in false easting (500,000 m vs. zone × 1,000,000 + 500,000 m). The transformation is a simple easting offset: no datum shift is involved. The plugin handles this by selecting the correct EPSG code for the target CRS rather than computing a coordinate transformation.
The plugin's CRS detector avoids the transformation accuracy problem entirely: it identifies the correct source CRS so that PROJ can apply the best available transformation, rather than guessing and potentially applying a wrong or inaccurate shift. When the detector cannot determine the datum with confidence (projection text absent, coordinates in TM form without zone prefix), it leaves the CRS empty rather than assuming TUREF, because a 200-metre error from a wrong datum is far worse than asking the user to verify.
For 2D GIS analysis, the plugin flattens Z coordinates via dropZValue(). Vertical datums (orthometric heights from mean sea level vs. ellipsoidal heights from the reference ellipsoid) are not transformed; Z values are preserved as-is when present, and dropped only when the target GeoPackage layer requires 2D geometry. The GeoPackage standard supports Z and M coordinates in its geometry encoding, but many QGIS analysis tools expect 2D input, making flattening the pragmatic default.
Technical Formulation
Conversion Pipeline Formalization
The 02CadGis conversion pipeline can be expressed as a composition of five transformations. Let a source file \(S\) produce an ordered set of intermediate representations:
where:
- Read (\(\mathcal{R}\)): Parse the source format into a set of layers \(L_k\) each containing an ordered multiset of geometric entities \(\Gamma_k\). For GDAL-supported formats, \(\mathcal{R} = \text{ogr.Open}(S)\). For NCZ, \(\mathcal{R} = \text{NczCatalog}(S.\text{bytes}).\text{index}()\). For delimited text, \(\mathcal{R} = \text{sniff\_delimited\_dataset}(S)\).
- Split (\(\mathcal{S}\)): For CAD formats, partition the single GDAL entities layer by the
LayerorLevelfield. For non-CAD formats, \(\mathcal{S}\) is the identity. Where a CAD layer subset contains mixed geometry types, \(\mathcal{S}\) further partitions into homogeneous geometry type groups. - Clean (\(\mathcal{C}\)): Apply
CadCleanupEngine: duplicate vertex removal (\(\varepsilon_c = 10^{-4}\) map units) followed by collinear simplification (see below). - Transform (\(\mathcal{T}\)): Resolve source CRS \(\text{CRS}_S\) via the detection algorithm, then apply \(\text{QgsCoordinateTransform}(\text{CRS}_S, \text{CRS}_T)\) to all coordinates.
- Export (\(\mathcal{E}\)): Write each homogeneous layer to a GeoPackage table via
QgsVectorFileWriterwithCreateOrOverwriteLayersemantics, applying attribute schema expansion, mojibake repair, and optional PlanGML column population.
Collinear Simplification Algorithm
The collinear simplification implemented in CadCleanupEngine is an iterative greedy algorithm distinct from the Douglas-Peucker polyline simplification [Douglas & Peucker 1973]. While Douglas-Peucker selects a subset of vertices that preserves the line within a perpendicular distance tolerance, the collinear simplifier removes only vertices that lie on or very near the straight line between their immediate neighbours — vertices that contribute no directional information.
A vertex \(v_i\) in a polyline \(P = (v_0, v_1, \ldots, v_{n-1})\) is collinear with tolerance \(\tau\) if the normalised cross product of the incident edge vectors satisfies:
In 2D, \((a_x, a_y) \times (b_x, b_y) = a_x b_y - a_y b_x\). 02CadGis uses \(\tau = 0.02\), corresponding to a deviation angle of approximately \(1.15^\circ\).
The algorithm proceeds iteratively:
Input: Polyline vertices P = [v_0, ..., v_{n-1}], tolerance τ
Output: Simplified polyline P'
1. P' ← copy(P)
2. if |P'| ≤ 3: return P'
3. repeat
4. changed ← False
5. for i = 1 to |P'|-2:
6. compute sin θ_i from P'[i-1], P'[i], P'[i+1]
7. if sin θ_i ≤ τ:
8. remove P'[i]
9. changed ← True
10. break // restart scan after removal
11. until not changed
12. return P'
This algorithm is \(O(kn)\) where \(k\) is the number of vertices removed (at most \(n-3\)). In practice, CAD drawings contain long straight segments with many intermediate construction points, making this pass highly effective — vertex counts drop by 50–90% on typical municipal plan drawings. The simplification preserves polyline topology: endpoints and true corners (where \(\sin\theta_i > \tau\)) are never removed. A preceding duplicate-removal pass (\(|v_i - v_{i-1}| < 10^{-4}\)) handles co-located construction points.
CRS Detection Logic
The CRS detector in core/crs_detect.py implements a decision procedure combining two independent signals: drawing metadata and coordinate statistics. The procedure is formalised below.
Input: Projection text T (optional), sample coordinates C = {(x_k, y_k)}
Output: CRS detection result (epsg, label, confidence, reason)
1. Compute median easting ẽ ← median({|x_k|}) from C
2. Compute median northing ñ ← median({y_k}) from C
3. if ẽ ≤ 180 and ñ ≤ 90: return (4326, "high") // geographic
4. gk_flag ← ẽ ≥ 1,000,000 // zone-prefixed easting
5. Parse T: extract datum d (ED50 / TUREF / WGS84 / None)
6. Parse T: extract zone_width w (3 / 6 / None)
7. Parse T: extract zone_value z (27..45 or 9..15 or 35..38)
8. Compute cm ← central_meridian(z, w)
9. if gk_flag: cm ← int(ẽ / 1,000,000) × 3 // easting overrides text
10. if cm is None and d is None:
11. if not gk_flag: return ("none", "cannot determine zone from 6-digit easting")
12. cm ← int(ẽ / 1,000,000) × 3; d ← "TUREF" // assume modern datum
13. if d is None and not gk_flag: return ("none", "ambiguous datum")
14. epsg ← lookup(d, cm, gk_flag)
15. if ñ ∉ [3,800,000, 4,800,000]: confidence ← "medium"
16. return (epsg, "high" | "medium")
The detector's design philosophy is conservative: when insufficient information exists to name a unique EPSG code, it returns confidence "none" and explains what is missing, rather than guessing. A silent wrong CRS puts data in the wrong place; an explicitly empty CRS forces the user to verify. The northing band check (3.8–4.8M) catches coordinates that are outside Turkey's geographic extent, which typically indicates a non-Turkish projection or a coordinate system error.
NCZ Binary Decoding Methodology
The NCZ Engine v2 employs a two-phase, block-oriented decoding strategy. NCZ files consist of a flat sequence of variable-length blocks, each with a 1-byte kind tag and a 4-byte stored length:
Phase 1 — Index. The file is scanned once. At each block, if the kind is a known geometry block type (21 or 22), a RecordIndex is created storing the block's absolute position, size, geometry type (byte +6), and layer code (byte +7), without decoding any coordinates. If the kind is a container type ({0, 5, 14, 48, 108, 111, 132, 150, 180}), the payload is scanned for embedded geometry records (recognised by a kind byte 21/22 whose bytes at +5 and +6 are equal, confirming the length-prefix echo pattern). Metadata blocks (kind 6, 25, 28) update the accumulating DrawingMetadata.
Phase 2 — Decode. Records whose layer codes are in the selected set are dispatched to the geometry decoder registry. Decoders are registered by numeric type:
| Type | Decoder | Key Offsets |
|---|---|---|
| 1 | decode_point | Position at +8/+16 (northing-first f64 pair); name at G+86/+87 |
| 2 | decode_line | Start at +8/+16; end at size-19/size-11 |
| 3 | decode_circle | Centre at +8/+16; diameter from +50/+66 |
| 4 | decode_arc | Centre at +8/+16; radius at G+86; angles at G+104/+112 |
| 5 | decode_text | Insertion at +8/+16; text at G+97/+98; height at G+86 |
| 6 | decode_symbol | Position at +8/+16; code at G+94; size at G+86 |
| 7 | decode_polyline | Vertex count = (size+1-113-G)/24; vertices at G+113 |
| 9 | decode_compressed_curve | Origin at +8/+16; f32 delta pairs from G+122 |
| 10 | decode_box | Corner at +8/+16; opposite at G+104/+112; rotation at G+120 |
| 11 | decode_map_sheet | Corners at +50/+58 and +66/+74 |
| 12 | decode_triangle | Vertices at +8/+16, +86/+94, +106/+114 |
| 13 | decode_block_reference | Insertion at +8/+16; name at G+86; rotation at G+118 |
| 15 | decode_smart_object | Origin at +8/+16; width/height at +169/+177; angle at +82 (grads) |
Coordinates are stored northing-first (Netcad convention); the QGIS x-coordinate is the second stored f64 value. All reads are bounds-checked by the Cursor class: out-of-range reads return neutral defaults (0, 0.0, empty string) rather than raising, so decoders need no per-field guards. Coordinates outside \([-10^8, 10^8]\) are rejected.
Mojibake Recovery Algorithm
Turkish CAD files suffer from a systematic encoding problem: layer names and text entities containing Turkish characters (ç, ğ, ı, ö, ş, ü and their uppercase forms) are frequently misinterpreted through incorrect codec chains. The term mojibake (Japanese: "character transformation") describes the resulting garbled text.
Three failure modes are common: (a) CAD software writes OEM-codepage bytes (CP1254) that GDAL interprets as UTF-8, producing Latin-1 mojibake; (b) intermediate conversion tools double-encode CP1254 as UTF-8, producing sequences like "PL_KONUT" → "PL_KÜNÜT"; and (c) DXF files use \U+XXXX Unicode escape sequences that must be unescaped.
Input: Raw text string t
Output: Repaired text string t'
1. t' ← unescape_dxf(t) // replace \U+XXXX with chr()
2. if no mojibake markers in t': return t'
3. for src in ("latin1", "cp1252"):
4. for dst in ("utf-8", "cp1254", "iso-8859-9"):
5. try: t_candidate ← t'.encode(src).decode(dst)
6. if t_candidate has no mojibake markers: t' ← t_candidate; goto 8
7. catch: continue
8. // Direct replacement for stubborn byte sequences
9. for (bad, good) in REPLACEMENT_MAP:
10. t' ← t'.replace(bad, good)
11. return t'
The replacement map covers the most stubborn double-encoding patterns: "ç" → "ç", "ö" → "ö", "ü" → "ü", "ÄŸ" → "ğ", "ÅŸ" → "ş", "ı" → "ı", and their uppercase variants. These patterns are the bytewise consequence of CP1254 → UTF-8 double-encoding of Turkish characters and are not naturally occurring in any single encoding.
Geometry Coercion & Type Promotion
CAD layers routinely mix geometry types: a single DXF layer may contain points (text insertion), lines (walls), and polygons (parcels) simultaneously. GIS layers require a single geometry type. The coercion engine resolves this through a hierarchy of conversions:
3D (Z-bearing) geometries are flattened by calling dropZValue(). Curved geometries (CircularString, CurvePolygon) are segmentized via constrainedStraightSegmentedGeometry(). These operations are applied by _coerce_geometry_for_layer() before features are written to memory layers or GeoPackage, ensuring compatibility with the GDAL GeoPackage driver's geometry type constraints.
GML and XML Namespace Handling
GML (Geography Markup Language) is an XML grammar defined by ISO 19136:2007 and updated in ISO 19136-1:2020 for expressing geographical features. Unlike simpler formats (GeoJSON, KML), GML documents carry XML namespace declarations that associate element names with schema definitions. GDAL's GML driver requires these namespaces to be correctly declared and may fail when they are malformed, incomplete, or reference inaccessible schema URLs [ISO/TC 211 2020].
Common GML interoperability failures include: (a) the gml:id attribute being used as a feature ID when the dataset already has a non-integer fid column, causing GDAL to reject features; (b) application schemas defining custom geometry property names that GDAL does not recognise as spatial columns; (c) xsi:schemaLocation attributes pointing to URLs that no longer resolve, causing validation failures that GDAL treats as fatal. The plugin handles (a) by renaming the conflicting fid column to cadgis_fid in GeoPackage output; (b) and (c) are reported as warnings with guidance on pre-processing the GML with an XSLT transform or schema repair tool.
SpatiaLite and GeoPackage: The SQLite Container Duality
Both SpatiaLite and GeoPackage store geospatial data in SQLite database files, but they use incompatible metadata schemas. SpatiaLite records geometry columns in the geometry_columns table with an srid field referencing spatial_ref_sys, using its own spatial indexing (R*Tree over MBR). GeoPackage records geometry columns in gpkg_geometry_columns with an srs_id referencing gpkg_spatial_ref_sys, using the GeoPackage-specific RTree extension [Yutzler & Daisey 2024].
The plugin reads SpatiaLite files as general SQLite sources through GDAL's SQLite driver (which auto-detects the SpatiaLite metadata tables), but writes output exclusively as GeoPackage. This ensures that output files are readable by any OGC-compliant GeoPackage client, not just QGIS. The CRS is always stored in both EPSG authority form (e.g. EPSG:5257) and as a full WKT definition, satisfying both the GeoPackage standard's requirement for authority-based SRS identification and SpatiaLite's preference for PROJ.4/WKT definitions.
Delimited Text: Type Detection and CRS Inference
The CSV sniffer's type detection logic distinguishes between point geometry (X/Y column pairs), WKT geometry (a column containing Well-Known Text strings), and attribute-only tables (no detected geometry). The detection is a cascade of increasing specificity:
Input: Header fields H = [h_0, ..., h_{m-1}], sample data rows S
Output: Geometry profile (x_field, y_field, wkt_field, crs_authid)
1. // Phase 1: WKT column detection (highest priority)
2. for each field h_i in H:
3. if normalize(h_i) in WKT_FIELD_NAMES:
4. wkt_field ← h_i; break
5.
6. // Phase 2: Point geometry detection
7. if wkt_field is empty:
8. for each field h_i in H:
9. if normalize(h_i) in X_FIELD_NAMES and column_values_numeric(S, h_i):
10. x_field ← h_i
11. for each field h_i in H:
12. if normalize(h_i) in Y_FIELD_NAMES and column_values_numeric(S, h_i):
13. y_field ← h_i
14.
15. // Phase 3: CRS inference
16. if x_field and y_field:
17. if normalize(x_field) in {"lon","long","longitude","boylam"}:
18. crs_authid ← "EPSG:4326"
19. elif wkt_field:
20. crs_authid ← "" // CRS is embedded in WKT or must be user-specified
21. else:
22. geometry_type ← "none" // attribute-only table
23. return profile
Numeric validation (_looks_numeric) requires at least one value in the sample to parse as float; a column where all sample values fail to parse is excluded from geometry candidacy. This prevents non-numeric identifier columns from being mistaken for coordinates. CRS inference for point geometry is conservative: only columns with explicitly geographic names (lon/lat/boylam/enlem) receive EPSG:4326; all others leave the CRS unspecified for the user to configure. The WKT path defers CRS detection entirely to the user, since WKT strings may carry their own embedded SRID or may be in any projected system.
CAD Engine
DXF, DWG & DGN Pipeline
The CAD pipeline is: GDAL/OGR read → split by CAD layer → geometry coercion (3D→2D flattening, curve segmentization, single↔multi conversion) → collinear simplification → CRS transform → output.
Layer Splitting & Geometry Coercion
When Split into CAD layers is on, DXF Layer names or DGN Level numbers become separate checkable rows in the layer tree. Geometry coercion handles three compatibility issues: 3D → 2D flattening (Z dropped), curve segmentization (arcs, ellipses, splines → straight segments), and single↔multi conversion (convertToMultiType / convertToSingleType).
Collinear Simplification
The CadCleanupEngine reduces CAD vertex count by 50–90% in two passes: duplicate removal (points within 10−4 map units merged) and collinear removal (iterative — intermediate vertices on straight lines between neighbours are dropped until no more collinear triplets remain). Visual appearance is unchanged; GeoPackage size and render speed improve dramatically.
AutoCAD DWG: GDAL → ODA → LibreDWG
| Tier | DWG Versions | Mechanism |
|---|---|---|
| 1. GDAL libopencad | R2000 and earlier | GDAL's built-in CAD driver reads legacy DWG directly. |
| 2. ODA File Converter | R2004–R2024 | Free CLI tool from Open Design Alliance. Searched in: QSettings, QGIS settings, PATH, environment, Windows registry, common install paths. DWG → DXF via ASCII-safe temp filename → normal DXF read. Interactive setup dialog if not found. |
| 3. LibreDWG | Various | Open-source dwg2dxf fallback when ODA is unavailable. |
DGN v8: Pure-Python Reader
When GDAL's DGNv8 driver is absent, the plugin activates its own pure-Python DGN reader: OLE2 compound document → locate element streams → zlib decompress → parse by element type. Line (type 3), LineString (4), and Shape/Polygon (6) are fully supported. Complex containers and annotations contribute Level/colour/style attributes via placeholder geometry. Elements are grouped by DGN Level — same workflow as DXF.
GIS Engine
KML, GML, GeoJSON, CSV & GDB Pipeline
Format-specific pre-processing → OGR read → layer tree → CRS transform → attribute expansion → output. Each format has tailored pre-processing: KMZ is extracted and every KML document inside is read; KML balloon HTML is expanded into real attribute columns; GroundOverlay elements become georeferenced GeoTIFF files; CSV is auto-sniffed for delimiter, encoding, and geometry columns.
CSV/TSV Auto-Detection
The sniffer samples the first 100 lines and determines: delimiter (comma, tab, semicolon, pipe — most consistent column count wins), encoding (UTF-8 → system locale), X/Y columns (header search for x/lon/longitude/easting and y/lat/latitude/northing with numeric range validation), WKT column (header search for wkt/geom/geometry with WKT keyword check), and CRS suggestion (EPSG:4326 when X∈[−180,180] and Y∈[−90,90]). All values are overridable.
KMZ: Multi-Document & GroundOverlay
Standard OGR reads only doc.kml. 02CadGis extracts the entire KMZ and opens every .kml inside. <GroundOverlay> elements are converted to GeoTIFF via their <LatLonBox> bounds and added as raster layers.
Geodatabase: Live Mode & Catalog Cache
Live mode adds checked layers as zero-copy references — data stays in the .gdb/.mdb file; QGIS reads on demand. Benchmarked at ~0.27 seconds for 4.27M features (two layers from a municipal FileGDB). The catalog cache (core/ogr_catalog_cache.py) fingerprints each file (size + mtime) and caches its layer list locally — reopening an unchanged source is 100×+ faster than reopening the driver.
MS Access (.mdb) Reader
When GDAL's PGeo driver is unavailable, a pure-Python reader opens the .mdb as a Jet database, enumerates spatial tables, and reads features into QGIS layers. Requires the 64-bit Microsoft Access Database Engine ODBC driver.
NCZ Engine v2
Engine Architecture
The v2 engine is a ground-up rewrite — an independent, block-oriented Netcad binary decoder with zero dependency on Netcad software. Verified bit-for-bit identical to the previous engine across a synthetic corpus and validated against a real 8,163-entity municipal drawing.
| Module | File | Role |
|---|---|---|
| Binary Reader | binary.py | Bounds-checked stream. Every read validates offset and length — no buffer overrun possible. |
| Parser | parser.py | File header, block index, dispatch to decoders by block type. |
| Geometry Decoder | geometry.py | Registry of decoders per entity type (point, line, polyline, circle, arc, text, block reference, etc.). |
| Attribute Decoder | attributes.py | Decodes @TAB attribute tables stored inside drawings. |
| Block Catalogue | blocks.py | Reusable geometry definitions — resolves block references by insertion point, scale, and rotation. |
Lazy Catalog & Selective Decode
Selecting a drawing reads only the file header + block index. The layer tree (CAD layer names, entity counts, types) is built from the index — near-instant even for large municipal files. Geometry is decoded only for checked layers at import time. A 50-layer drawing where you need 5 costs only the decode time of those 5.
Fingerprinted Cache (~160×)
Layer catalogs are cached to disk, keyed by absolute path + file size + modification time. Reopening an unchanged drawing shows the layer tree from cache — benchmarked at ~160× faster on a real 1.2 MiB municipal file. Auto-invalidates on file change. Manual reset via "Clear cache" button.
CRS Detection: Turkish Projections
A Netcad drawing stores its own SRS id (e.g. SRS=7936) — this is not an EPSG code. The detector combines two signals:
- Projection text → datum (TUREF vs ED50 vs WGS 84) + zone/meridian hint. TUREF and ED50 differ ~200 m over the same ground — coordinates alone cannot distinguish them.
- Easting magnitude → axis convention. 6-digit easting (~500,000) = TM form. 8-digit easting (e.g. 3,542,000) = Gauss-Krüger form (zone baked into easting).
| Family | TM Form (EPSG) | GK Form (EPSG) | Zones |
|---|---|---|---|
| TUREF | 5253–5259 | 5269–5275 | 27, 30, 33, 36, 39, 42, 45 |
| ED50 | 2319–2325 | 2206–2212 | 27, 30, 33, 36, 39, 42, 45 |
| WGS 84 / UTM | 32635–32638 | — | 35N–38N |
| ED50 / UTM | 23035–23038 | — | 35N–38N |
Batch Import, @TAB Tables & Joins
Multiple NCZ/NCA files can be selected simultaneously. By default, file groups stay separate. Merge geometry types joins layers with the same name across files. @TAB tables (attribute tables stored inside the drawing) are decoded and presented alongside CAD layers; checking both a layer and its matching @TAB performs an automatic attribute join on entity name/label. Optional geometry metrics add length, area, and centroid fields.
PlanGML & Symbology
PlanGML Mode Overview
PlanGML mode (opt-in) transforms a raw CAD drawing into a standards-compliant spatial plan. Three actions occur: (1) tabaka are grouped into the Ministry's official upper groups; (2) PlanGML schema columns are populated from the MPYY UIP database; (3) the official e-Plan symbology is applied — each tabaka drawn with its Ministry-defined fill, stroke, hatch pattern, and label style.
symbology-style.db, 888 KB). Nothing is downloaded; no style server is needed. The gösterim is the official standard, not 02CadGis artwork — see THIRD_PARTY_NOTICES.md.
UIP Tabaka Catalog & Upper Groups
The catalog maps 256 official tabaka to their hierarchy. A documented alias list resolves local municipal spellings onto the official tabaka they represent. Tabaka not in the catalog get empty code cells (not invented codes) and go to DİĞER PLAN ALANLARI — deliberately the only group name not from the Ministry's catalog.
350+ Gösterim Rules & Tarama
The PlanX Adaptive Symbology Engine (PASE) matches each tabaka against the official e-Plan catalog. Each rule specifies: fill colour, stroke, hatch pattern, tarama tile path, plan type applicability (UIP 1/1000, NIP 1/5000, CDP 1/25,000+). The plan type is auto-detected from the file name and can be overridden.
PlanGML Attribute Schema
When PlanGML mode is on, each feature receives 16 official columns: UST_GRUP_ID, UST_GRUP_ADI, ALT_GRUP_ID, ALT_GRUP_ADI, DETAY_GRUP_ID, PLAN_KODU, FONKSIYON_KODU, LEJANT_KODU, KOD, TAM_ADI, GISTERIM, GUSTERIM_ADI, LEJANT, FONKSIYON, KULLANIM, and uip_tabaka (the drawing's original tabaka name, always preserved).
Adaptive Tabaka Matching Algorithm
The PlanX Adaptive Symbology Engine (PASE) uses a longest-token-subsequence matching strategy that is robust to the variations in tabaka naming encountered in real municipal drawings. A tabaka name like "1000_UIP_PL_KONUT_ALANI_POLYGON" must be matched to the official catalogue entry for "PL_KONUT" despite the scale prefix, plan type prefix, geometry type suffix, and Turkish character variations.
The matching algorithm proceeds in four tiers of decreasing specificity:
- Tier 1 — Official e-Plan catalogue (longest-token-subsequence). The layer name is normalised (uppercase, Turkish-to-ASCII mapping
Ç→C, Ğ→G, İ→I, Ö→O, Ş→S, Ü→U, non-alphanumeric to underscore, leading scale/plan-type prefixes stripped), then tokenised on underscores. All catalogue keys (also tokenised) are sorted by decreasing token count and decreasing key length for longest-first priority. Matching checks whether all tokens of a catalogue key appear as a contiguous subsequence in the layer name tokens. When a match is found for the requested plan type, that entry is used; when the token is missing for the requested plan type, the fallback chain (UIP → NIP → CDP or NIP → UIP → CDP or CDP → NIP → UIP) reuses the closest official equivalent. - Tier 2 — Attribute-based matching. If the layer carries PlanGML schema columns populated from a previous import, PASE checks the
PLAN_KODU,FONKSIYON_KODU, andLEJANT_KODUfields against the keyword lists of the legacy PASE catalogue. This tier handles re-imported or enriched layers where the tabaka identity is already known. - Tier 3 — Exact tabaka name match (legacy PASE catalogue). The raw layer name (with Turkish characters normalised) is compared verbatim against every keyword in the legacy catalogue. Both direct match and
"PL_"-prefixed variants are tried, because some municipalities use"KONUT"while others use"PL_KONUT"for the same function. - Tier 4 — Substring keyword match (legacy PASE catalogue). Normalised tokens from the layer name are checked against normalised keywords. A single matching token is sufficient for a match, so
"YESIL_ALAN"matches the"YESIL_ALAN"keyword even if the full layer name is"1000_UIP_ACIK_VE_YESIL_ALANLAR". - Tier 5 — Default fallback. A neutral grey fill with medium opacity and the upper group DİĞER PLAN ALANLARI. The original tabaka name is preserved in all columns that store it, so downstream analysis can filter and reclassify.
Two-Stage Hybrid Rendering Pipeline
Symbology application uses a two-stage strategy that chooses between categorised and single-symbol rendering based on the data content:
Stage 1 — Categorised renderer. If the layer has a field from the PlanGML schema candidate list (uip_tabaka, ALT_GRUP_ADI, TAM_ADI, GISTERIM, etc.) that contains 1–300 unique values, a QgsCategorizedSymbolRenderer is created. Each unique value is independently matched against the symbology catalogues, producing a symbol that reflects that specific tabaka's official appearance. A single-tabaka layer is still categorised (1 category) because the single-value path correctly resolves the tabaka's official symbol, whereas the fallback single-symbol path (Stage 2) matches against the layer name, which for official upper-group names (e.g. "SU_ATIKSU_VE_ATIK_SISTEMLERI") does not contain any keyword and would render grey. This behaviour was corrected in v2.8.1.
Stage 2 — Single-symbol renderer. Applied when no suitable category field exists. The layer name is matched against the catalogues. For polygon layers, the symbol is a multi-layer fill symbol consisting of (a) an optional background fill with the official colour, (b) optional vector hatch pattern layers (one for diagonal/cross patterns, two for cross hatch with perpendicular angles), and (c) a clean outline layer with the official stroke colour and dash style. For official e-Plan rules with tarama tiles, the fill layer is replaced by a QgsRasterFillSymbolLayer rendering the Ministry's pattern image at its native pixel size. For line layers, the symbol respects the official dash pattern vector (e.g. [12, 4, 2, 4] from the SLD) via setCustomDashVector.
Text labelling uses the pick_label_field function, which probes candidate field names ("label", "label_text", "text", "yazi", "kat_adedi", "emsal", etc.) and accepts a candidate only when at least one non-empty value has been observed in the layer. This prevents binding labels to a column that exists but is empty on all features — a failure mode that previously produced silently blank labels. Point layers that carry text render with a subtle 0.6 mm anchor marker so the text's insertion point is visible without competing with the label content.
Parameters
CAD Engine Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
Split into CAD layers | boolean | True | When enabled, each DXF Layer name or DGN Level number becomes a separate checkable row in the layer tree. When disabled, all entities appear as a single layer. |
Collinear simplification | boolean | True | Apply CadCleanupEngine: duplicate vertex removal (< 10−4 map units) followed by iterative collinear vertex removal (cross-product tolerance 0.02). Reduces vertex count 50–90% with no visible geometry change. |
Close polylines | boolean | False | When enabled, polylines whose first and last vertices are within 0.0001 map units are closed into polygons. Useful for CAD drawings where parcels or building footprints are drawn as open polylines. |
Polyline closure tolerance | float (map units) | 0.01 | Maximum gap between polyline endpoints that triggers automatic closure to a polygon. Larger values close more aggressively but may incorrectly close intended open lines. |
ODA File Converter path | file path | (auto-detected) | Path to ODAFileConverter.exe for modern DWG (R2004+) conversion. Auto-detected from QSettings, QGIS settings, PATH, Windows registry, and common install locations. Interactive setup dialog offered if not found. |
DWG target version for ODA | string | "ACAD2018" | Target DXF version for ODAFileConverter. Fallback chain: ACAD2018 → ACAD2013 → ACAD2010 → ACAD2000. Each version is tried in sequence until conversion succeeds. |
GIS Engine Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
Expand KML HTML descriptions | boolean | True | Parse HTML tables and list items from KML <description> balloon content into real attribute columns. Both <tr><td> and <li><b>key</b>: value</li> patterns are recognised. |
Extract GroundOverlay rasters | boolean | True | Convert KML <GroundOverlay> elements to georeferenced GeoTIFF raster layers. Each overlay's <LatLonBox> bounds define the geotransform; CRS is EPSG:4326. |
CSV delimiter | string | (auto-detected) | Field delimiter for CSV/TSV files. Auto-detected from {,, ;, \t, |} by Python's csv.Sniffer with majority-consistency tiebreaking. Manual override available. |
CSV X field / Y field | field name | (auto-detected) | Column names for point geometry coordinates. Auto-detected by matching header field names against known X/Y candidates (x/lon/longitude/easting/boylam/saga and y/lat/latitude/northing/enlem/yukari) with numeric value validation. |
CSV WKT field | field name | (auto-detected) | Column name for WKT geometry. Auto-detected from wkt/geometry/geom/the_geom/shape/wkt_geom. |
CSV source CRS | EPSG code | (auto-detected) | Source CRS for CSV geometry. Auto-set to EPSG:4326 when coordinate field names suggest geographic coordinates (lon/lat). Manual override available. |
CSV encoding | string | utf-8-sig | File encoding. Read with BOM detection (utf-8-sig); errors are replaced rather than raised. |
GDB/MDB Live mode | boolean | False | Load selected layers as zero-copy references instead of converting to GeoPackage. No conversion time or disk space; CRS transformation is not applied. Ideal for browsing large geodatabases. |
OGR catalog cache | boolean | True | Cache multi-layer source catalogs (GDB, MDB, SQLite) locally, keyed by file fingerprint (size + mtime). Controlled by ZERO2CADGIS_OGR_CACHE_DISABLE environment variable. |
NCZ Engine Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
Merge geometry types | boolean | False | When importing multiple NCZ files, merge layers sharing the same name across files into single output layers. When disabled, each file's layers stay separate with file-name prefixes. |
Apply PlanGML schema | boolean | False | Populate the 16-column PlanGML attribute schema for each feature. Matches tabaka names against the official MPYY UIP catalog. Empty cells for unrecognised tabaka (no code invention). |
Apply PlanGML symbology | boolean | False | Apply the official e-Plan gösterim symbology (fill, stroke, hatch, tarama tile, label) via the PlanX Adaptive Symbology Engine. Sourced from the compiled symbology-style.db. |
Plan type | enum | AUTO | Selects the official style set: UIP (1/1000), NIP (1/5000), CDP (1/25,000+), or AUTO (inferred from file/layer name). Controls which plan-type-specific gösterim rules are applied. Falls back through UIP → NIP → CDP when a token is missing for the selected type. |
Calculate geometry metrics | boolean | False | Add geom_len, geom_area, cent_x, cent_y columns computed from each feature's geometry. Length and area are in target CRS units; centroids are point coordinates. |
Import @TAB attribute tables | boolean | True | Decode @TAB attribute tables embedded in the NCZ file. Tables are presented as checkable rows in the layer tree alongside CAD layers. Checking both a layer and its @TAB performs automatic join on entity name. |
NCZ index cache | boolean | True | Cache the decoded drawing metadata, layer catalog, and attribute tables locally (JSON, per-user cache directory). Keyed by SHA-256 of absolute path + (size, mtime_ns) fingerprint. Controlled by ZERO2CADGIS_NCZ_CACHE_DISABLE. |
NCZ selective decode | boolean | True | Decode geometry only for checked layers (v2 engine). Unchecked layers contribute zero decode time. When disabled, all records are decoded (v1-compatible full-parse mode). |
CAD colour styling | boolean | True | Apply ARGB colours from the NCZ drawing to output layers as categorised QGIS renderers. When disabled, layers use default QGIS styling. NCZ colour codes: 0 = by layer, 1 = forced blue, 255 = forced red; layer colours come from LEX.ST2 block. |
Output & Export Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
Output mode | enum | GeoPackage | One of: GeoPackage (permanent .gpkg, CRS transform applied), Scratch (temporary memory layer, CRS transform applied), Live (zero-copy reference, no CRS transform, for GDB/MDB only). |
Target GeoPackage | file path | (user-selected) | Destination .gpkg file. Created if absent; layers are added with CreateOrOverwriteLayer semantics. Existing layers with the same name are replaced. |
Target CRS | CRS | (project CRS) | Target coordinate reference system for GeoPackage and Scratch output modes. Source coordinates are transformed via QgsCoordinateTransform using the PROJ pipeline. |
Export format | enum | DXF | Export format for the exporter panel: DXF (via QgsVectorFileWriter, attributes skipped), KML, or KMZ (KML zipped as doc.kml with DEFLATE compression). |
DXF preserve layer names | boolean | True | Write QGIS layer names as DXF layer names to preserve organisational intent for CAD collaborators. |
KML/KMZ name field | field name | "name" | Attribute column used as the KML <name> element for each exported feature. |
KML/KMZ description field | field name | "description" | Attribute column used as the KML <description> element for each exported feature. |
Output, Export & Styling
GeoPackage, Scratch & Live
| Mode | Persistence | CRS Transform | Best For |
|---|---|---|---|
| GeoPackage | Permanent .gpkg | Yes | Durable deliverables, data exchange, archival |
| Scratch | Temporary memory layer | Yes | Quick inspection, "try before you commit" |
| Live | Zero-copy reference | No | Browsing large GDB/MDB. No conversion time or disk space. |
DXF / KML / KMZ Export
The exporter panel writes any active QGIS vector layer to DXF, KML, or KMZ via QgsVectorFileWriter. DXF preserves layer names and colour intent for CAD collaborators.
CAD Colors, Labels & Mojibake Repair
Netcad ARGB colours are preserved via categorised renderers (one symbol per colour). Text labels select a column that actually contains text (not merely exists while empty). Point layers with text draw a subtle 0.6 mm anchor. The mojibake repair engine applies multi-pass encoding recovery: UTF-8 → CP1254 → CP1252 → ISO-8859-9 → direct character replacement map for stubborn byte sequences. Applied to layer names, attributes, labels, and UI tree items. DXF \U+XXXX escapes are also unescaped.
Interpretation Guide
DXF / DWG Workflow
Step 1: Source selection. Drag a .dxf or .dwg file onto the dock, or click "Browse" on the CAD & GIS Converter tab. For DWG R2004+, the plugin searches for ODA File Converter; if not found, an interactive setup dialog guides installation from opendesign.com. Once configured, the file opens and the layer tree populates.
Step 2: Layer tree inspection. With "Split into CAD layers" enabled (default), each DXF Layer name becomes a row showing its geometry family and feature count. Uncheck layers you do not need — this eliminates their conversion time. The tree is built from GDAL's OGR driver, which reads the header only; feature counts may be −1 (unknown) for large files until conversion begins.
Step 3: Configure options. Keep "Collinear simplification" on unless you need exact vertex-level fidelity (e.g. for legal cadastral boundaries where every vertex matters). The "Close polylines" option is useful when CAD polylines represent parcels or building footprints but were drawn open. Set the tolerance conservatively (0.01–0.05 map units).
Step 4: CRS. DXF/DWG files carry no explicit CRS. The plugin attempts auto-detection from coordinate magnitudes, but in most cases you must select the CRS manually. If the coordinates look like projected values (6–8 digit eastings, 7-digit northings), select the appropriate projected CRS. If the drawing is in arbitrary local coordinates, convert without CRS and georeference later.
Step 5: Output. Choose GeoPackage for durable output or Scratch for quick inspection. Click "Convert". Output layers appear in the QGIS layer tree with CAD colours applied and mojibake-repaired names.
DXF Layer Name Conventions
DXF layer names in Turkish planning practice follow several common conventions that affect how they appear in the layer tree. Understanding these conventions helps in deciding whether to split by layer and which layers to import:
- Standardised MPYY names — layers named after official tabaka codes, e.g.
PL_KONUT,PL_YOL,PL_PARK. These are immediately recognisable to the PlanGML engine and will receive official symbology when PlanGML mode is enabled. The underscore convention is significant:PL_is the MPYY prefix for planning layers;SNR_for boundary/limit layers; numeric prefixes (e.g.100_,200_) are upper-group codes. - Local municipal names — layers with descriptive Turkish names like
KONUT ALANI,MEVCUT YOL,PARK VE YESIL ALAN. These are matched by the PASE keyword catalogue through normalisation and token matching. Turkish characters in layer names are normalised by the mojibake repair engine before matching. - Numeric-only layer names — some CAD operators use layer names like
0,1,2or colour-based names. These layers convey no semantic meaning and must be manually classified after import. The plugin preserves them as-is; the user should rename them in QGIS or use the layer tree to identify their content before conversion. - Null or empty layer names — entities on AutoCAD layer
0(the default layer) or with an empty layer string appear as(no layer)in the tree. These entities are typically construction geometry or drafting artefacts; unless you specifically need them, uncheck this entry.
DGN Level Number Mapping
MicroStation DGN files use numeric levels (1–63 in older versions, up to 65,535 in v8) rather than named layers. When the GDAL DGNv8 driver is available, level names from the DGN's level table are displayed alongside numbers, e.g. Existing Buildings (Level 12). When using the pure-Python fallback reader, level names are extracted from the DGN's named level table if present; otherwise, only level numbers are shown. The plugin groups entities by level exactly as it groups DXF entities by layer name, making the workflow identical once the source is opened.
Handling Large CAD Files
For DXF files exceeding 50 MB or with more than 100 layers:
- Use the layer tree to uncheck all layers, then check only the 5–10 layers you need. This dramatically reduces conversion time because GDAL reads and filters at the driver level.
- Disable collinear simplification if you need every vertex (e.g. for legal cadastral boundaries). For visualisation and analysis, keeping it enabled reduces output file size by 50–90%.
- Use Scratch mode for first inspection — it creates temporary memory layers with no file I/O overhead. Once you've verified which layers contain useful data, re-import to GeoPackage with only those layers checked.
- For DWG files, the ODA File Converter step adds 5–30 seconds depending on file complexity. This cost is paid once per file; the converted DXF is cached in a temp directory and reused for subsequent imports.
DGN Workflow (Detailed)
DGN files require special attention because the GDAL build shipped with standard QGIS does not include the DGNv8 driver. The pure-Python fallback reader handles most planning-grade DGN files but has limitations that affect workflow decisions.
Step 1: Determine whether the fallback reader is needed. If GDAL opens the DGN file successfully (the layer tree shows non-zero feature counts for at least one layer), the GDAL driver is handling the file and full fidelity is available. If GDAL returns zero layers or 0 features, the pure-Python reader activates automatically. The plugin logs which reader is active.
Step 2: Understand fallback reader limitations. The pure-Python reader supports line (type 3), linestring (type 4), and shape/polygon (type 6) elements. Complex cell geometry, text elements as full text (not placeholder points), dimension elements, and custom line styles are not rendered as full geometry. These entities contribute placeholder points with their level, colour, weight, and style attributes preserved. If your workflow requires these element types as full geometry, convert the DGN to DXF using Bentley MicroStation or ODA Drawings Explorer (both free for viewing/conversion) before importing.
Step 3: Polygon detection. The reader classifies shape elements (type 6) as polygons when their vertex ring is closed (first and last points coincident within 10−6 map units). Open shape elements are imported as polylines. If your DGN uses open shapes to represent parcels or building footprints (a common MicroStation convention), those elements will import as lines. Enable "Close polylines" in the CAD options to auto-close them within the specified tolerance.
Step 4: Coordinate filtering. The fallback reader validates coordinates against realistic bounds: x and y must each be in [100,000, 16,000,000] and the absolute difference between x and y must be at least 1.0. This filters degenerate point clouds and corrupted elements. Elements entirely outside these bounds are skipped with a logged count. If legitimate data is being filtered (e.g. very small-scale regional maps with coordinates near the origin), check the coordinate system and consider adjusting the source.
GML and GeoJSON Workflow
GML and GeoJSON are both text-based OGC standard formats but have very different performance and usability characteristics in the conversion pipeline.
GML approach. GML files are XML documents that GDAL parses into a DOM before reading features. For files larger than 50 MB, this DOM construction can be slow and memory-intensive. Strategy: (a) if the GML contains many feature types, uncheck the ones you do not need to reduce the feature count; (b) if GDAL reports schema validation errors, the GML's xsi:schemaLocation URLs may be unreachable — try editing the GML header to remove or fix these URLs; (c) GML files carrying a fid attribute with non-integer values will have that column renamed to cadgis_fid in the output GeoPackage to avoid conflicting with the GeoPackage primary key.
GeoJSON approach. GeoJSON is simpler: it is a single JSON object with a features array. GDAL reads it efficiently, but RFC 7946 mandates WGS 84 (EPSG:4326). If your GeoJSON uses an alternative CRS (a legacy convention), GDAL reads the CRS from the crs member if present. Always verify the CRS after import: if the coordinates look like projected values (6–8 digit eastings) but the layer CRS is EPSG:4326, the GeoJSON was written with non-standard CRS and the coordinates were not reprojected. In this case, manually set the source CRS to the correct projected system and re-import.
NCZ / NCA Workflow
Step 1: File selection. Switch to the "Netcad NCZ/NCA Importer" tab. Use "Select Files" to choose one or more .ncz/.nca files. The plugin indexes each file (reading only the header + block index) and displays its metadata: version, projection text, SRS id, layer count, and entity count. With the v2 index cache enabled, reopening an unchanged file shows this instantly.
Step 2: CRS detection. The projection text is parsed, sample coordinates are read, and the detector proposes an EPSG code with a confidence level and explanation. Green (high confidence) means both datum and zone are certain. Yellow (medium confidence) means one signal is missing (typically the datum — TUREF is assumed for modern data). Red (none) means neither signal is sufficient; you must select manually. Always verify the proposed CRS against your knowledge of the data.
Step 3: Layer selection. The layer tree shows each CAD layer by code, name, record count, and geometry families. Check only the layers you need. @TAB tables appear alongside layers; checking both a layer and its @TAB performs the automatic join. Use "Select All" / "Deselect All" for bulk operations.
Step 4: PlanGML (optional). Enable both "Apply PlanGML schema" and "Apply PlanGML symbology" for planning data. Select the plan type (AUTO usually works, inferring from scale prefixes in the file name). The plugin populates the 16-column schema and styles each feature per the official e-Plan catalogue.
Step 5: Batch options. When importing multiple files, decide whether to merge same-named layers across files ("Merge geometry types") or keep them separate. Geometry metrics (geom_len, geom_area, cent_x, cent_y) are useful for quality checking but add processing time.
KML / KMZ Workflow
Step 1: Source. Drag a .kml or .kmz file onto the dock. KMZ archives are fully extracted; all .kml documents inside are enumerated (not just doc.kml). Multi-document KMZ files have each document's layers prefixed with the document stem to prevent name collisions.
Step 2: Layers. The layer tree shows each KML folder/feature class as a row. KML sources typically carry EPSG:4326 (WGS 84 geographic). Enable "Expand HTML descriptions" to parse balloon content into attribute columns. Enable "Extract GroundOverlay rasters" for aerial imagery or scanned maps embedded as overlays.
Step 3: Output. Choose GeoPackage with a projected target CRS if you need metric geometry for analysis. KML's native geographic coordinates will be transformed. GroundOverlay rasters are written as separate GeoTIFF files in the same directory as the KML source.
GDB / MDB Workflow
Step 1: Source. Select a .gdb directory (File Geodatabase) or .mdb file (Personal Geodatabase / MS Access). The OGR catalog cache makes reopening instant after the first inspection.
Step 2: Mode choice. For browsing, use Live mode: layers load as zero-copy references in ~0.27 seconds regardless of size. For durable deliverables or CRS transformation, use GeoPackage mode. Live mode preserves the source CRS; if you need reprojection, you must use GeoPackage or Scratch.
Step 3: MS Access fallback. If GDAL's PGeo driver cannot open the .mdb, the plugin's pure-Python MS Access reader activates (requires 64-bit Microsoft Access Database Engine ODBC driver). It enumerates spatial tables via pyodbc and reads features directly.
CSV / TSV Workflow
Step 1: Source. Select a .csv, .tsv, or .txt file. The sniffer samples the first 100 lines and auto-detects delimiter, fields, point geometry columns, WKT column, and CRS.
Step 2: Verify detection. Review the auto-detected settings in the UI. The delimiter, X/Y fields, WKT field, geometry type, source CRS, and encoding are all overridable. If the sniffer misidentifies a column, correct it before conversion. The geometry summary line ("Point from 'longitude' / 'latitude'" or "WKT column 'geometry'") confirms what will be imported.
Step 3: Output. Delimited text with point geometry writes a Point layer. WKT geometry preserves whatever type the WKT strings describe (Point, LineString, Polygon, etc.). Files with no detected geometry become attribute-only tables. All output goes to the selected GeoPackage.
PlanGML Workflow
Step 1: Prerequisites. PlanGML mode requires an NCZ drawing whose tabaka names follow the MPYY convention (e.g. PL_KONUT, PL_PARK, PL_YOL). Drawings using arbitrary local naming will have unrecognised tabaka routed to DİĞER PLAN ALANLARI with empty code cells.
Step 2: Enable. On the NCZ Importer tab, check both "Apply PlanGML schema" and "Apply PlanGML symbology". Verify the auto-detected plan type. For files named like 1000_BAHCESARAY_IMAR.ncz the plan type will be detected as UIP.
Step 3: Output verification. After conversion, each feature has 17 columns (16 PlanGML + uip_tabaka). The symbology is applied as a QGIS categorised renderer on the uip_tabaka field when multiple tabaka are present, or a single-symbol renderer for single-tabaka layers. Hatch patterns (tarama) render via raster fill symbol layers at the Ministry's native pixel resolution.
Troubleshooting
| Symptom | Likely Cause | Remedy |
|---|---|---|
| DWG file fails to open | Modern DWG (R2004+) with no ODA File Converter available | Install ODA File Converter from opendesign.com. The plugin's setup dialog guides this. Alternatively, convert the DWG to DXF in AutoCAD or a free viewer. |
| DGN file opens but returns 0 features | DGN v8 file; GDAL build lacks the DGNv8 driver (requires ODA Teigha) | The plugin's pure-Python DGN reader activates automatically. If it also fails, convert to DXF using Bentley MicroStation or ODA Drawings Explorer. |
| NCZ data appears in the wrong location | Wrong CRS selected. Common error: SRS id (e.g. 7936) used as EPSG code. | The SRS id is not an EPSG code. Use the auto-detected CRS or manually select the correct projected CRS for your region. Verify a few known point coordinates after import. |
| Turkish characters garbled (e.g. PL_KÜNÜT instead of PL_KONUT) | Mojibake: CP1254 bytes decoded as UTF-8 by intermediate tool or GDAL | The plugin's mojibake repair engine runs automatically. If some strings remain garbled, the layer name or attribute was corrupted beyond the known recovery patterns. Rename manually in QGIS. |
| KML GroundOverlay images not georeferenced | KML uses <Icon><href> with a relative path that cannot be resolved | Ensure the image file is present in the same directory as the KML. The plugin checks both relative path (including subdirectories) and basename-only fallback. |
| CSV imports with wrong delimiter or no geometry | First 100 lines are header/metadata; data structure differs afterward | Override the auto-detected settings manually. Specify delimiter, X/Y or WKT columns, and CRS explicitly. |
| GDB/MDB fails with "Unable to open" | Missing ODBC driver for .mdb; incompatible GDB version; file locked by another process | For .mdb: install 64-bit Microsoft Access Database Engine. For .gdb: ensure no other process (ArcGIS, ArcCatalog) has the GDB open. Try copying the GDB directory and opening the copy. |
| GeoPackage conversion succeeds but feature count is lower than expected | Features with no geometry (NULL geometry) are filtered. Empty geometries are skipped. | This is by design. Check the plugin's warning log for the count of skipped features. If many features were dropped, inspect the source in its native application to verify geometry presence. |
| PlanGML symbology shows grey (default) for a known tabaka | The tabaka name does not match any keyword in the official or legacy catalogues | Check the exact spelling (including underscores, Turkish characters) against the catalog. If the tabaka is genuinely missing, it receives the DİĞER PLAN ALANLARI group with a neutral grey fill. The original tabaka name is preserved in uip_tabaka. |
| Conversion is slow for a large file | All layers are selected for conversion; collinear simplification is heavy on very complex polylines | Uncheck layers you do not need. The NCZ v2 engine decodes only checked layers. For GDAL-based formats, use Live mode first to inspect the data, then convert only the layers you need. |
Performance Characteristics
The plugin employs a layered caching architecture to minimise repeated work on large datasets. Performance is characterised across three access patterns.
Cold Open (First Access)
On first access, the file must be parsed or opened through the appropriate driver. The dominant cost is I/O, not computation; therefore the plugin reads only what is necessary to build the layer tree. For NCZ drawings, the v2 engine reads just the file header and block index (Phase 1); geometry decoding (Phase 2) is deferred until import. For OGR-based formats, ogr.Open() is the unavoidable cost, but the catalog cache eliminates it on subsequent accesses. For CSV/TSV, the sniffer reads only the first 256 KB to determine structure.
| Format | Typical Cold Open | What Is Read | Bottleneck |
|---|---|---|---|
| NCZ (1.2 MiB, 8163 entities) | ~15 ms (index only) | Block headers + metadata | Sequential file read |
| NCZ full decode (same file) | ~160 ms | All geometry coordinates | f64 unpacking + dict construction |
| DXF (< 10 MiB) | ~1–3 s | Full text parse by GDAL | GDAL DXF driver text processing |
| FileGDB (municipal, 4.27M features) | ~2–5 s (catalog) | Table directory scan | File count walk in .gdb directory |
| KML/KMZ (< 50 MiB) | ~0.5–2 s | Full XML parse by GDAL | XML DOM construction |
| CSV/TSV (any size) | ~0.1–0.3 s | First 256 KB only | csv.Sniffer heuristics |
Warm Reopen (Catalog Cache Hit)
Reopening an unchanged file loads the layer catalog from the fingerprinted JSON cache. No driver is opened; no bytes are read from the source file. The NCZ index cache achieves ~160× speedup on a real 1.2 MiB municipal file (from ~15 ms to ~0.1 ms). The OGR catalog cache achieves 100×+ speedup on multi-layer geodatabases by avoiding ogr.Open() entirely. Cache invalidation is automatic on file size or modification time change; manual invalidation is available through the dock's "Clear cache" button and the ZERO2CADGIS_NCZ_CACHE_DISABLE / ZERO2CADGIS_OGR_CACHE_DISABLE environment variables.
Selective Import
The lazy catalog enables a decode-what-you-need strategy. Checking only 5 of 50 layers in an NCZ drawing decodes only those 5 layers' geometry records — about 10% of the file's entity count. Benchmark shows ~3.4× speedup over full decode for 1 of 5 layers. For GDAL-based CAD formats, the subset string ("Layer" = 'BUILDINGS') is pushed down to GDAL, which filters at read time.
Cross-Format Comparison
The table below summarises structural differences that affect conversion behaviour across the supported formats.
| Property | DXF/DWG | DGN v8 | NCZ/NCA | KML/KMZ | GeoJSON | GML | CSV/TSV | GDB/MDB |
|---|---|---|---|---|---|---|---|---|
| CRS in file | No | Optional | Partial (SRS id + projection text) | Yes (WGS 84) | Yes (WGS 84) | Yes | No | Yes |
| Geometry types per layer | Mixed | Mixed | Mixed | Homogeneous | Homogeneous | Homogeneous | One type | Homogeneous |
| Attributes | Implicit (Layer, colour, linetype) | Implicit (Level, colour, weight, style) | Implicit + @TAB tables | Explicit (HTML description, schema) | Explicit (properties object) | Explicit (feature-member properties) | Explicit (column headers) | Explicit (field definitions) |
| Text support | Native entities | Native elements | Native entities (Type 5) | Placemark name/description | Properties only | Properties only | Column values | Field values |
| 3D geometry | Yes (Z coordinates) | Yes (3D elements) | Yes (Z per vertex) | Yes (altitudeMode) | Yes (Z in coordinates) | Yes (3D geometry types) | Optional (Z column) | Yes (Z-enabled) |
| Curve support | Yes (ARC, ELLIPSE, SPLINE) | Yes (curve elements) | Yes (ARC type 4, compressed curve type 9) | No (segmentized) | No | Yes (Curve, ArcString) | No | Optional (CAD-only) |
| Block/cell support | Yes (INSERT) | Yes (shared cells) | Yes (BlockRef type 13, SmartObject type 15) | No | No | No | No | No |
| Multi-file support | Single file | Single file | Multiple files (batch) | KMZ: multi-KML archive | Single file | Single file | Single file | Directory-based (GDB) |
| Live mode | No | No | No | No | No | No | No | Yes |
| Export supported | DXF | No | No | KML, KMZ | No | No | No | No |
Encoding Recovery & Text Handling
Character Encoding in CAD Files
CAD systems and their exchange formats handle text encoding with varying degrees of rigour. The most severe problems arise in Turkish-language CAD data, where the Turkish alphabet introduces six characters (ç, ğ, ı, ö, ş, ü) not present in ASCII or ISO 8859-1 (Latin-1). The encoding chain from CAD authoring to QGIS display involves multiple transcoding steps, each of which can introduce corruption.
DXF files nominally use the system code page of the authoring machine. Turkish Windows uses code page 1254 (Turkish), but DXF readers often default to ASCII or Latin-1. DXF \U+XXXX Unicode escape sequences can represent any Unicode character but are not universally supported by GDAL's DXF driver. Netcad NCZ files store text in an OEM byte mapping that maps Turkish characters to non-standard byte values (221 → İ, 222 → Ş, 208 → Ğ, etc.).
The mojibake repair engine in core/qgis_compat.py addresses these systematically through a three-stage recovery pipeline:
- DXF unescape — regex replacement of
\U+XXXXand\u+XXXXpatterns with the corresponding Unicode character viachr(int(hex, 16)). This handles CAD software that writes Turkish characters as escapes rather than encoding them in the code page. - Encoding round-trip recovery — for each detected mojibake pattern (Latin-1 characters in the range 0xC0–0xFF that form plausible double-encoding signatures), the engine tries encoding to Latin-1 or CP1252 and decoding as UTF-8, CP1254, or ISO-8859-9. The first candidate that eliminates all known mojibake markers is accepted.
- Direct byte-pattern replacement — a fixed mapping of 8 stubborn byte sequences (
"ç" → "ç", etc.) that survive the round-trip recovery because the original encoding chain was more than two steps deep. These patterns are exhaustive for the CP1254 → UTF-8 double-encoding of all six Turkish characters plus their uppercase forms.
docs/NCZ_FORMAT.md and verified against real municipal drawings. The CP1254/CP1252/UTF-8 mojibake patterns were collected empirically from hundreds of real CAD files and verified by reversible encoding/decoding test cases.
Caching Architecture
The plugin implements two independent, fingerprinted on-disk caches for different data sources, sharing a common design but specialised for their respective access patterns.
NCZ Index Cache
Stores a drawing's decoded metadata (DrawingMetadata), per-layer catalog summaries, and decoded @TAB attribute tables as JSON. The cache key is SHA-256 of the absolute file path; invalidation uses a (size, mtime_ns) fingerprint plus CACHE_VERSION. Cache files live under %LOCALAPPDATA%/zero2cadgis/ncz_index/ (Windows) or $XDG_CACHE_HOME/zero2cadgis/ncz_index/ (Linux).
On a cache hit, the dock builds the layer tree without opening the NCZ file at all. Geometry is still decoded from the file at import time (Phase 2), so the cache never stores coordinate data — it is purely a metadata accelerator. Atomic writes (write to .tmp, then os.replace) prevent corruption on concurrent access.
OGR Catalog Cache
Stores multi-layer OGR source catalogs (layer names, geometry types, feature counts) as JSON. For directory-based datasets (File Geodatabase .gdb), the fingerprint aggregates file count, total byte size, and newest modification time across up to 4096 entries (to keep the stat walk cheap). For single-file datasets (.mdb, .sqlite, .gml), the fingerprint is simply (size, mtime_ns).
The OGR catalog cache is critical for multi-layer geodatabases where ogr.Open() on a large .mdb or .gdb takes several seconds just to enumerate layers. With a warm cache, the layer list appears instantly, and the driver is only opened when the user initiates conversion.
GDAL/OGR Driver Fallback Chain
Not all GDAL builds include all drivers. Standard OSGeo4W QGIS builds ship with the CAD (DXF), KML, GML, GeoJSON, GPKG, SQLite, OpenFileGDB, and CSV drivers, but may lack the PGeo (Personal Geodatabase), DGNv8, or libopencad (DWG) drivers. The plugin detects missing drivers and activates its own readers:
| Format | GDAL Driver | Availability in Standard QGIS | 02CadGis Fallback |
|---|---|---|---|
| DWG (R2000 and earlier) | libopencad (CAD) | Usually present | — |
| DWG (R2004+) | libopencad (CAD) | Usually fails on modern DWG | ODA File Converter → DXF → GDAL DXF driver. LibreDWG dwg2dxf as tertiary fallback. |
| DGN v8 | DGNv8 | Not included (requires ODA Teigha) | Pure-Python DgnV8Reader: OLE2 → zlib decompress → element parse |
| Personal GDB (.mdb) | PGeo | Sometimes absent | MsAccessDbReader: pyodbc → ODBC → spatial table scan |
| NCZ/NCA | None | No GDAL driver exists | NCZ Engine v2: pure-Python block-oriented binary decoder |
The fallback chain for each format is exercised automatically. The DGN v8 reader probes the OLE2 signature and falls back silently; the MS Access reader checks pyodbc availability and installed ODBC driver names; the ODA File Converter is location-discovered through a 7-step search. Users see only a clear error message when all tiers fail, with guidance on installing the missing component.
Spatial Data Quality and Validation
CAD-to-GIS conversion introduces several classes of potential data quality degradation. The plugin implements validation checks and corrective measures at each stage of the pipeline to minimise information loss.
Geometry Validity
QGIS memory providers and the GeoPackage driver enforce OGC Simple Features geometry validity rules. Invalid geometries (self-intersecting polygons, non-closed rings, zero-length segments) are rejected by the data provider. The plugin applies pre-write geometry repair through the coercion engine: curve segmentization eliminates curved geometries that are not supported by Simple Features; Z-coordinate dropping eliminates 3D geometries incompatible with 2D-only layers; single-to-multi conversion ensures type compatibility. The add_features_or_raise helper (in core/qgis_compat.py) wraps dataProvider().addFeatures() with a verification step: it compares the feature count before and after the add, and if fewer features were accepted than submitted, it attempts a second pass with geometry coercion (drop Z, segmentize curves, convert multi/single type) before raising a descriptive error.
Coordinate Range Validation
All decoded coordinates are validated against realistic bounds. The NCZ engine's Cursor class enforces \([-10^8, 10^8]\) for all coordinate values; values outside this range are rejected. The DGN v8 reader additionally validates that coordinates fall within [100,000, 16,000,000] and that the absolute difference between x and y coordinates is at least 1.0 — filtering out degenerate point clouds. The CRS detector's northing band check (3,800,000–4,800,000 m) catches coordinates that are in the correct numeric range but outside Turkey's geographic extent, flagging them as medium-confidence detections.
Feature Count Verification
Every stage that constructs QGIS layers tracks the number of features submitted vs. accepted. The conversion pipeline reports a summary: total entities in source, entities decoded (for NCZ selective decode), entities written to output, and entities skipped (with reasons). The NCZ v2 engine tracks unsupported geometry types in catalog.unsupported (a dict of {type: count}) and reports them in the parse result. Empty geometries and degenerate features (zero-length lines, zero-area polygons) are filtered with logged warnings, never as fatal errors.
Attribute Null Handling
CAD files frequently have attributes that exist on some entities but not others. The plugin preserves this faithfully: missing attributes become NULL in the GeoPackage output. The mojibake repair and PlanGML schema population operate only on non-null string values. The PlanGML empty-code policy deliberately leaves cells blank rather than filling them with invented values, maintaining a clean distinction between "this tabaka is in the catalog" and "this tabaka was unrecognised."
CRS Validation
After conversion, the output GeoPackage's CRS is verified to be valid and appropriate. The _effective_source_crs method applies a precedence chain: layer CRS (if valid and not EPSG:4326 when coordinates are clearly metric), explicit user-provided source CRS, coordinate-based auto-detection, target CRS, project CRS, and finally a safe default (EPSG:5253 for metric coordinates, EPSG:4326 for geographic). At each level, the CRS is validated before use; an invalid CRS at any level causes fallthrough to the next. This chain ensures that a CRS is always available, even when every signal fails, without silently applying a wrong one.
Memory Management for Large Datasets
Large CAD drawings and geodatabases present memory pressure challenges. The plugin uses several strategies to keep memory usage bounded:
- Streaming writes. Features are written to GeoPackage in batches via
add_features_or_raise. The NCZ v2 engine materialises decoded entities as Python dicts for the selected layers only; the CAD engine uses GDAL's iterator-basedgetFeatures()which reads one feature at a time from the driver. For the DGN v8 pure-Python reader, features are batched at 50,000 per write to keep the in-memory feature list bounded. - Lazy decoding. The NCZ v2 engine's Phase 1 (index) reads only block headers, not geometry. A 1.2 MiB file with 8,163 entities produces a catalog of ~200 record indices (a few KB). Geometry is decoded on demand for checked layers only.
- Live mode. For GDB and MDB sources, live mode adds zero-copy references. QGIS reads features lazily from the source file using GDAL's random-access capability; no features are materialised in plugin memory. A 4.27M-feature municipal FileGDB loads its two layers in ~0.27 seconds with negligible memory overhead.
- Temp file cleanup. Temporary directories created for KMZ extraction, DWG-to-DXF conversion, and KML/KMZ export are cleaned up via
shutil.rmtreein the engine'scleanup()method. The dock calls this after each conversion completes. Temp directories use the system temp path (tempfile.mkdtemp); leftover directories from crashes are harmless but consume disk space. - Catalog caches. Both the NCZ index cache and OGR catalog cache store only metadata (layer names, types, counts), not geometry. Cache files are typically < 10 KB for NCZ and < 5 KB for OGR sources.
Concurrency and Thread Safety
QGIS plugins run in the main thread, and all GUI operations must be dispatched on the Qt event loop. The conversion engines are single-threaded and not designed for concurrent use. However, the cache modules (cache.py, ogr_catalog_cache.py) are designed for multi-process safety: writes use atomic rename (os.replace of a tempfile), which is guaranteed atomic on POSIX and NTFS. Multiple QGIS instances reading the same cache files will not corrupt each other, though they may experience a stale read if one instance updates a cache entry while another reads it — a harmless condition that self-corrects on the next access when the fingerprint mismatch triggers a rescan.
DGN v8: OLE2 Compound Document Structure
The pure-Python DGN v8 reader operates on the OLE2 (Object Linking and Embedding) Compound Document format that MicroStation uses as its file container. Understanding this container is essential for diagnosing read failures.
An OLE2 file is a FAT (File Allocation Table) file system within a file. It contains: (a) a header (512 bytes) with magic number D0 CF 11 E0, sector size, and FAT sector count; (b) a directory (structured storage) enumerating named streams (equivalent to files); (c) a FAT chain linking data sectors; and (d) stream data in 512-byte or 4096-byte sectors. The DGN reader opens the OLE2 container, locates the design file stream, and passes its decompressed contents to the element parser.
Element data in DGN v8 is stored as a sequence of variable-length records. Each element begins with a 2-byte type field (line = 3, linestring = 4, shape = 6, text = 7/17) followed by a 2-byte level number, attributes (colour, weight, style), and coordinate data. Polygons are distinguished from polylines by element type: shape elements (type 6) whose vertex ring is closed (first and last points coincident within 10−6 map units). Complex elements (shared cells, text nodes, dimension elements) are represented as placeholder points with level/colour/style attributes preserved.
The reader handles two DGN v8 sub-formats: standard binary encoding and compressed (zlib) encoding. The olefile library (BSD-licensed, vendored) handles OLE2 parsing; zlib decompression is done via Python's built-in zlib module. Elements containing Unicode strings use the standard MicroStation UCS-2 or UTF-16 encoding in the element attributes.
KMZ Multi-Document Extraction Theory
The KMZ format is a ZIP archive (using DEFLATE compression) with a .kmz extension containing one or more KML documents and their referenced resources (images, 3D models, icons). The OGC KML 2.3 standard specifies that the root document must be named doc.kml, but does not restrict additional KML documents [OGC 2015]. Google Earth, the reference implementation, reads only doc.kml; any other KML files in the archive are ignored unless explicitly linked from the root document.
02CadGis departs from this convention to support multi-document KMZ files commonly produced by municipal GIS exports, where each thematic layer is stored as a separate KML document. The extraction algorithm:
- Extracts the entire KMZ to a temporary directory via
zipfile.ZipFile.extractall(). - Enumerates all
.kmlfiles (case-insensitive) in the extraction tree. - Prefers
doc.kmlas the primary document; if absent, chooses the first KML in sorted order. - Returns the primary document path for immediate GDAL reading; the remaining documents are accessed by
_kml_docs(), which walks the extraction directory and orders documents with the primary first. - In
_ogr_sources(), each KML document becomes an OGR source with a prefix derived from its stem ("boundaries.kml"→ prefix"boundaries_"). When only one document exists, the prefix is empty for backward compatibility.
This approach ensures that no KML data inside a KMZ is silently dropped. The multi-document prefix keeps layer names distinct when two documents define a layer with the same name. GroundOverlay images referenced by relative paths in any KML document are resolved against that document's location in the extraction tree.
DWG Conversion: ODA File Converter Discovery Algorithm
The search for ODAFileConverter.exe is exhaustive and ordered by decreasing likelihood, ensuring that the most common installation paths are checked first while still covering edge cases:
- QSettings (plugin custom path). The value of
zero2cadgis/oda_converter_pathis checked first; this is set when the user manually locates the executable through the interactive setup dialog. - QgsSettings (QGIS native settings). Three keys are probed:
/qgis/odaFileConverterPath(QGIS general setting),/dwg/odaConverterPath(DWG-specific setting), and/Processing/Configuration/ODA_FILE_CONVERTER_PATH(Processing framework setting). These capture installations configured through QGIS's own DWG import workflow. - Environment variables.
ODA_FILE_CONVERTER_PATH,ODA_PATH, andODA_CONVERTERare checked. These allow CI environments and containerised QGIS instances to point to the converter without interactive setup. - System PATH.
shutil.which("ODAFileConverter.exe")checks the executable search path. This covers installations that added the ODA directory to PATH during setup. - Static install paths. Five known installation directories under
C:\Program FilesandC:\Program Files (x86)are probed:ODA\ODAFileConverter\,ODAFileConverter\, andC:\ODA\ODAFileConverter\. - Glob search. A shallow glob in Program Files directories for
ODAFileConverter*directories captures versioned installations (e.g.ODAFileConverter 25.6.0). - Windows Registry. Both
HKLMandHKCUare searched underSOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall(and WOW6432Node for 32-bit on 64-bit) for entries whoseDisplayNamecontains "oda". Matching entries'InstallLocationvalues are checked for the executable. Direct registry keysSOFTWARE\OpenDesignandSOFTWARE\ODAare also probed.
The ODA File Converter invocation uses a safe ASCII filename (input_converted.dwg) to avoid Unicode/Turkish filename issues with the CLI tool. The target DXF version is attempted in descending order (ACAD2018 → ACAD2013 → ACAD2010 → ACAD2000) to maximise compatibility with GDAL's DXF reader. Each version is tried independently; the first successful conversion that produces a non-empty DXF file is used.
Format History and Provenance
| Format | Introduced | Standardising Body | Current Version | Status |
|---|---|---|---|---|
| DXF (Drawing Exchange Format) | 1982 | Autodesk (de facto) | R2024 (proprietary) | Active; widely supported |
| DWG | 1982 | Autodesk (proprietary) | R2024 | Active; requires external converter for modern versions |
| DGN v8 | 2000 | Bentley Systems | v8i / CONNECT | Active; GDAL driver requires ODA Teigha |
| NCZ/NCA | ~1990s | Netcad (proprietary) | Varies by producer version | Active in Turkey; no public specification |
| KML | 2004 (Keyhole) | OGC (since 2008) | 2.3 (OGC 12-007r2) | Active OGC standard |
| KMZ | 2004 (Keyhole) | OGC (KML 2.2 Annex) | ZIP container for KML | Active; de facto standard |
| GeoJSON | 2008 | IETF (RFC 7946, 2016) | RFC 7946 | Active IETF standard |
| GML | 2000 | OGC / ISO TC 211 | 3.2.1 (ISO 19136:2007) | Active ISO/OGC standard |
| GeoPackage | 2014 | OGC | 1.4.0 (OGC 12-128r19) | Active OGC standard |
| File Geodatabase | 2006 | Esri (proprietary) | ArcGIS 10.x / Pro | Active; GDAL OpenFileGDB driver reads v9/v10 |
| Shapefile | ~1994 | Esri (de facto, 1998 whitepaper) | Stable since 1998 | Legacy; superseded by GeoPackage |
| SpatiaLite | 2008 | Alessandro Furieri | 5.x | Active; OGC adopted SpatiaLite GeoPackage extension |
Format Reference
Complete Format Matrix
| Format | Extension | Engine | Layer Split | Live | Export |
|---|---|---|---|---|---|
| AutoCAD DXF | .dxf | CAD | ✓ | ✓ | |
| AutoCAD DWG | .dwg | CAD | ✓ | ||
| MicroStation DGN v8 | .dgn | CAD | ✓ | ||
| Netcad NCZ | .ncz | NCZ | ✓ | ||
| Netcad NCA | .nca | NCZ | ✓ | ||
| KML | .kml | GIS | ✓ | ||
| KMZ | .kmz | GIS | ✓ | ||
| GML | .gml | GIS | |||
| GeoJSON | .geojson | GIS | |||
| CSV / TSV / TXT | .csv, .tsv, .txt | GIS | |||
| SpatiaLite / SQLite | .sqlite, .db | GIS | |||
| GPX | .gpx | GIS | |||
| File Geodatabase | .gdb | GIS | ✓ | ||
| Personal Geodatabase | .mdb | GIS | ✓ |
Format-Specific Notes
- DWG R2004+ requires ODA File Converter or LibreDWG — neither is bundled. The plugin searches for them and guides the user through setup.
- DGN v8 pure-Python reader does not render complex cell geometry or text as full geometry — represented as placeholder points. Full fidelity requires the GDAL DGNv8 driver.
- NCZ/NCA Engine v2 is validated against real municipal drawings. Drawings from very old or very new Netcad versions may use unregistered block types — these are skipped with a warning.
- KMZ Only KML documents are read; other archive contents are ignored unless referenced by GroundOverlay.
- GDB/MDB Live mode uses native CRS without transformation. For reprojection, use GeoPackage or Scratch mode.
- CSV/TSV The sniffer reads the first 100 lines. Geometry columns beyond line 100 must be selected manually.
OGR Format Driver Reference
The following table lists the GDAL/OGR drivers used for each format, their availability in standard OSGeo4W QGIS builds, and any known limitations.
| Format | OGR Driver | Read Support | Write Support | CRS Awareness | Known Limitations |
|---|---|---|---|---|---|
| DXF | DXF | Full | Partial (via QgsVectorFileWriter) | No | Curved entities segmentized; HATCH entities read as outlines; MTEXT not fully parsed |
| DWG (R2000) | CAD (libopencad) | Full | No | No | R2004+ files return 0 features; ODA File Converter needed |
| DGN v7 | DGN | Full | No | Optional (header) | v8 files fail; DGNv8 driver needed for modern files |
| DGN v8 | DGNv8 | Full (if available) | No | Optional | Requires ODA Teigha libraries; not in standard QGIS GDAL builds |
| KML | KML (LIBKML) | Full | Full | EPSG:4326 only | Large files (>100 MB) may exceed DOM memory limits |
| KMZ | LIBKML (via ZIP extraction) | doc.kml only | Yes (via QgsVectorFileWriter) | EPSG:4326 only | GDAL reads only doc.kml; 02CadGis reads all KML documents |
| GML | GML | Full | Full | Full | Namespace-sensitive; malformed schemas or inaccessible schemaLocation URLs may fail |
| GeoJSON | GeoJSON | Full | Full | EPSG:4326 only (RFC 7946) | Non-WGS84 CRS is a convention extension, not guaranteed interoperable |
| CSV/TSV/TXT | CSV (delimitedtext in QGIS) | Full | Full | User-specified or auto-detected | Sniffer reads first 256 KB; geometry columns beyond this must be manually selected |
| SpatiaLite | SQLite | Full | Full (GPKG for output) | Full | Reads SpatiaLite metadata; output always GeoPackage, not SpatiaLite |
| GPX | GPX | Full | No | EPSG:4326 only | Track segments, waypoints, and routes read as separate layers |
| File Geodatabase | OpenFileGDB | Full (v9/v10) | No | Full | Pre-v9 GDBs fail; Esri proprietary extensions unsupported; directory-based |
| Personal Geodatabase | PGeo | Full (if ODBC available) | No | Full | Requires 64-bit MS Access Database Engine; .accdb support varies by ODBC driver version |
| NCZ/NCA | None (proprietary) | N/A | N/A | Partial (SRS id + projection text) | No GDAL driver exists; 02CadGis NCZ Engine v2 is the only open-source reader |
Detailed Format Comparison: Geometry Support
| Geometry Type | DXF/DWG | DGN v8 | NCZ/NCA | KML/KMZ | GeoJSON | GML | GPKG Output |
|---|---|---|---|---|---|---|---|
| Point | POINT, TEXT, MTEXT, INSERT | Type 2 (point), 7 (text), 17 (text node) | Type 1 (point), 5 (text), 6 (symbol), 13 (block ref) | Point Placemark | Point | gml:Point | Point / MultiPoint |
| LineString | LINE, POLYLINE, LWPOLYLINE | Type 3 (line), 4 (linestring) | Type 2 (line), 7 (polyline), 9 (compressed curve) | LineString Placemark | LineString | gml:LineString, gml:Curve | LineString / MultiLineString |
| Polygon | LWPOLYLINE (closed), HATCH, 3DFACE | Type 6 (shape, closed) | Type 7 (closed polyline), 10 (box), 11 (map sheet), 12 (triangle), 15 (smart object) | Polygon Placemark | Polygon | gml:Polygon, gml:Surface | Polygon / MultiPolygon |
| Circle/Arc | CIRCLE, ARC | Type 12 (arc), 14 (ellipse) | Type 3 (circle), 4 (arc) | N/A (must be segmentized) | N/A | gml:Circle, gml:Arc | Segmentized to LineString |
| Spline/Curve | SPLINE | Type 24 (B-spline), 25 (curve) | Type 9 (compressed curve) | N/A | N/A | gml:BSpline, gml:Bezier | Segmentized to LineString |
| Text/Label | TEXT, MTEXT, ATTDEF | Type 7 (text), 17 (text node) | Type 5 (text) | Placemark name/description | feature.properties | gml:featureMember attributes | Point + label column |
| Block/Cell/Insert | INSERT | Type 34 (shared cell), 35 (shared cell def) | Type 13 (block ref), 15 (smart object) | N/A | N/A | N/A | Decomposed or placeholder Point |
Format Size Characteristics
The following table provides guidance on expected file sizes and practical limits for each format, based on real-world municipal and planning datasets.
| Format | Typical Size Range | Practical Upper Limit | Encoding Efficiency | Notes |
|---|---|---|---|---|
| DXF (ASCII) | 100 KB – 500 MB | ~1 GB (GDAL memory) | Low (text-based, verbose) | ASCII DXF is 5–10× larger than binary equivalent. Binary DXF is rare. |
| DWG | 100 KB – 200 MB | ~500 MB (then ODA conversion) | High (binary, compressed) | DWG is compact; ODA DXF intermediate may be larger. |
| DGN v8 | 50 KB – 100 MB | ~200 MB (pure-Python reader memory) | Medium (binary with optional zlib compression) | Compressed DGN files are smaller. Element count, not byte size, determines read time. |
| NCZ/NCA | 50 KB – 20 MB | ~50 MB (municipal drawings) | High (binary, compact records) | Typical municipal drawing: 1–5 MB, 5,000–15,000 entities. |
| KML | 1 KB – 200 MB | ~64 MB (GroundOverlay scan limit) | Low (XML, verbose) | KML over 64 MB skips GroundOverlay scan for safety. |
| KMZ | 5 KB – 500 MB | ~1 GB (ZIP extraction) | Medium (ZIP compressed KML) | KMZ compression typically 5–10×. Temp extraction needs disk space. |
| GeoJSON | 1 KB – 500 MB | ~1 GB (GDAL memory) | Low (text-based) | GeoJSON is the least compact vector format. Use GeoPackage for large datasets. |
| GML | 10 KB – 1 GB | ~2 GB (DOM memory) | Very low (XML, extremely verbose) | GML can be 10–50× larger than equivalent GeoPackage. Schema validation adds overhead. |
| GeoPackage | 1 KB – 10 GB | ~140 TB (SQLite maximum) | High (binary, SQLite storage) | Recommended output format. Efficient storage, spatial index, portable. |
| File Geodatabase | 1 MB – 100 GB | ~1 TB (directory-based) | High (binary, Esri-proprietary) | Live mode avoids materialising. Conversion copies all features. |
| CSV/TSV | 1 KB – 1 GB | ~2 GB (GDAL text scan) | Very low (text-based, no compression) | Sniffer reads only first 256 KB. For files >100 MB, use database import instead. |
Interoperability Notes
- DXF to GeoPackage round-trip. DXF exported from 02CadGis preserves layer names but loses CAD-specific properties (colour index, linetype, block definitions). It is intended for GIS collaborators, not CAD round-tripping. For full-fidelity CAD exchange, use the original DWG/DXF.
- KML to GeoPackage CRS conversion. KML's fixed EPSG:4326 means all coordinates are in decimal degrees. Converting to a projected CRS (e.g. TUREF / TM39) requires a datum transformation from WGS 84 to the target datum, which introduces a small error (typically <1 m for Helmert transformation). For high-precision cadastral work, verify that the source KML coordinates meet your accuracy requirements before projection.
- GeoJSON and non-WGS84 CRS. RFC 7946 mandates WGS 84 (EPSG:4326). GeoJSON files with alternative CRS (a legacy convention from the 2008 specification) will be read by GDAL but may not be interoperable with other tools. 02CadGis reads the CRS as declared and transforms to the target CRS.
- Multi-geometry-type layers. No GIS format (except GeoPackage with generic geometry columns) stores mixed geometry types in a single layer. 02CadGis splits CAD layers with mixed types into separate output layers suffixed with the geometry type (e.g.
BUILDINGS_POINT,BUILDINGS_LINESTRING,BUILDINGS_POLYGON). This is deterministic and documented, but the suffix naming should be accounted for in downstream workflows.
Appendix A — CRS Quick Reference
| Drawing Says | Easting | EPSG | Label |
|---|---|---|---|
| ITRF / 3 / Zone 39 | 6 digits | 5257 | TUREF / TM39 |
| ITRF / 3 / Zone 39 | 8 digits | 5273 | TUREF / 3° GK zone 13 |
| ED50 / 3 / Zone 33 | 6 digits | 2321 | ED50 / TM33 |
| ED50 / 3 / Zone 33 | 8 digits | 2208 | ED50 / 3° GK zone 11 |
| WGS 84 / UTM zone 36N | 6 digits | 32636 | WGS 84 / UTM 36N |
| Geographic (lat/lon) | ≤3 digits | 4326 | WGS 84 |
| (empty / unrecognised) | — | — | User must select manually |
Complete EPSG code tables for all 28 Turkish projected CRS variants are maintained in core/crs_detect.py. The detector covers TUREF TM (5253–5259), TUREF GK (5269–5275), ED50 TM (2319–2325), ED50 GK (2206–2212), WGS 84 UTM (32635–32638), and ED50 UTM (23035–23038).
Appendix B — PlanGML Symbology Excerpt
| Upper Group | Example Tabaka | Fill | Stroke |
|---|---|---|---|
| KONUT ALANLARI | PL_KONUT (Yerleşik) | #FDE49B | #B37400, 0.7 mm |
| KONUT ALANLARI | PL_GELISME_KONUT | #FEF0D9 | #D4A017, 0.7 mm |
| TICARET ALANLARI | PL_TICARET | #D47879 | #8B0000, 0.5 mm |
| EGITIM TESISLERI | PL_ILKOKUL | #FEFA96 | #B3A000, 0.5 mm |
| SAGLIK TESISLERI | PL_HASTANE | #FAC8C8 | #CC0000, 0.5 mm |
| ACIK VE YESIL ALANLAR | PL_PARK | #BEE6B0 | #2E7D32, 0.3 mm |
| YOL VE ULAŞIM | PL_YOL | #FFFFFF | #333333, 0.3 mm |
| PLANLAMA SINIRLARI | PL_SINIR | — | #FF0000, 1.2 mm dash |
Complete catalog: 350+ rules in core/symbology.py. Sourced from the official e-Plan SLD files.
Appendix C — Glossary
| Term | Definition |
|---|---|
| ACI | AutoCAD Color Index — 255 indexed colours in DXF/DWG. Mapped to RGB for QGIS rendering. |
| CAD layer / Level | Named grouping of entities within a CAD drawing. DXF: Layer; DGN: Level. The plugin splits by this grouping. |
| Collinear simplification | Removing intermediate vertices lying on a straight line between neighbours. Reduces vertex count without visual change. |
| e-Plan | The Turkish Ministry's electronic plan portal (eplan.csb.gov.tr). Source of official gösterim standards. |
| Gösterim | Turkish: the official visual representation (colour, hatch, line type) of a plan function per Ministry standards. |
| Live mode | Zero-copy reference — data stays in the source file; QGIS reads on demand. |
| Mojibake | Garbled text from incorrect encoding interpretation. Turkish CAD files frequently exhibit CP1254↔CP1252↔UTF-8 mojibake. |
| MPYY | Mekânsal Planlar Yapım Yönetmeliği — Turkish Spatial Planning Regulation. Defines the official UIP tabaka catalog. |
| NCZ / NCA | Netcad's native binary drawing formats. NCZ is standard; NCA is a compatible variant. |
| ODA | Open Design Alliance — provides ODAFileConverter for modern DWG conversion. |
| PASE | PlanX Adaptive Symbology Engine — matches CAD tabaka to official e-Plan gösterim rules. |
| PlanGML | Turkish Ministry's XML-based spatial plan data standard. The plugin populates PlanGML schema columns. |
| SRS id | Netcad-internal coordinate system identifier. Not an EPSG code — never use as one. |
| Tabaka | Turkish: layer. In planning, a named spatial feature category (e.g. PL_KONUT) per the MPYY. |
| Tarama | Turkish: hatch pattern. Official tile image used as polygon fill for certain plan functions. |
| @TAB | Netcad's internal attribute table format inside NCZ files. Linked to geometry by name/label. |
Appendix D — Bibliography & Third-Party Notices
CAD/GIS Conversion & Interoperability
Al-Sabban, W., Issa, R.R.A., & Olbina, S. (2022). The C2G Framework to Convert Infrastructure Data from Computer-Aided Design (CAD) to Geographic Information Systems (GIS). Informatics, 9(2), 42. DOI: 10.3390/informatics9020042
Zhu, J., Wright, G., Wang, J., & Wang, X. (2018). A Critical Review of the Integration of Geographic Information System and Building Information Modelling at the Data Level. ISPRS International Journal of Geo-Information, 7(2), 66. DOI: 10.3390/ijgi7020066
Liu, X., Wang, X., Wright, G., Cheng, J.C.P., Li, X., & Liu, R. (2017). A State-of-the-Art Review on the Integration of Building Information Modeling (BIM) and Geographic Information System (GIS). ISPRS International Journal of Geo-Information, 6(2), 53. DOI: 10.3390/ijgi6020053
Sani, M.J. & Abdul Rahman, A. (2018). GIS and BIM Integration at Data Level: A Review. International Archives of the Photogrammetry, Remote Sensing and Spatial Information Sciences, XLII-4/W9, 299–306. DOI: 10.5194/isprs-archives-XLII-4-W9-299-2018
Şenol, H.İ. & Gökgöz, T. (2024). Integration of Building Information Modeling (BIM) and Geographic Information System (GIS): A New Approach for IFC to CityJSON Conversion. Earth Science Informatics, 17, 3437–3454. DOI: 10.1007/s12145-024-01343-1
Line Simplification & Geometric Algorithms
Douglas, D.H. & Peucker, T.K. (1973). Algorithms for the Reduction of the Number of Points Required to Represent a Digitized Line or Its Caricature. Cartographica: The International Journal for Geographic Information and Geovisualization, 10(2), 112–122. DOI: 10.3138/FM57-6770-U75U-7727
Hershberger, J. & Snoeyink, J. (1992). Speeding Up the Douglas-Peucker Line-Simplification Algorithm. Proceedings of the 5th International Symposium on Spatial Data Handling, 134–143.
Coordinate Reference Systems & Geodesy
Knudsen, T. & Evers, K. (2017). Transformation Pipelines for PROJ.4. FIG Working Week 2017 Proceedings, Helsinki, Finland. URL: fig.net
Evers, K. & Knudsen, T. (2017). Transformation Pipelines for PROJ.4. Geophysical Research Abstracts, 19, EGU2017-8050. URL: copernicus.org
PROJ Contributors. (2024). PROJ — Coordinate Transformation Software Library. Open Source Geospatial Foundation. proj.org
ISO/TC 211. (2020). ISO 19136-1:2020 — Geographic Information — Geography Markup Language (GML) — Part 1: Fundamentals. International Organization for Standardization.
Geospatial Data Standards & Formats
OGC. (2015). OGC KML 2.3. OGC Standard 12-007r2. URL: docs.ogc.org
Butler, H., Daly, M., Doyle, A., Gillies, S., Hagen, S., & Schaub, T. (2016). The GeoJSON Format. RFC 7946, Internet Engineering Task Force. DOI: 10.17487/RFC7946
Yutzler, J. & Daisey, P. (Eds.). (2024). OGC GeoPackage Encoding Standard, Version 1.4.0. OGC Standard 12-128r19. URL: geopackage.org
OGC. (2006). OpenGIS Symbology Encoding Implementation Specification, Version 1.1.0. OGC Standard 05-077r4. URL: docs.ogc.org
Software & Tools
GDAL/OGR Contributors. (2024). GDAL — Geospatial Data Abstraction Library. OSGeo. gdal.org
Open Design Alliance. (2024). ODA File Converter. opendesign.com
Turkish Planning Regulation & Standards
T.C. Çevre, Şehircilik ve İklim Değişikliği Bakanlığı. (2024). Mekânsal Planlar Yapım Yönetmeliği (MPYY). Resmî Gazete.
T.C. Çevre, Şehircilik ve İklim Değişikliği Bakanlığı. (2024). e-Plan Gösterim Kataloğu. eplan.csb.gov.tr
MpyyUipDb_2026_02_27.gpkg catalog is compiled and maintained by Yusuf Eminoğlu from the Ministry's published tabaka lists. The vendored olefile library is used under its BSD license for DGN v8 OLE2 reading. Full attribution: THIRD_PARTY_NOTICES.md.
Appendix E — NCZ Block Type Reference
The following table documents the known NCZ block types understood by Engine v2. Types not listed here are container blocks (kinds {0, 5, 14, 48, 108, 111, 132, 150, 180}) that may embed geometry records, or are unrecognised and skipped with a logged count.
| Kind | Name | Decoder | Key Geometric Semantics |
|---|---|---|---|
| 1 | Point | decode_point | Single coordinate pair (northing-first). Name string at G+86. Used for point features, survey markers, and CAD symbols. |
| 2 | Line | decode_line | Two-vertex segment: start at +8/+16, end at (size-19)/(size-11). Simplest CAD primitive. |
| 3 | Circle | decode_circle | Centre point + diameter (from f64 difference at +50 and +66). Segmentized to polygon in output. |
| 4 | Arc | decode_arc | Centre + radius + start/end angles (radians). Used for curved road alignments and parcel boundaries. |
| 5 | Text | decode_text | Insertion point + text string + height + rotation. Label field candidate in output. Multiple fallback text locations. |
| 6 | Symbol | decode_symbol | Insertion point + symbol code + size + rotation. Point marker from Netcad symbol library. |
| 7 | Polyline / Polygon | decode_polyline | Variable-length vertex array (24 bytes per vertex: 3×f64). Closure detected by endpoint proximity. Rectangle metrics computed for oriented boxes. |
| 9 | Compressed Curve | decode_compressed_curve | Origin + sequence of f32 delta pairs (18-byte records). Efficient storage for smooth curves; duplicates skipped by proximity check. |
| 10 | Box | decode_box | Two opposite corners defining an oriented rectangle. Rotation in radians. Plan name token extracted for labelling. |
| 11 | Map Sheet | decode_map_sheet | Four-corner bounding box. Sheet name extracted as first printable length-prefixed string. Used for map grid frames. |
| 12 | Triangle | decode_triangle | Three vertices with optional Z. Area validated (> 0.0001) to reject degenerate triangles. Used in TIN surfaces and 3D meshes. |
| 13 | Block Reference | decode_block_reference | Insertion point + optional name + rotation. References a block definition stored elsewhere in the file. |
| 15 | Smart Object | decode_smart_object | Complex compound entity: width, height, rotation (grads), scale, grid coordinates, label. Represents parametric CAD objects. Rotation conversion: grads × 0.9 = degrees. |
NCZ Coordinate Conventions
All coordinate pairs in NCZ geometry records are stored northing-first: the first f64 value at +8 is the northing (Y in GIS), and the second f64 value at +16 is the easting (X in GIS). The map_point(first, second) helper transposes these to GIS convention: {"x": second, "y": first, "z": z}. This transposition is verified bit-for-bit against v1 output. Z values are stored as f32 at offset +24; some record types store an alternative Z at +28 (used as fallback when +24 is 0.0). All coordinate values must be finite and within [−108, 108] to pass the range validation gate in finite_pair_in_range().
@TAB Attribute Table Variants
@TAB records are classified into three variants by the attribute decoder:
| Variant | Detection Criteria | Key Fields | Use Case |
|---|---|---|---|
| Label | Printable length-prefixed label at offset 28/29, all bytes in ASCII printable range | label text, prefix f32, code u16, style code u32, 3 coordinate pairs (f64) | Named entities with spatial reference: parcels, buildings, zones. Label links to geometry entity name. |
| Segment | Record ≥ 119 bytes, f64 pairs at 17/25, 45/53, 87/95, 103/111 all look like projected coordinates (|v| in [103, 108]) | 4 coordinate pairs, style code, 4 flag bytes | Linear referencing: road segments, pipeline sections, cadastral edges. |
| ASCII | Anything else. All printable length-prefixed ASCII fields collected | ascii_values (pipe-joined), table_ref_inline | Tabular metadata: material schedules, quantity takeoffs, code lists. |
Tables are grouped by their @TAB marker suffix (e.g. @TAB1, @TAB2) and presented as separate checkable rows in the dock's layer tree. Checking both a geometry layer and its matching @TAB table triggers an automatic attribute join on entity name (for label variants) or on spatial proximity (for segment variants, joining to the nearest polyline segment).
Producer-Specific Quirks in NCZ Files
Netcad's proprietary format exhibits several documented quirks that the engine handles transparently:
- Smart object S0 suppression. When a drawing contains at least one smart object (type 15), the producer emits "S0" symbol entities (type 6) on layer code 0 alongside each smart object. These are not real symbols; they are internal implementation artefacts. The
_drop_smart_object_artifactsfunction removes them when any SmartObject entity is present. - 1-based layer indexing. Some NCZ versions index layers from 1 rather than 0. The
DrawingMetadata.layer_name()andlayer_color()methods trylayer_codefirst, thenlayer_code - 1as a fallback. This handles both conventions without configuration. - Near-black-blue normalisation. A layer colour of ARGB (255, 0, 0, 1) — effectively pure black with a blue LSB — is a known Netcad producer artefact. The
_normalize_colorfunction maps this to pure black (255, 0, 0, 0). - Colour code 0 = by layer. A per-entity colour code of 0 means "use the layer colour." Colour code 1 forces blue; colour code 255 forces red. Any other non-zero colour code is passed through as-is (no lookup).
- Version string encoding. The producer version string (block kind 25) uses the Netcad OEM codepage for Turkish characters, decoded by
decode_oem_text()which maps byte values 208, 221, 222, 240, 253, 254 to their Turkish equivalents. - GIS layout shift. Geometry records of kind 22 (GIS layout) have all geometry-related fields shifted +28 bytes relative to kind 21 (plain layout). The
GeometryRecord.shiftfield encodes this (0 or 28), and all decoders addrecord.shiftto the relevant offsets. This is invisible to callers but critical for correct decoding.
Benchmark Reproducibility
All performance claims in this manual are reproducible. The benchmark script (benchmarks/ncz_engine_benchmark.py) records fixture SHA-256, Python/QGIS build details, p50/p95 duration, MiB/s throughput, entities/s, peak Python allocation (via tracemalloc), and a normalised output SHA-256 digest. A benchmark run is invalid if repeated output hashes differ. To reproduce:
python zero2cadgis/benchmarks/ncz_engine_benchmark.py \
C:/path/small.ncz C:/path/large.ncz \
--label v1-baseline --output C:/path/v1-baseline.json
The benchmark JSON includes fixture metadata (SHA-256, byte size, producer, covered geometry types, expected entity/table counts) and is designed for before/after comparison during engine development. The synthetic fixture corpus (tests/ncz_fixtures.py) exercises every decoder path, both block layouts, embedded containers, metadata blocks, and @TAB tables, and is validated field-by-field against v1 output. Validation against real municipal drawings requires setting the ZERO2CADGIS_NCZ_FIXTURE environment variable to a licensed drawing path; this opt-in test was validated on a 1.2 MiB / 8,163-entity municipal Netcad file (60 layers, ITRF/3 zone 42).
Appendix F — Configuration Reference
| Setting | Storage | Default | Description |
|---|---|---|---|
zero2cadgis/oda_converter_path | QSettings | (empty) | Custom path to ODAFileConverter.exe. Set via the interactive setup dialog. Overrides all auto-detection. |
zero2cadgis/last_source_dir | QSettings | (empty) | Last-used directory for source file selection. Persisted between sessions. |
zero2cadgis/last_target_dir | QSettings | (empty) | Last-used directory for GeoPackage output. Persisted between sessions. |
zero2cadgis/last_target_crs | QSettings | (project CRS) | Last-used target CRS authid (e.g. EPSG:5257). Persisted between sessions. |
zero2cadgis/cad_split_layers | QSettings | true | Default state of "Split into CAD layers" checkbox. |
zero2cadgis/cad_simplify | QSettings | true | Default state of collinear simplification checkbox. |
zero2cadgis/plan_type | QSettings | AUTO | Default PlanGML plan type: UIP, NIP, CDP, or AUTO. |
ZERO2CADGIS_NCZ_CACHE_DISABLE | Environment | (unset) | Set to 1/true/yes to disable the NCZ index cache. Useful for debugging or when working with rapidly-changing files. |
ZERO2CADGIS_OGR_CACHE_DISABLE | Environment | (unset) | Set to 1/true/yes to disable the OGR catalog cache. |
ZERO2CADGIS_NCZ_FIXTURE | Environment | (unset) | Path to a real NCZ file for opt-in validation testing. Used by test_ncz_engine_v2.py to assert bit-exact parity for a specific drawing. |
ODA_FILE_CONVERTER_PATH | Environment | (unset) | Path to ODAFileConverter.exe. Checked during ODA discovery (tier 3). Useful for CI and containerised environments. |
ODA_PATH | Environment | (unset) | Alternative environment variable for ODA installation directory. |
ODA_CONVERTER | Environment | (unset) | Alternative environment variable for ODA converter executable path. |