GDPR for Geospatial Data Lineage
Part of: Regulatory Compliance & Standards Mapping
A coordinate is not automatically anonymous. A latitude and longitude that resolves to a residential parcel, a habitual commuting route, or a single visit to a medical facility is personal data under the General Data Protection Regulation, and once a dataset contains it, every activity that ingests, reprojects, joins, or publishes that data inherits obligations. The problem for geospatial teams is that these obligations are historical: when a data subject invokes their right of access or erasure, you must reconstruct what you did with their location, when, on what lawful basis, and where it flowed — sometimes years after the processing. Lineage is the only mechanism that answers those questions without heroic archaeology, which is why GDPR compliance for location data is fundamentally a provenance-tracking problem rather than a consent-banner problem.
This guide applies GDPR to spatial data lineage concretely: how to detect personal data hiding in coordinates, how to log lawful basis on every activity, how to reconstruct data-subject rights from lineage rows, how to produce Data Protection Impact Assessment evidence, and how to reconcile the right to erasure with an immutable audit trail. It sits within the broader regulatory compliance and standards mapping section, whose control-to-field philosophy drives everything below, and it feeds two companion pages: a precise mapping of GDPR controls to lineage fields and a how-to on anonymizing location data for GDPR.
Prerequisites
Step-by-step
1. Detect personal data in coordinates
Before you can log a lawful basis, you must know which columns are in scope. A coordinate becomes personal data when its precision and context allow it to single out a person. The purpose of this step is to flag processing that touches identifiable location so the pipeline can require a lawful basis downstream. Use resolution and linkage as the test: sub-metre points tied to a subject identifier are almost always personal; a coarse municipal centroid usually is not.
from __future__ import annotations
import geopandas as gpd
def is_personal_location(gdf: gpd.GeoDataFrame, subject_col: str | None) -> bool:
"""Heuristic: identifiable if a subject key exists and points are high-resolution."""
if subject_col and subject_col in gdf.columns:
return True
# Point geometries with sub-block precision are treated as identifiable.
if (gdf.geometry.geom_type == "Point").all():
# WGS84 degrees: ~5 decimal places ≈ 1 m; flag anything finer than a block.
precise = gdf.geometry.apply(lambda p: round(p.x, 4) != round(p.x, 3))
return bool(precise.any())
return False
Anything this function flags must carry a lawful basis and, where feasible, be routed through the anonymizing location data for GDPR workflow before wider processing.
2. Log lawful basis on every activity
Article 6 requires that every processing activity rest on a lawful basis, and Article 5 requires purpose limitation. The purpose of this step is to make basis and purpose non-nullable at capture so no personal-location activity can be recorded without them. Write the basis into the same lineage event as the transformation itself.
from __future__ import annotations
from datetime import datetime, timezone
import hashlib
import json
import psycopg
LAWFUL_BASES = {"consent", "contract", "legal_obligation",
"vital_interests", "public_task", "legitimate_interests"}
def log_activity(conn: psycopg.Connection, *, dataset_id: str, subject_key: str,
activity: str, lawful_basis: str, purpose: str) -> str:
if lawful_basis not in LAWFUL_BASES:
raise ValueError(f"invalid Art. 6 basis: {lawful_basis}")
ts = datetime.now(timezone.utc)
body = json.dumps({"dataset_id": dataset_id, "subject_key": subject_key,
"activity": activity, "lawful_basis": lawful_basis,
"purpose": purpose, "ts": ts.isoformat()},
sort_keys=True, separators=(",", ":"))
digest = hashlib.sha256(body.encode()).hexdigest()
conn.execute(
"""INSERT INTO lineage_event
(dataset_id, subject_key, activity, lawful_basis, purpose,
valid_from, content_hash)
VALUES (%s, %s, %s, %s, %s, %s, %s)""",
(dataset_id, subject_key, activity, lawful_basis, purpose, ts, digest),
)
return digest
The gradient is the point rather than any individual row: there is no coordinate precision at which data becomes categorically anonymous, because identifiability depends on how many people share the resulting cell. Truncating to three decimals is genuinely anonymising in a dense city block and genuinely identifying on a rural road, and a pipeline applying one rule uniformly will publish disclosures in exactly the places where they are most sensitive.
That is why only the bottom row is defensible: it makes the threshold explicit, tests it against the actual data, and suppresses what fails. Record k, the aggregation unit, and the count of suppressed cells as parameters on the anonymisation activity, so the assessment is auditable rather than assumed. A suppression count of zero across a large heterogeneous area is itself a signal worth checking — it usually means the test was not applied.
3. Reconstruct data-subject rights from lineage
Articles 15 through 17 give subjects the rights of access, rectification, and erasure. The purpose of this step is to resolve any of those requests from the lineage store alone, keyed on subject_key. Because the store is append-only, an access request is a filtered read, and a rectification is a new corrective event rather than an in-place edit.
-- Article 15: assemble the full processing history for one data subject.
SELECT dataset_id, activity, lawful_basis, purpose, valid_from, content_hash
FROM lineage_event
WHERE subject_key = %(subject_key)s
ORDER BY valid_from ASC;
This single query is the backbone of every rights response; the mapping of GDPR controls to lineage fields guide turns its columns into a formal Article 30 record. The append-only design is what makes rectification tractable under Article 16: rather than overwrite a wrong geocode in place, which would destroy the audit trail and leave you unable to prove what the record said before, you append a corrective event that supersedes the earlier one. A point-in-time read reconstructs whichever version was authoritative on a given date by filtering on valid_from, so the store simultaneously honors the subject’s correction and preserves the history a regulator may ask you to defend.
4. Produce DPIA evidence
Article 35 requires a Data Protection Impact Assessment for high-risk processing, and large-scale location tracking usually qualifies. The purpose of this step is to derive DPIA inputs — the categories of data, the purposes, and the actual processing performed — from lineage rather than from a self-reported questionnaire.
-- DPIA input: distinct purposes and bases actually exercised, with volume.
SELECT purpose, lawful_basis, COUNT(*) AS activities,
MIN(valid_from) AS first_seen, MAX(valid_from) AS last_seen
FROM lineage_event
GROUP BY purpose, lawful_basis
ORDER BY activities DESC;
Grounding the DPIA in observed lineage means the assessment reflects what the system does, not what a form claims it does.
5. Reconcile erasure with an immutable trail
The right to erasure appears to conflict with an append-only audit log. The purpose of this step is to resolve that conflict with crypto-shredding: encrypt each subject’s payload under a per-subject key, and satisfy erasure by destroying the key. The lineage row — its timestamps, activity name, lawful basis, and hash — survives for audit, but the personal payload becomes unrecoverable.
from __future__ import annotations
import psycopg
def erase_subject(conn: psycopg.Connection, subject_key: str) -> int:
"""Crypto-shred: drop the key, retain the tamper-evident metadata."""
conn.execute("DELETE FROM subject_key_vault WHERE subject_key = %s",
(subject_key,))
# Metadata rows remain; payload ciphertext is now undecryptable.
cur = conn.execute(
"""UPDATE lineage_event SET payload_erased = TRUE
WHERE subject_key = %s RETURNING id""", (subject_key,))
return len(cur.fetchall())
Configuration reference
| Parameter | Type | Valid values | Default |
|---|---|---|---|
subject_key |
text | opaque per-subject identifier (never raw PII) | required |
lawful_basis |
enum | consent, contract, legal_obligation, vital_interests, public_task, legitimate_interests |
required |
purpose |
text | free text bound to a processing register entry | required |
retention_days |
integer | 1–3650 | 730 |
precision_flag |
enum | identifiable, anonymized, pseudonymized |
identifiable |
payload_encryption |
enum | per_subject_key, shared_key, none |
per_subject_key |
content_hash |
text | SHA-256 hex over the canonical event body | auto |
Where Erasure and Immutability Actually Meet
The retained hash deserves a moment’s thought, because it looks like a loose end. A SHA-256 of erased personal data is not itself personal data in any practical sense — it is not reversible, and it cannot be used to identify anyone without already possessing the original. What it does provide is the ability to demonstrate, later, that a specific dataset was the one processed under a specific basis, which is precisely the demonstration an Article 30 record exists to support. Where a regulator takes a stricter view for a particular category, the resolution is to record the digest of a salted derivation rather than to omit the field entirely.
Scope is the harder question in practice. An erasure request covers a data subject, while lineage records cover datasets, and the mapping between them is rarely one-to-one. Maintain a subject-to-dataset index built at ingestion rather than attempting to search payloads at request time — reconstructing which of four hundred derived products contains a given subject, months later, is exactly the work the index exists to avoid.
Common failure modes & mitigations
| Failure mode | Symptom | Mitigation |
|---|---|---|
| Silent CRS drift on personal points | Coordinates shift meters during reprojection, breaking subject matching | Log input and output CRS on every transform; validate subject_key joins survive reprojection |
| Anonymization treated as erasure | Truncated geohashes still re-identify via linkage | Record anonymization as its own event; measure k-anonymity, do not assume it |
| Lawful basis backfilled | Activities logged without a basis, patched later | Enforce NOT NULL on lawful_basis; reject inserts at the pipeline boundary |
| Erasure breaks the hash chain | Deleting rows invalidates downstream content hashes | Crypto-shred the payload, never delete the metadata row |
| Subject key = raw PII | The lineage store itself becomes a breach surface | Store an opaque token; keep the token-to-identity map in a separate vault |
Cross-Border Transfer, Which Coordinates Make Concrete
Transfer rules are usually discussed in terms of where a server sits, and geospatial work adds a second dimension that trips teams up: the data describes a place, and the place is not necessarily where the processing happens. Both matter, and they are governed differently.
The transfer question is about the processing location. Reprojecting an EU residents’ dataset on a worker in another jurisdiction is a transfer regardless of where the coordinates point, and it needs a lawful transfer mechanism recorded alongside the lawful basis. Because pipelines schedule work onto whichever worker is free, this is easy to violate accidentally — a burst that spills into a secondary region turns a compliant nightly job into a transfer nobody authorised.
Record the processing region as a field on every activity, not as a property of the system. A system-level statement that “processing occurs in the EU” is unverifiable after the fact and false the first time capacity spills elsewhere; a per-activity region field makes the exception queryable and lets a scheduled check assert that no activity on a restricted dataset ran outside its permitted set. Pair it with the dataset’s own permitted-region list so the assertion is data-driven rather than hard-coded.
The second dimension — what the coordinates describe — matters for a different reason. Data about locations in a jurisdiction can attract that jurisdiction’s rules even when processed elsewhere, and sensitive-site restrictions in particular are usually tied to the territory depicted rather than the server. Carrying the depicted territory as an attribute of the dataset, derived from its extent at ingestion, is what makes those rules applicable automatically rather than by someone remembering.
Compliance & governance alignment
| GDPR article | Requirement | Lineage field or practice |
|---|---|---|
| Art. 5(1)(b) | Purpose limitation | purpose bound to a processing-register entry |
| Art. 5(1)(e) | Storage limitation | retention_days, enforced by scheduled expiry job |
| Art. 6 | Lawful basis for processing | non-nullable lawful_basis enum on every event |
| Art. 15 | Right of access | subject_key-indexed read of all events |
| Art. 17 | Right to erasure | crypto-shred via subject_key_vault key deletion |
| Art. 30 | Records of processing | aggregation over purpose, lawful_basis, recipients, transfers |
| Art. 35 | Data Protection Impact Assessment | derived from observed purpose/lawful_basis distribution |
| Art. 44 | Cross-border transfer records | transfer_destination and safeguard fields on export events |
These mappings connect directly to the field-level crosswalk in the control-to-lineage-field mapping guide and to the anonymization practice in the anonymizing location data for GDPR how-to. Treated together, they let a data protection officer answer any subject request or supervisory-authority query from the lineage store itself, which is the entire point of building compliance in at the field level rather than bolting it on at audit time.
Frequently Asked Questions
Is a coordinate personal data?
It depends on what it can be linked to, which is the answer the regulation itself gives. A building footprint is not; the same footprint labelled as one person’s residence is. A GPS trace is, almost always, because movement patterns identify individuals even without a name attached. Treat the question as an assessment recorded per dataset rather than a property of the geometry type.
Can we rely on aggregation to take data out of scope?
Only with a recorded threshold test. Aggregation reduces identifiability by an amount that depends on the underlying population, so the same aggregation is anonymising in one polygon and disclosing in another. Apply a k threshold, suppress cells below it, and log the parameters — an aggregation without a threshold is a hope, not a control.
How do we handle a subject-access request for derived products?
Answer from the derivation graph rather than from the datasets. Start at the subject-to-dataset index, walk downstream edges, and enumerate every product that inherited the data. This is the query the graph exists to serve, and it is also the check that reveals products nobody remembered were derived from personal sources.
Does GDPR apply to lineage records themselves?
Yes, where they contain personal data — and actor fields routinely do. Staff names in actor are personal data about employees, with their own lawful basis and retention considerations. Use role or service-account identifiers in the record and resolve to individuals through a separately governed roster.
What lawful basis applies to public-sector mapping?
Usually public task rather than consent, which materially changes the obligations: there is no right to erasure under public task in the same form, but there is a right to object that must be considered. Recording the basis explicitly per activity — rather than assuming one basis for the whole system — is what makes those differences applicable at the granularity they actually apply.
How long can we keep lineage on erased data?
As long as the accountability obligation lasts, which is typically longer than the data itself. The retained record is what demonstrates lawful processing and lawful erasure; discarding it early leaves you unable to answer the question the retention was for. Set its schedule from the accountability clock rather than from the dataset’s.
Related
- Mapping GDPR Controls to Lineage Fields — the field-level crosswalk
- Anonymizing Location Data for GDPR — k-anonymity and suppression in practice
- FISMA Compliance for Spatial Systems — the integrity controls this shares
- Establishing Trust Boundaries in GIS — recording obligations that travel with a release
- Part of: Regulatory Compliance & Standards Mapping