# Map Links: [Home](../index.md) An interactive map on [OpenStreetMap](https://www.openstreetmap.org/) data: pins, contours, lines, circles and rectangles, address search, routing, measurement, track playback and GeoJSON interchange. This is an **SGApps.IO module on open source**: the module, its API, its vertex editor and its rate-limited provider layer are ours (Apache-2.0); the rendering is done by the vendored [Leaflet 1.9.4](../thirdparty/gui/map-leaflet.md) (BSD-2-Clause), and the data and services come from OpenStreetMap and whatever other providers the host configures. Nothing Leaflet-shaped crosses the API. Coordinates go in and out as plain numbers, latitude first, so a host never holds an `L.*` object -- except through the one deliberate escape hatch, `leafletMap()`. ## Loading the Module ```js Application.require("gadgets/map").then(function (SGMap) { // SGMap is a constructor function }); ``` **Dependencies:** `thirdparty/gui/map/leaflet`, `uri-load`. The geometry half is also a module of its own -- `gadgets/map/geo` -- with no DOM and no Leaflet, so form validation or a worker can require it without a map. ## What reaches third parties The map talks to services on the network. Hosts need this for their own privacy notice, so it is also available at runtime through `dataRecipients()`: | Service | Default | What it receives | |---|---|---| | Tiles | `tile.openstreetmap.org` | the viewer's IP address, the browser's `Referer`, and the tile coordinates of the area being looked at | | Geocoding | `nominatim.openstreetmap.org` | the viewer's IP address, the browser's `Referer`, and the searched text or the reverse-geocoded coordinates | | Routing | **none** | nothing, until the host configures a router | The module is built to keep the [OSMF tile usage policy](https://operations.osmfoundation.org/policies/tiles/) and the [Nominatim usage policy](https://operations.osmfoundation.org/policies/nominatim/): - the attribution control is always visible and cannot be emptied through the API; - nothing is prefetched, no tile is stored, and no cache-busting header is sent; - geocoding requests are serialised **at module level** -- at least 1.1 s apart across every map on the page -- and cached in memory by query text; - **there is no autocomplete**: a search leaves only on Enter or on the button; - routing has no default server. `route()` without `providers.route.url` fails with `ERR_NO_ROUTER` and sends nothing. The OSRM demo server is not permitted behind a paid product; a host may set it explicitly for development. For a map that touches no service at all -- a demo, a test, an offline host -- use the tile-less coordinate grid: `providers: { tiles: 'blank' }`. ## The module's own frame Identifying the application means sending a `Referer`, and **a document loaded from a `blob:` or `data:` URL sends none**. That is not a corner case: this repository's own playground renders every preview inside a `blob:` iframe, and [`gadgets/wrapper`](wrapper.md) is a `blob:` sandbox too. A map built directly in such a document asks OpenStreetMap for tiles with an empty `Referer`, and OSM answers with a blocked placeholder image -- *"App is not following the tile usage policy"* -- served as **HTTP 200**, so nothing reports an error and the map simply shows "Access blocked" in every tile. The module therefore ships its own document, `modules/gadgets/map/frame.html`, loaded through `module.resourceUrl('../frame.html')`. It is a real URL, so requests made from it carry a real `Referer` again. `frame.html` pulls in Leaflet and the module stylesheet itself, relative to its own location, and the map is built inside it. `frame` decides when that happens: | Value | Behaviour | |---|---| | `'auto'` (default) | the frame is used only when `location.protocol` is `blob:`, `data:` or `about:`, or the document's origin is opaque | | `true` | always use the frame | | `false` | never use it, even in a document that cannot identify itself | On an ordinary page nothing changes: the map is built directly in the host's container, the host's CSS can style it, and there is no extra document to load. When the frame **is** used: - `node()` returns the `