Building inspection records present a specific challenge when you try to add them to a GIS layer: they do not have their own spatial footprint. An inspection record describes an event (an inspection, on a specific date, with a specific outcome) that is associated with a parcel, but the inspection itself has no geometry. The parcel has geometry. The question is how to represent the inspection event spatially in a way that supports the queries planners actually need to run.
The geometry question is connected to the schema question. The attribute fields you define for the inspection layer determine what queries are possible. Get the schema wrong and you end up with a layer that is visually present in the GIS but essentially useless for analytical queries. This article covers the decisions involved in both areas.
Geometry choice: parcel polygon vs. centroid point
There are two common geometry approaches for inspection records that are spatially associated with parcels. The first is to use the parcel polygon geometry: each inspection record inherits the polygon boundary of the parcel it belongs to. The second is to use a point at the parcel centroid.
Parcel polygon geometry produces a visually rich layer. Parcels with inspection records are shaded by status, outcome, or recency, and the size of each shaded area on the map corresponds to the actual size of the parcel. This works well for density mapping and is visually intuitive for planners accustomed to working with parcel layers. The drawback is file size: polygon geometry is substantially heavier than point geometry, and a layer with tens of thousands of inspection records as polygon features can become slow to render and query in a desktop GIS without proper indexing.
Centroid point geometry is lighter and faster. Each inspection record appears as a point at the center of the associated parcel. Multiple inspections on the same parcel produce multiple points at the same location, which can be handled by using slightly offset jitter or by aggregating to a single feature per parcel with the most recent inspection as the display geometry. Point layers also join more cleanly to tabular data in ArcGIS and QGIS, which is useful when the inspection record layer is being used as a lookup table rather than a primary display layer.
For a planning department GIS, the practical recommendation is to maintain both: a polygon layer keyed one-to-one with the parcel layer, with summarized inspection attributes (most recent inspection date, most recent outcome, count of inspections in the last N years), and a separate point layer containing one feature per individual inspection event with full attribute detail. The polygon layer supports map display and spatial queries. The point layer supports tabular analysis and time-series filtering.
The one-to-many problem and how to handle it
A single parcel typically has multiple inspection records over its history. A residential property might have had a framing inspection, a rough electrical inspection, a plumbing inspection, a final inspection, and several reinspections following failed items, all within a single permit cycle. Across the full permit history of a parcel, the inspection record count can reach into the dozens.
GIS attribute tables are not naturally designed for one-to-many relationships. The parcel layer has one row per parcel. Inspection records have many rows per parcel. Joining them directly produces a table with one row per inspection per parcel combination, which is correct for the inspection point layer but is not how the parcel polygon layer should work.
The standard solution is to build a separate inspection records table and join to the parcel layer selectively, either as a relate (in ArcGIS terminology) or via a SQL join at query time. The parcel polygon layer holds summary attributes: last inspection date, last inspection outcome, count of inspections with failed outcomes in the last five years, and similar aggregate fields. The detail is in the related table, accessible by drilling into a specific parcel.
This architecture requires that the inspection records table has a reliable join key to the parcel layer. The APN is the natural join key, but as discussed elsewhere, APN formatting inconsistency creates matching ambiguity. A normalized APN field in the inspection records table, formatted to match the parcel layer's APN format exactly, is a prerequisite for clean joins. If the parcel layer uses a hyphenated format like "07-043-00210" and the inspection records table contains "07043210," the join will fail unless a normalization step aligns them.
Field definitions for the inspection record schema
The specific fields in an inspection record schema depend on what the source documents contain. County building inspection forms vary, but a core set of fields appears across most jurisdictions and supports the queries planning departments typically need.
The minimum useful set for a planning GIS layer includes: APN (normalized, indexed), address (normalized to a consistent format matching the parcel layer or a county address master), inspection type (framing, electrical, plumbing, final, reinspection, etc.), inspection date (ISO 8601 formatted, indexed), inspector ID or identifier (not full name, which varies in format and creates classification problems), outcome code (pass, fail, conditional, no-access), and permit number (linking the inspection back to the originating permit). Optionally, a corrective action required flag (boolean) and a correction deadline date field are useful when the source documents contain them.
Field naming conventions matter for GIS interoperability. Shapefile attribute fields are limited to 10 characters, which forces truncation and abbreviation. If you are targeting Shapefile as an output format, design the schema with 10-character field names from the start: "INSP_DATE" instead of "inspection_date," "OUTCOME" instead of "inspection_outcome_code." GeoJSON and GDB formats do not have this constraint, but planning for the most restrictive format avoids downstream renaming problems.
Date fields should be stored as actual date types rather than strings. A date stored as "08/01/2025" in a text field cannot be sorted chronologically without parsing. An ISO-formatted date in a proper date type field can be sorted, filtered by range, and used in date arithmetic (days since inspection, inspection age, etc.) directly in the GIS.
Outcome codes and value normalization
Inspection outcome values in source documents are not standardized across jurisdictions or even within a single jurisdiction across different eras. The same outcome might appear as "PASS," "Approved," "App," "P," or "1" depending on the form template and the clerk who filled it out. The same applies to failure: "FAIL," "Rejected," "Rej," "F," "0," "Correction Required."
An extraction pipeline needs an outcome normalization step that maps these variant strings to a controlled vocabulary before loading into the GIS layer. The controlled vocabulary should be small and semantically clear: PASS, FAIL, CONDITIONAL, NO_ACCESS, UNKNOWN. CONDITIONAL covers inspections where partial approval was granted with conditions attached. NO_ACCESS covers cases where the inspector could not reach the site. UNKNOWN is the appropriate value when the source document does not contain a readable outcome field rather than forcing a best guess.
Using UNKNOWN rather than defaulting to FAIL or PASS for ambiguous records is important. A planner querying for parcels with recent failed inspections needs to be able to trust the FAIL records they see. If ambiguous records are silently mapped to FAIL, the query results become unreliable. An explicit UNKNOWN category keeps the data honest and routes those records to a human review queue for resolution.
Indexing for query performance
A county permit archive might contain tens of thousands of inspection records spanning decades. Without proper indexing, spatial and attribute queries on this layer become slow enough to affect usability. The fields that benefit most from indexing are APN (used in virtually every parcel-keyed lookup), inspection date (used in time-range filters), and outcome code (used in status filters). In a Shapefile, spatial indexing is handled by the .sbn and .sbx files. In a file geodatabase, the index is managed within the GDB structure. In GeoJSON loaded into a web GIS like Mapbox or a PostGIS database, explicit index creation is required.
Spatial indexes in the GIS file format are separate from attribute indexes. Both matter. A spatial query that finds all inspection records within a drawn polygon uses the spatial index. An attribute query that finds all parcels with failed inspections in the last 24 months uses the attribute index on the date and outcome fields. Running both simultaneously (spatial filter plus attribute filter) uses both. Without both indexes, this type of compound query reverts to a full table scan, which is noticeably slow on large datasets.
Connecting inspection records to the permit they belong to
An inspection record exists in the context of a permit. A framing inspection on parcel 07-043-00210 is meaningful when you know which building permit it was conducted under. Without that context, you cannot determine whether the inspection record relates to original construction, a later addition, or a rehabilitation permit. For planning analysis, this context matters.
The extraction schema should include a permit number field that links the inspection record to its parent permit. This creates a three-level data model: parcel (one), permits per parcel (many), inspections per permit (many). The permit number is the join key between the second and third levels. A parcel with two active permits in the same year should have two distinct inspection record sets, one per permit, not a merged set of inspections that could be misread as belonging to a single permit.
Maintaining this linkage through extraction requires that the source inspection documents contain the parent permit number, which most do. When the permit number is not on the inspection form (which happens, particularly with older records where inspections were recorded separately from the permit application), the linkage has to be inferred from date overlap and parcel match, which introduces ambiguity. That ambiguity should be flagged in the data rather than silently resolved, so planners can see where the permit linkage is confirmed versus inferred.