ODbL vs CC BY-SA for Vector Datasets
For vector datasets whose value is the structured collection of facts — road networks, address points, parcels, gazetteers — ODbL is the better license, because it protects database rights directly and confines its share-alike obligation to publicly distributed derivative databases; CC BY-SA is the better choice only when the vector layer is genuinely a creative cartographic work. This guide explains that verdict dimension by dimension and gives you a script that tags an existing vector inventory with the correct obligations for whichever license each layer already carries.
Both instruments are copyleft open licenses, and both are common in the spatial world — CC BY-SA underpins a great deal of community-mapped cartography, while ODbL is the backbone of large collaborative feature databases. But they were engineered for different legal regimes, and applying the wrong one to a vector dataset produces obligations that are either unenforceable or accidentally over-broad. This page is a focused companion to Open Data License Selection & Comparison and sits under the broader Geospatial Data Licensing & Compliance Fundamentals guide, which frames why licensing is an engineering constraint rather than a legal afterthought.
The core distinction
ODbL (the Open Database License) was written to protect database rights — the sui generis right, recognised in the EU and applied by contract elsewhere, that rewards substantial investment in assembling and maintaining a collection of facts. It treats the dataset as a database with three separable elements: the database itself, its contents, and any produced works derived from it. Its share-alike condition attaches to a Derivative Database that is publicly used or distributed.
CC BY-SA was written to protect copyright in creative expression. Applied to a vector layer, it protects whatever originality exists in the selection, arrangement, and styling — which for a plain feature table is thin, and for a hand-drawn thematic map is substantial. Its share-alike condition attaches to any Adapted Material that is shared, with no requirement that the adaptation be a database.
That difference in what is protected and when share-alike fires is the whole decision.
Dimension-by-dimension comparison
| Dimension | ODbL-1.0 | CC BY-SA-4.0 |
|---|---|---|
| Legal regime targeted | Database rights (sui generis) plus any copyright in the DB | Copyright, extended in 4.0 to sui generis database rights |
| Best-fit asset | Feature databases: roads, addresses, parcels, POIs | Cartographic works, styled thematic maps, creative compilations |
| Attribution required | Yes — must credit the source database | Yes — must credit the author |
| Share-alike trigger | Public distribution of a derivative database | Sharing any adaptation (database or not) |
| Internal / undistributed use | Not restricted; share-alike does not fire | Not restricted; share-alike does not fire |
| Produced works (maps, images from the data) | May carry a separate “Produced Work” notice, not full share-alike | The rendered work itself is an adaptation and inherits SA |
| Keeping technical protection (DRM) on output | Prohibited unless a parallel unrestricted copy is offered | Prohibited (no effective technological measures) |
| Commercial reuse | Permitted | Permitted |
| Compatibility with the other | Not compatible for a combined distributed work | Not compatible for a combined distributed work |
| Typical ecosystem | Collaborative open-data databases, gazetteers | Community cartography, wiki-style map projects |
The practical read: if someone can extract your rows and the loss to you is the collection, choose ODbL. If the loss is the look and craft of a map, choose CC BY-SA. A crucial consequence sits in the “Produced works” row — under ODbL you can render a public map tile from ODbL data and license that image more freely (subject to attribution), whereas under CC BY-SA the rendered map is itself an adaptation that must stay CC BY-SA.
Automated Python Implementation
The script below scans a directory of vector datasets (GeoPackage layers or shapefiles), reads each layer’s recorded license, and writes a normalized obligation profile — attribution requirement, share-alike trigger scope, and the SPDX identifier — into a sidecar JSON manifest. It is self-contained and uses geopandas/fiona for reading and fiona.listlayers for multi-layer GeoPackages.
#!/usr/bin/env python3
"""Tag a vector inventory with ODbL / CC BY-SA obligation profiles.
For each GeoPackage layer or shapefile, read the recorded license from a
'license' column or an adjacent '<name>.license' sidecar, resolve its
obligation profile, and emit '<name>.obligations.json'.
Usage:
python tag_vector_obligations.py /data/vector_inventory
"""
import json
import sys
from pathlib import Path
import fiona
import geopandas as gpd
# Obligation profiles keyed by SPDX identifier. The share_alike_scope field
# captures the crucial ODbL vs CC BY-SA difference.
OBLIGATIONS = {
"ODbL-1.0": {
"requires_attribution": True,
"share_alike": True,
"share_alike_scope": "distributed_derivative_database",
"produced_work_relaxation": True,
},
"CC-BY-SA-4.0": {
"requires_attribution": True,
"share_alike": True,
"share_alike_scope": "any_shared_adaptation",
"produced_work_relaxation": False,
},
"CC-BY-4.0": {
"requires_attribution": True,
"share_alike": False,
"share_alike_scope": "none",
"produced_work_relaxation": True,
},
}
def read_license(path: Path, layer: str, gdf: gpd.GeoDataFrame) -> str:
"""Resolve a layer's SPDX license from a column or a sidecar file."""
# Preferred: an explicit 'license' attribute column, single-valued.
if "license" in gdf.columns:
values = set(gdf["license"].dropna().unique())
if len(values) == 1:
return str(values.pop())
# Fallback: a sidecar file named '<stem>.license'.
sidecar = path.with_suffix(".license")
if sidecar.exists():
return sidecar.read_text(encoding="utf-8").strip()
return "UNKNOWN"
def profile_layer(path: Path, layer: str) -> dict:
"""Build the obligation profile for one vector layer."""
gdf = gpd.read_file(path, layer=layer) if layer else gpd.read_file(path)
spdx = read_license(path, layer, gdf)
obligations = OBLIGATIONS.get(spdx)
return {
"source": str(path),
"layer": layer or path.stem,
"feature_count": int(len(gdf)),
"license": spdx,
"recognized": obligations is not None,
"obligations": obligations
or {"note": "unknown license — manual review required"},
}
def iter_layers(path: Path):
"""Yield (path, layer_name) pairs; GeoPackages may hold many layers."""
if path.suffix.lower() == ".gpkg":
for layer in fiona.listlayers(str(path)):
yield path, layer
elif path.suffix.lower() in (".shp", ".geojson"):
yield path, ""
def main(root: str) -> int:
root_path = Path(root)
if not root_path.is_dir():
print(f"ERROR: not a directory: {root}")
return 1
profiles = []
for path in sorted(root_path.rglob("*")):
if path.suffix.lower() not in (".gpkg", ".shp", ".geojson"):
continue
for src, layer in iter_layers(path):
profile = profile_layer(src, layer)
profiles.append(profile)
out = src.with_suffix(".obligations.json")
out.write_text(json.dumps(profile, indent=2), encoding="utf-8")
flag = "OK " if profile["recognized"] else "REVIEW"
print(f"[{flag}] {profile['layer']}: {profile['license']}")
unknown = [p for p in profiles if not p["recognized"]]
print(f"\nTagged {len(profiles)} layer(s); {len(unknown)} need review.")
return 1 if unknown else 0
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else "."
sys.exit(main(target))
The key line is share_alike_scope: distributed_derivative_database for ODbL versus any_shared_adaptation for CC BY-SA. Downstream automation — attribution generators, publish gates, lineage trackers — reads that field to decide whether an operation triggers the copyleft obligation.
Validation and pipeline integration
Run the tagger and confirm every layer resolved to a recognized license before publishing:
python tag_vector_obligations.py /data/vector_inventory
echo "exit status: $?" # non-zero means at least one UNKNOWN license
Lock the obligation mapping with a small pytest block so a future edit cannot silently flip ODbL’s narrow trigger into CC BY-SA’s broad one:
# tests/test_obligations.py
from tag_vector_obligations import OBLIGATIONS
def test_odbl_scope_is_database_only():
assert OBLIGATIONS["ODbL-1.0"]["share_alike_scope"] == \
"distributed_derivative_database"
def test_cc_by_sa_scope_is_any_adaptation():
assert OBLIGATIONS["CC-BY-SA-4.0"]["share_alike_scope"] == \
"any_shared_adaptation"
def test_cc_by_has_no_share_alike():
assert OBLIGATIONS["CC-BY-4.0"]["share_alike"] is False
Long-term compliance best practices
- Record the SPDX identifier, not a prose label. Store
ODbL-1.0andCC-BY-SA-4.0, never “open” or “share-alike license” — automation cannot branch on prose, and audits require the exact instrument and version. - Preserve the produced-work distinction for ODbL. When you render map tiles or images from ODbL vector data, tag them as Produced Works with attribution rather than propagating full database share-alike; conflating the two over-restricts your own outputs.
- Never assume the two licenses combine. A distributed product that mixes ODbL and CC BY-SA layers cannot satisfy both copyleft terms; source one contributor under a permissive license before joining. The mechanics of that resolution live in choosing a license for derived spatial products.
- Track deployment context. Because ODbL’s trigger is public distribution, tag each derivative
internalorpublic; applying share-alike to undistributed internal work is a self-inflicted constraint. - Re-tag on every ingest. Vendor and community layers occasionally change license between releases; make the tagging pass part of ingestion rather than a one-time exercise so the obligation profile never goes stale.
Related
- Open Data License Selection & Comparison — the full selection framework and compatibility engine that this comparison feeds into
- Choosing a License for Derived Spatial Products — how mixing ODbL and CC BY-SA sources computes the output license
- Creative Commons Licensing for GIS Datasets — attribution stacking and version tracking for CC-licensed layers
- Geospatial Data Licensing & Compliance Fundamentals — parent overview of licensing models and obligations