Attribution requirements explained: OpenStreetMap, Copernicus and national data
Problem statement
Almost every open spatial dataset requires attribution, and almost every map made from them gets it wrong in one of three ways: the notice is missing, it is in the metadata rather than on the map, or it is a paraphrase rather than the wording the publisher specified.
The requirement is not decorative. It is the condition under which you are allowed to use the data at all, and it is the one condition that is visible to the publisher, who can see your map. It is also the easiest to satisfy correctly, once you know that each source prescribes its own string and its own placement.
This guide collects the wordings for the sources you will actually use, explains where the notice has to appear for each medium, and shows how to assemble the credit line from metadata rather than from memory.
Quick answer
Store the required string with each layer and build the line from the layers you used:
ATTRIBUTION = {
"osm": "ยฉ OpenStreetMap contributors",
"os_opendata": "Contains OS data ยฉ Crown copyright and database right 2026",
"ea": "Contains Environment Agency data ยฉ Crown copyright and database right 2026",
"copernicus": "Contains modified Copernicus Sentinel data 2026",
"naturalearth": "Natural Earth",
"usgs_landsat": "Landsat imagery courtesy of the U.S. Geological Survey",
"eurostat_nuts": "ยฉ EuroGeographics for the administrative boundaries",
}
def credit(layers):
seen = {}
for layer in layers:
seen.setdefault(ATTRIBUTION[layer], None)
return " ยท ".join(seen)
print(credit(["osm", "os_opendata", "copernicus"]))
ยฉ OpenStreetMap contributors ยท Contains OS data ยฉ Crown copyright and database right 2026 ยท Contains modified Copernicus Sentinel data 2026
Two details that trip people up: Copernicus requires the word modified when you have processed the data, and UK public-sector data requires the year of the copyright and database right.
Step-by-step solution
1. Use the publisher's wording
Attribution clauses generally prescribe a string. "Map data from OSM" is not "ยฉ OpenStreetMap contributors", and the difference matters because the clause is what you agreed to. Copy the wording from the licence page and store it with the layer.
2. Put it where the medium requires
- Static map image โ visible on the image, usually bottom-left or bottom-right, legible at the size the image will be viewed.
- Web map โ in the map interface, typically the attribution control; it must be visible or one click away, not hidden behind a menu that the publisher considers buried.
- Tiles served to third parties โ in the tile service's metadata and in the reference client, because your users will build maps from it.
- Data download โ in a file that travels with the data, and in the dataset's metadata record.
- Printed report โ on the figure, or in a credits section the figure refers to.
3. Say what you changed
Copernicus requires "modified" when the data has been processed, and several national licences ask you to state that the work is derived and not endorsed by the publisher. A line such as "derived from X; the analysis is the author's" satisfies both and prevents the map being read as official.
4. Get the OpenStreetMap case right
OSM requires "ยฉ OpenStreetMap contributors" and, for anything a reasonable person would call a map, a link to the copyright page. If you publish a derivative database, you must also state that it is available under the ODbL. A produced work โ an image, a report โ needs the credit and not the ODbL notice.
5. Keep one notice per source, not per layer
A map with six OSM-derived layers needs one OSM credit. Deduplicate when assembling.
6. Carry the year
UK public-sector attributions include the year of the copyright and database right, and the year to use is the year of the data, not the year you made the map.
7. Automate the assembly
Store the string in the layer's metadata record and build the credit line at render time. Typing it into a template is how it goes stale when a layer is swapped.
Code examples
Example 1 โ attach the notice to the layer, not the map
import json, pathlib, geopandas as gpd
def load_with_attribution(path):
gdf = gpd.read_file(path)
meta_path = pathlib.Path(str(path).rsplit(".", 1)[0] + ".meta.json")
meta = json.loads(meta_path.read_text()) if meta_path.exists() else {}
gdf.attrs["attribution"] = meta.get("attribution")
gdf.attrs["licence"] = meta.get("licence")
return gdf
layers = [load_with_attribution(p) for p in ("roads.gpkg", "flood.gpkg", "coast.gpkg")]
missing = [i for i, g in enumerate(layers) if not g.attrs.get("attribution")]
if missing:
raise ValueError(f"layers with no attribution string: {missing}")
GeoDataFrame.attrs survives most pandas operations and is the cheapest place to carry this through a plotting function.
Example 2 โ render the credit onto a matplotlib figure
import matplotlib.pyplot as plt
def add_credit(ax, layers, fontsize=6):
seen = {}
for layer in layers:
text = layer.attrs.get("attribution")
if text:
seen.setdefault(text, None)
ax.annotate(" ยท ".join(seen), xy=(0.005, 0.005), xycoords="axes fraction",
fontsize=fontsize, color="#333333", ha="left", va="bottom",
bbox=dict(boxstyle="square,pad=0.25", fc="white", ec="none", alpha=0.75))
fig, ax = plt.subplots(figsize=(8, 6))
for layer in layers:
layer.plot(ax=ax)
add_credit(ax, layers)
Six points is legible on a printed figure and small enough not to compete with the map. Test it at the size the map will actually be used.
Example 3 โ the notice that travels with a download
import pathlib, zipfile, datetime
def package_with_notices(files, layers, out="delivery.zip"):
notices = []
for layer in layers:
notices.append(f"{layer.attrs.get('name', 'layer')}: "
f"{layer.attrs['attribution']} ({layer.attrs['licence']})")
text = ("ATTRIBUTION AND LICENCE NOTICES\n"
f"Packaged {datetime.date.today().isoformat()}\n\n" + "\n".join(notices) + "\n")
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for f in files:
z.write(f, pathlib.Path(f).name)
z.writestr("ATTRIBUTION.txt", text)
return out
Explanation
Why attribution is the clause that is enforced
It is the only licence condition a publisher can check by looking at your output. Share-alike obligations and non-commercial restrictions require somebody to investigate; a missing credit on a published map is visible immediately, which is why almost every enforcement conversation starts there.
Why "modified" matters for Copernicus
The Copernicus terms distinguish unmodified data from data you have processed. If you have composited, masked, reprojected or indexed Sentinel imagery โ which is to say, if you have used it โ the notice must say modified. The wording exists so that a reader does not attribute your processing decisions to the programme.
Why the OpenStreetMap notice is two requirements
The credit line identifies the source; the link to the copyright page tells the reader what the licence is and lets them find the contributors. On a print map where a link is impossible, the standard practice is to spell out openstreetmap.org/copyright. The ODbL notice is only required when what you publish is a database.
Why placement is specified rather than left to you
A notice that a user cannot see is not a notice. Every licence in this guide describes where it must appear, and "in a metadata file distributed alongside the image" is not one of the permitted answers for a published map.
Edge cases or notes
- Basemap tiles have their own attribution. The tile provider and the data are often different parties.
- Deduplicate, but do not merge. One credit per source; do not invent a combined wording.
- The year is the data's year. Not the year of publication.
- Screenshots inherit the requirement. A screenshot of a map in a slide deck still needs the credit.
- Print size matters. Legible means legible at the printed size, not on screen.
- Geocoding providers usually require a notice too. Check the API terms, not only the data licence.
- Attribution is not a substitute for a licence check. Crediting a source does not grant you the right to use it.
- Keep the strings in one file. Six copies in six templates will disagree within a year.
Internal links
- Open data licences explained for spatial data โ the clauses the notices satisfy
- How to check licence compatibility before combining datasets โ the other half of the check
- Spatial metadata explained: what a dataset must tell you โ where the attribution string is stored
- Map elements explained โ where the credit sits among the other furniture
- How to add a scale bar and north arrow in Python โ the same layout problem
- How to add a basemap in GeoPandas with contextily โ basemap attribution in practice
- How to package GIS deliverables in Python โ shipping the notice with the data
- A downloaded layer has no licence you can find โ when there is no string to store
FAQ
What attribution does OpenStreetMap require?
"ยฉ OpenStreetMap contributors", with a link to openstreetmap.org/copyright where a link is possible. If you publish a derivative database rather than a map, you must also state that it is available under the ODbL.
Where does the attribution have to appear?
On the work itself: on a static map image, in a web map's interface, and in a file that travels with a data download. A metadata field alone does not satisfy any of them.
Do I need to say I modified the data?
For Copernicus, yes โ the notice must read "modified Copernicus Sentinel data". Several other licences ask you to state that the work is derived and not endorsed by the publisher.
Which year goes in a UK public-sector notice?
The year of the data, not the year you made the map: "Contains OS data ยฉ Crown copyright and database right 2026".
Can I shorten the required wording?
No. The clause specifies a string; a paraphrase is not compliance. Store the exact text with the layer.
How do I handle six layers from the same source?
One credit per source. Deduplicate when you assemble the line.