---
name: aqicn-air-quality-map-embed
description: Embed the World Air Quality Index (aqicn.org) real-time air quality map in a web page, and set or update its location, center (latitude/longitude), zoom, language and size. Use when a user wants to add a live air quality / pollution / AQI map to a website, blog, CMS page or web app, or to change an embedded aqicn.org map.
---

# Embedding the aqicn.org real-time air quality map

The World Air Quality Index project (aqicn.org) provides an embeddable real-time
air quality map ("maplet"): a full-size interactive map with the live Air Quality
Index of the monitoring stations around a location. It is embedded with a single
script tag (or a plain iframe), needs no API key, and contains no ads.

- Human documentation and interactive builder: https://aqicn.org/maplet/
  (the location can be preset in the hash: https://aqicn.org/maplet/#location:london)
- These instructions (Agent Skill format): https://aqicn.org/maplet/SKILL.md

## Quick start

Put this tag where the map should appear. The map is inserted right after the tag:

```html
<script src="https://aqicn.org/maplet/embed.v1.js"
        data-city="london"
        data-height="450"></script>
```

To center the map on a precise position, add `data-lat`, `data-lng` and `data-zoom`:

```html
<script src="https://aqicn.org/maplet/embed.v1.js"
        data-city="london"
        data-lat="51.5074" data-lng="-0.1278" data-zoom="11"
        data-height="450"></script>
```

## Attributes

All attributes are optional, except `data-dom-target` when the script is loaded
asynchronously.

| Attribute | Value | Default |
|---|---|---|
| `data-city` | Location (see "Locations" below), eg `london`, `paris`, `beijing`, `greece`, `united-kingdom/wandsworth-lavender-hill-clapham-jct` | `world` |
| `data-lat`, `data-lng` | Center of the map, in decimal degrees: latitude -90..90, longitude -180..180. Both are required, otherwise both are ignored. | center of the location |
| `data-zoom` | Zoom level, 1 (world) to 18 (street), decimals allowed (eg `11.5`). Only used together with `data-lat` and `data-lng`. | `11` |
| `data-lang` | Language of the map, as a 2-letter aqicn.org code (see "Languages" below) | English |
| `data-width` | A number (pixels) or a CSS size with unit `px`, `%`, `vh`, `vw`, `em` or `rem`, eg `600`, `600px`, `100%` | `100%` |
| `data-height` | Same format as `data-width`, eg `450`, `450px`, `60vh` | `450px` |
| `data-dom-target` | Id (with or without `#`) or CSS selector of the element in which the map is inserted | none: inserted right after the script tag |

Invalid values are ignored (the default is used instead): non numeric or out of
range coordinates, sizes without a supported unit, unknown languages.

### Locations

`data-city` takes the location path of the aqicn.org map pages: for
`https://aqicn.org/map/<location>/`, use `<location>`. Examples:

- cities: `london`, `paris`, `beijing`, `delhi`, `los-angeles`
- countries and regions: `greece`, `france`, `europe`, `asia`, `world`
- a specific station: `united-kingdom/wandsworth-lavender-hill-clapham-jct`

To find the location of a place, open its map on aqicn.org (eg search for the
city, then follow the "map" link) and copy the path after `/map/`.

If the location is unknown and no `data-lat`/`data-lng` are given, the iframe
shows an "Unknown location" error page instead of the map. If valid
`data-lat`/`data-lng` are given, the map is always displayed, centered on these
coordinates, even when the location is unknown. So when you only know the
coordinates of a place, set them and use any plausible location name.

### Languages

`en` (default), `fr`, `es`, `de`, `pt`, `pl`, `ru`, `it`, `nl`, `cn` (Chinese,
simplified), `hk` (Chinese, traditional), `jp` (Japanese), `kr` (Korean),
`vn` (Vietnamese), `id`, `fa`, `th`, `hu`, `el`, `ro`, `bg`, `ur`, `hi`, `ar`,
`sr`, `bn`, `bs`, `hr`, `tr`, `uk`, `cs`, `be`, `km`, `lo`.

Note the aqicn.org codes for Chinese, Japanese, Korean and Vietnamese: `cn`,
`hk`, `jp`, `kr`, `vn` (not `zh`, `ja`, `ko`, `vi`).

## Asynchronous loading

With `async` or `defer`, or when the script tag is inserted from javascript, the
script does not run at its position in the page, so `data-dom-target` is
REQUIRED. Without it, nothing is inserted and the console shows
`[aqicn maplet] the data-dom-target attribute is required when the script is loaded asynchronously`.

```html
<div id="aqi-map"></div>

<script async src="https://aqicn.org/maplet/embed.v1.js"
        data-city="paris"
        data-dom-target="aqi-map"
        data-height="450"></script>
```

The target element can be anywhere in the page, before or after the script tag.
If it does not exist yet when the script runs, the script waits for the page to
be loaded (`DOMContentLoaded`) before inserting the map.

From javascript (single page applications, tag managers):

```js
var s = document.createElement("script");
s.src = "https://aqicn.org/maplet/embed.v1.js";
s.setAttribute("data-city", "london");
s.setAttribute("data-dom-target", "aqi-map");
document.head.appendChild(s);
```

Several maps can be embedded on the same page: use one script tag per map.

## Embedding with a plain iframe

When scripts can not be added (some CMS, email-like editors, markdown renderers
that allow iframes), use the map page directly:

```html
<iframe src="https://aqicn.org/maplet/london/?lat=51.5074&lng=-0.1278&zoom=11"
        width="100%" height="450" style="border:0" loading="lazy"
        title="Real-time air quality map"></iframe>
```

URL format: `https://aqicn.org/maplet/<location>/[<lang>/][?lat=<lat>&lng=<lng>&zoom=<zoom>]`

- `<lang>` is optional (English by default), eg `https://aqicn.org/maplet/paris/fr/`
- the query parameters follow the same rules as `data-lat`, `data-lng` and
  `data-zoom` (`lon` is accepted as an alias of `lng`)
- escape `&` as `&amp;` if the URL is written in an HTML attribute by a tool that requires it

## Updating the parameters of an embedded map

Each script tag is processed once (it is then marked with the
`data-aqicn-maplet` attribute), so changing the `data-*` attributes of an
existing tag has no effect. To change the location, view, language or size:

1. Recommended: insert the map with `data-dom-target`, and to update it, empty
   the target element and insert a new script tag with the new attributes:

```js
function showAirQualityMap(target, options) {
  var el = document.getElementById(target);
  el.innerHTML = "";
  var s = document.createElement("script");
  s.src = "https://aqicn.org/maplet/embed.v1.js";
  s.setAttribute("data-dom-target", target);
  s.setAttribute("data-city", options.city || "world");
  if (options.lat != null && options.lng != null) {
    s.setAttribute("data-lat", options.lat);
    s.setAttribute("data-lng", options.lng);
    if (options.zoom != null) s.setAttribute("data-zoom", options.zoom);
  }
  if (options.lang) s.setAttribute("data-lang", options.lang);
  if (options.height) s.setAttribute("data-height", options.height);
  document.head.appendChild(s);
}

showAirQualityMap("aqi-map", { city: "paris" });
showAirQualityMap("aqi-map", { city: "paris", lat: 48.8566, lng: 2.3522, zoom: 12, lang: "fr" });
```

2. Or change the `src` of the iframe directly (see "Embedding with a plain
   iframe" for the URL format). The size is the size of the iframe element
   (its `width`/`height` attributes or CSS).

3. In static HTML, edit the attributes of the script tag (or the URL of the
   iframe) and reload the page.

## Getting the view selected by the visitor

When the visitor moves or zooms the map, the map sends its view to the page
with `postMessage` (only after an interaction of the visitor, not when the map
is loaded):

```js
window.addEventListener("message", function (e) {
  if (e.origin !== "https://aqicn.org") return;
  var d = e.data;
  if (!d || d.source !== "aqicn-maplet" || d.type !== "view") return;
  /* d.lat, d.lng (4 decimals) and d.zoom (2 decimals), eg to save the view
   * and use it as data-lat, data-lng and data-zoom */
  console.log(d.lat, d.lng, d.zoom);
});
```

With several maps in the page, compare `e.source` with the `contentWindow` of
each map iframe to know which map sent the message.

## Good to know

- The page must be served over `http` or `https`. The map can not be displayed
  from a page opened as a local file (`file://`).
- In a sandboxed iframe, allow at least `allow-scripts allow-same-origin allow-popups`.
- Links in the map (station details, full map) open in a new tab, so visitors
  stay on the page.
- Wheel zooming requires Ctrl/Cmd + wheel (or the +/- buttons), so that the map
  does not capture the scrolling of the page.
- Do not remove or hide the attribution to the World Air Quality Index project
  and to the data sources.
- The map is free to embed, and needs no API key or registration.

## Checklist for generating embed code

1. Use `https://aqicn.org/maplet/embed.v1.js` (not another path or version).
2. Set `data-city` to an aqicn.org location path; when only coordinates are known,
   also set `data-lat` and `data-lng` (both), and optionally `data-zoom`.
3. Use `data-dom-target` whenever the tag is `async`/`defer` or inserted with javascript.
4. Give an explicit `data-height` (eg `450`) if the default 450px does not fit the layout.
5. Use the aqicn.org language codes (`cn`, `hk`, `jp`, `kr`, `vn` for Chinese,
   Japanese, Korean, Vietnamese).
6. To update the map later, re-insert a script tag (or change the iframe `src`):
   do not just change the attributes of an existing tag.
