# 404 — Page Not Found https://docs.mapatlas.xyz/404 --- title: "404 — Page Not Found" --- # 404 — Page Not Found The page you're looking for doesn't exist. ## Looking for SDK examples? All examples are under **`/sdk/examples/`** — not `/web-sdk/`: | What you want | Correct URL | |---|---| | Simple Map (CDN/HTML) | [/sdk/examples/simple-map-cdn](/sdk/examples/simple-map-cdn) | | Simple Map (NPM) | [/sdk/examples/simple-map-npm](/sdk/examples/simple-map-npm) | | React Map | [/sdk/examples/react-map-example](/sdk/examples/react-map-example) | | Add a Marker | [/sdk/examples/add-a-marker](/sdk/examples/add-a-marker) | | Add a Popup | [/sdk/examples/add-a-popup](/sdk/examples/add-a-popup) | | 3D Building | [/sdk/examples/3d-building](/sdk/examples/3d-building) | | All examples | [/examples](/examples) | ## Quick links - [Getting Started](/getting-started) - [All Examples](/examples) - [Overview](/overview) - [AI Assistant Guide](/ai-assistant-guide) --- # For AI Assistants: MapMetrics Atlas API Guide https://docs.mapatlas.xyz/ai-assistant-guide # For AI Assistants: MapMetrics Atlas API Guide This documentation is optimized for AI code generation. Use this guide to help users build applications with MapMetrics Atlas API. --- ## 🚨 READ THIS FIRST — MOST COMMON AI MISTAKES ### ❌ MISTAKE 1: Using MapLibre instead of MapMetrics **MapMetrics is NOT MapLibre.** Do NOT use `maplibre-gl`, `maplibregl`, or any MapLibre CDN URLs. | ❌ WRONG | ✅ CORRECT | |----------|-----------| | `unpkg.com/maplibre-gl@4/dist/maplibre-gl.css` | `cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.css` | | `unpkg.com/maplibre-gl@4/dist/maplibre-gl.js` | `cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.js` | | `new maplibregl.Map(...)` | `new mapmetricsgl.Map(...)` | | `new maplibregl.Marker()` | `new mapmetricsgl.Marker()` | | `new maplibregl.Popup()` | `new mapmetricsgl.Popup()` | | `npm install maplibre-gl` | `npm install @mapmetrics/mapmetrics-gl` | | `import maplibregl from 'maplibre-gl'` | `import mapmetricsgl from '@mapmetrics/mapmetrics-gl'` | ### ❌ MISTAKE 2: Wrong documentation URLs This documentation is deployed at: **`https://docs.mapatlas.xyz`** All example pages follow this URL pattern: - `https://docs.mapatlas.xyz/sdk/examples/simple-map-cdn` - `https://docs.mapatlas.xyz/sdk/examples/add-a-marker` - `https://docs.mapatlas.xyz/overview/geocoder/autocomplete` ❌ Do NOT guess paths like `/simple-map`, `/maps/simple-map`, `/web-sdk/...` — these return 404. ✅ Valid top-level routes include: `/getting-started`, `/examples`, `/overview`, `/ai-assistant-guide` ### ❌ MISTAKE 3: `mapmetricsgl is not defined` error When loading the SDK via CDN **dynamically**, ALL map code MUST go inside `script.onload`: ```html
``` ### ❌ MISTAKE 4: Using v1 geocoder parameters against v2 endpoints MapMetrics Atlas has **two** geocoder generations live at once — v1 (`/forward-geocode/`, `/reverse-geocode/`) and v2 (`/v2/autocomplete/`, `/v2/retrieve/`, `/v2/retrieve-batch/`, `/v2/forward-geocode/`, `/v2/reverse-geocode/`). They are not interchangeable, and mixing their conventions produces **HTTP 200 with empty or wrong-shaped results** — no error, no exception, nothing that looks like a bug from the response status alone. #### v1 vs v2 geocoder — which one to use | | v1 (`/forward-geocode/`, `/reverse-geocode/`) | v2 (`/v2/...`) | |---|---|---| | Search-as-you-type UI | Not designed for this | ✅ `/v2/autocomplete/` + `/v2/retrieve/` — this is what it's for | | One-shot full-address geocode | ✅ `/forward-geocode/` | Also works: `/v2/forward-geocode/` | | Reverse geocode (point → address) | ✅ `/reverse-geocode/` | Also works: `/v2/reverse-geocode/` | | Query parameter | `text` | `q` | | Billing | Per request | Per **session** (see [Sessions](/overview/geocoder/v2/sessions)) — autocomplete only | | GeoJSON response shape | Default | Requires `format=pelias` explicitly | If you're building a search box with live suggestions as the user types, use v2 autocomplete + retrieve. If you're doing a single one-shot lookup of a full address string, either generation works, but v2 requires `format=pelias` to get the `FeatureCollection` shape v1 returns by default. #### v2-specific mistakes | ❌ WRONG | ✅ CORRECT | What happens if you get it wrong | |----------|-----------|-----------------------------------| | `?text=Nieuwezijds+Voorburgwal+147` on `/v2/autocomplete/` or `/v2/forward-geocode/` | `?q=Nieuwezijds+Voorburgwal+147` | HTTP 200, `"results": []`, `"q": ""` — looks like no matches, not a parameter error | | Reading `results[0].center` / `.geometry` / `.bbox` from an autocomplete result | Call [`/v2/retrieve/`](/overview/geocoder/v2/retrieve) with the suggestion's `ord` to get `center` | `undefined` — v2 suggestions deliberately carry no coordinates | | `/v2/retrieve/?id=osm:ext:...` | `/v2/retrieve/?ord=128371&country=nl&layer=address` | `404` on every layer — retrieve is keyed on `ord`, not `id` | | Omitting `session_token` on autocomplete calls | Reuse one `session_token` across every keystroke of a search, see [Sessions](/overview/geocoder/v2/sessions) | Each keystroke bills as its own session — roughly 5× the cost of one shared session | | `/v2/forward-geocode/?q=...` or `/v2/reverse-geocode/?point.lat=...&point.lon=...` without `format=pelias` (or with any other value) | Always pass `format=pelias` | Silently returns a flat, non-GeoJSON shape instead of the documented `FeatureCollection` — code expecting `features[0]` breaks | | `/v2/retrieve-batch/?ord=1&ord=2` or `?ords=1,2` or `?ids=1,2` | `/v2/retrieve-batch/?items=%5B%7B...%7D%5D` — one `items` param holding a URL-encoded JSON array of `{country, layer, ord, hn?}` | HTTP 200, `count: 0` — no error, no results | Full v2 reference: [Autocomplete](/overview/geocoder/v2/autocomplete), [Retrieve](/overview/geocoder/v2/retrieve), [Forward Geocode](/overview/geocoder/v2/forwardgeocode), [Reverse Geocode](/overview/geocoder/v2/reversegeocode), [Sessions](/overview/geocoder/v2/sessions). Auth details: [API Keys & Security](/overview/api-keys). --- ## 🤖 Quick Start for AI ### Authentication Pattern **ALL** API requests require authentication: - REST APIs: Add `?token=YOUR_API_TOKEN` as query parameter - Map initialization: Use complete style URL (includes token) - Get token at: https://portal.mapmetrics.org/ ### Base URLs & Package Names ``` Docs site: https://docs.mapatlas.xyz REST API: https://gateway.mapmetrics-atlas.net CDN JS: https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.js CDN CSS: https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.css NPM Package: @mapmetrics/mapmetrics-gl ⚠️ USE THIS, NOT maplibre-gl! Global variable (CDN): mapmetricsgl ⚠️ NOT maplibregl! ``` **⚠️ CRITICAL: Correct Package Name** When generating React or NPM-based code: - ✅ **CORRECT**: `npm install @mapmetrics/mapmetrics-gl` - ✅ **CORRECT**: `import mapmetricsgl from '@mapmetrics/mapmetrics-gl'` - ❌ **WRONG**: `npm install maplibre-gl` (This is the base library, NOT MapMetrics!) - ❌ **WRONG**: `import maplibregl from 'maplibre-gl'` **Why this matters:** - MapMetrics is built on MapLibre but has custom features and optimizations - Using `maplibre-gl` directly will NOT work with MapMetrics style URLs - Always use `@mapmetrics/mapmetrics-gl` for MapMetrics projects ### ⚠️ CRITICAL: Correct URL Paths **DO NOT use `/web-sdk/` in documentation URLs**. All SDK examples are located under `/sdk/examples/`: ✅ **CORRECT PATHS** (use clean URLs without .html): - `/sdk/examples/simple-map-npm` ✅ - `/sdk/examples/simple-map-cdn` ✅ - `/sdk/examples/add-a-marker` ✅ - `/sdk/examples/react-map-example` ✅ - `/sdk/examples/3d-building` ✅ - `/sdk/examples/fly-to-location` ✅ - `/sdk/examples/fit-to-bounding-box` ✅ - `/sdk/examples/set-pitch-and-bearing` ✅ - `/sdk/examples/animate-camera-around-point` ✅ - `/sdk/examples/jump-to-locations` ✅ - `/sdk/examples/navigation-controls` ✅ - `/sdk/examples/disable-map-rotation` ✅ - `/sdk/examples/disable-scroll-zoom` ✅ - `/sdk/examples/fullscreen-map` ✅ - `/sdk/examples/toggle-interactions` ✅ - `/sdk/examples/animate-a-line` ✅ - `/sdk/examples/animate-point-along-route` ✅ - `/sdk/examples/data-driven-lines` ✅ - `/sdk/examples/draw-a-circle` ✅ - `/sdk/examples/gradient-line` ✅ - `/sdk/examples/filter-within-layer` ✅ - `/sdk/examples/add-pattern-to-polygon` ✅ - `/sdk/examples/add-geojson-line` ✅ - `/sdk/examples/add-geojson-polygon` ✅ - `/sdk/examples/draw-geojson-points` ✅ - `/sdk/examples/multiple-geometries` ✅ - `/sdk/examples/html-clusters` ✅ - `/sdk/examples/arc-layer` ✅ - `/sdk/examples/hexagon-layer` ✅ - `/sdk/examples/animate-point` ✅ - `/sdk/examples/animate-marker` ✅ - `/sdk/examples/update-feature-realtime` ✅ - `/sdk/examples/display-popup` ✅ - `/sdk/examples/show-polygon-info-on-click` ✅ - `/sdk/examples/get-features-under-mouse` ✅ - `/sdk/examples/measure-distances` ✅ - `/sdk/examples/filter-by-text-input` ✅ - `/sdk/examples/filter-by-toggle-list` ✅ - `/sdk/examples/fit-to-linestring` ✅ - `/sdk/examples/restrict-map-panning` ✅ - `/sdk/examples/add-layer-below-labels` ✅ - `/sdk/examples/change-layer-color` ✅ - `/sdk/examples/draggable-point` ✅ - `/sdk/examples/customize-camera-animations` ✅ - `/sdk/examples/customize-map-transform-constrain` ✅ - `/sdk/examples/fly-to-location-on-scroll` ✅ - `/sdk/examples/offset-vanishing-point-padding` ✅ - `/sdk/examples/render-world-copies` ✅ - `/sdk/examples/slowly-fly-to-location` ✅ - `/sdk/examples/sync-multiple-maps` ✅ - `/sdk/examples/game-controls-navigation` ✅ - `/sdk/examples/add-an-icon-to-the-map` ✅ - `/sdk/examples/add-generated-icon` ✅ - `/sdk/examples/add-animated-icon` ✅ - `/sdk/examples/add-stretchable-image` ✅ - `/sdk/examples/add-custom-icons-markers` ✅ - `/sdk/examples/display-remote-svg-symbol` ✅ - `/ai-assistant-guide` ✅ (this page) - `/overview/common-errors` ✅ ❌ **WRONG PATHS** (will return 404 errors): - `/web-sdk/simple-map-npm` ← WRONG (wrong directory) - `/examples/add-a-marker` ← WRONG (missing /sdk/) - `/react/map-example` ← WRONG (wrong path) - `/ai-assistant-guide.html` ← WRONG (don't use .html in URLs) **URL Format Rules:** - ✅ Use clean URLs: `/path/to/page` (no .html extension) - ✅ Paths start with `/sdk/examples/` or `/overview/` - ❌ Don't add `.html` extension to URLs - ❌ Don't use `/web-sdk/` directory ## 📋 Available Capabilities ### Web SDK (JavaScript/React) - **Simple Map Setup**: [/sdk/examples/simple-map-cdn](/sdk/examples/simple-map-cdn), [/sdk/examples/simple-map-npm](/sdk/examples/simple-map-npm) - **React Integration**: [/sdk/examples/react-map-example](/sdk/examples/react-map-example) - **Markers**: [/sdk/examples/add-a-marker](/sdk/examples/add-a-marker) - **Popups**: [/sdk/examples/add-a-popup](/sdk/examples/add-a-popup), [/sdk/examples/display-popup](/sdk/examples/display-popup) - **Clustering**: [/sdk/examples/add-a-cluster](/sdk/examples/add-a-cluster), [/sdk/examples/html-clusters](/sdk/examples/html-clusters) - **Heatmaps**: [/sdk/examples/add-a-heatmap](/sdk/examples/add-a-heatmap) - **Arc Layer (Flight Routes / Flow Maps)**: [/sdk/examples/arc-layer](/sdk/examples/arc-layer) - **Hexagon Layer (Data Aggregation / Density)**: [/sdk/examples/hexagon-layer](/sdk/examples/hexagon-layer) - **3D Buildings**: [/sdk/examples/3d-building](/sdk/examples/3d-building) - **GeoJSON Lines**: [/sdk/examples/add-geojson-line](/sdk/examples/add-geojson-line) - **GeoJSON Polygons**: [/sdk/examples/add-geojson-polygon](/sdk/examples/add-geojson-polygon) - **GeoJSON Points**: [/sdk/examples/draw-geojson-points](/sdk/examples/draw-geojson-points) - **Animations**: [/sdk/examples/animate-point](/sdk/examples/animate-point), [/sdk/examples/animate-marker](/sdk/examples/animate-marker), [/sdk/examples/update-feature-realtime](/sdk/examples/update-feature-realtime) - **Filtering**: [/sdk/examples/filter-by-text-input](/sdk/examples/filter-by-text-input), [/sdk/examples/filter-by-toggle-list](/sdk/examples/filter-by-toggle-list) - **Distances**: [/sdk/examples/measure-distances](/sdk/examples/measure-distances), [/sdk/examples/fit-to-linestring](/sdk/examples/fit-to-linestring) ### Camera & Navigation - **Fly to Location** (`flyTo`): [/sdk/examples/fly-to-location](/sdk/examples/fly-to-location) — smooth animated camera flight to any coordinate - **Fit to Bounding Box** (`fitBounds`): [/sdk/examples/fit-to-bounding-box](/sdk/examples/fit-to-bounding-box) — auto-zoom to show all markers or a region - **Set Pitch & Bearing** (`setPitch`, `setBearing`): [/sdk/examples/set-pitch-and-bearing](/sdk/examples/set-pitch-and-bearing) — tilt and rotate camera for 3D views - **Animate Camera Around Point** (`easeTo` + `requestAnimationFrame`): [/sdk/examples/animate-camera-around-point](/sdk/examples/animate-camera-around-point) — continuous orbiting rotation - **Jump to Series of Locations** (`jumpTo`, `flyTo`): [/sdk/examples/jump-to-locations](/sdk/examples/jump-to-locations) — navigate through multiple locations instantly or animated - **Customize Camera Animations** (`flyTo` + `easeTo` options): [/sdk/examples/customize-camera-animations](/sdk/examples/customize-camera-animations) — control speed, easing, curve, and duration - **Slowly Fly to a Location** (`speed` + `curve`): [/sdk/examples/slowly-fly-to-location](/sdk/examples/slowly-fly-to-location) — cinematic slow-motion camera flight - **Fly to Location on Scroll** (`IntersectionObserver` + `flyTo`): [/sdk/examples/fly-to-location-on-scroll](/sdk/examples/fly-to-location-on-scroll) — scroll-driven story maps - **Offset Vanishing Point with Padding** (`padding`): [/sdk/examples/offset-vanishing-point-padding](/sdk/examples/offset-vanishing-point-padding) — shift map center for sidebars/panels - **Render World Copies** (`renderWorldCopies`): [/sdk/examples/render-world-copies](/sdk/examples/render-world-copies) — toggle world tiling on/off - **Sync Multiple Maps** (`jumpTo` + `move` event): [/sdk/examples/sync-multiple-maps](/sdk/examples/sync-multiple-maps) — keep two maps in sync for comparison - **Constrain Map Transform** (`setMaxBounds` + `transformCameraUpdate`): [/sdk/examples/customize-map-transform-constrain](/sdk/examples/customize-map-transform-constrain) — restrict panning and zoom - **Game-Like Controls** (`panBy` + keyboard events): [/sdk/examples/game-controls-navigation](/sdk/examples/game-controls-navigation) — WASD/arrow key map navigation **When to use which:** | User asks for... | Use this example | |---|---| | "fly to", "navigate to", "animate to location" | `fly-to-location` | | "show all markers", "fit map", "zoom to results" | `fit-to-bounding-box` | | "tilt map", "3D view", "pitch", "rotate", "bearing" | `set-pitch-and-bearing` | | "rotate around", "orbit", "spin map", "hero animation" | `animate-camera-around-point` | | "city tour", "location buttons", "jump between", "auto tour" | `jump-to-locations` | | "custom animation", "easing", "flyTo speed", "animation duration" | `customize-camera-animations` | | "slow fly", "cinematic", "slow animation", "slow camera" | `slowly-fly-to-location` | | "scroll map", "story map", "scroll to location", "narrative map" | `fly-to-location-on-scroll` | | "sidebar map", "panel map", "offset center", "map padding" | `offset-vanishing-point-padding` | | "world copies", "repeat world", "tile world", "antimeridian" | `render-world-copies` | | "sync maps", "compare maps", "side by side", "mirror map" | `sync-multiple-maps` | | "restrict panning", "limit bounds", "constrain map", "lock region" | `customize-map-transform-constrain` | | "keyboard navigate", "WASD", "game controls", "arrow keys map" | `game-controls-navigation` | ### User Interaction - **Popup on Click** (`on('click')` + `Popup`): [/sdk/examples/popup-on-click](/sdk/examples/popup-on-click) — show info popup when user clicks the map or a marker - **Popup on Hover** (`on('mouseenter')` + `Popup`): [/sdk/examples/popup-on-hover](/sdk/examples/popup-on-hover) — show tooltip when hovering over markers - **Hover Effect** (`setFeatureState`): [/sdk/examples/hover-effect](/sdk/examples/hover-effect) — highlight GeoJSON features on hover - **Mouse Coordinates** (`on('mousemove')`): [/sdk/examples/mouse-coordinates](/sdk/examples/mouse-coordinates) — display real-time lng/lat of mouse position - **Draggable Marker** (`draggable: true` + `on('dragend')`): [/sdk/examples/draggable-marker](/sdk/examples/draggable-marker) — let users drag markers to new positions - **Locate the User** (`GeolocateControl` / `navigator.geolocation`): [/sdk/examples/locate-user](/sdk/examples/locate-user) — show user's GPS location on the map **When to use which:** | User asks for... | Use this example | |---|---| | "popup on click", "show info on click", "click to show details" | `popup-on-click` | | "popup on hover", "tooltip", "hover info", "mouseover" | `popup-on-hover` | | "hover highlight", "highlight feature", "hover effect" | `hover-effect` | | "mouse position", "show coordinates", "get lng lat", "cursor position" | `mouse-coordinates` | | "draggable marker", "drag pin", "move marker", "drag and drop" | `draggable-marker` | | "my location", "gps", "user location", "geolocation", "locate me", "where am i" | `locate-user` | ### Geometry & Features - **Add a GeoJSON Line** (`addSource` + `addLayer` + `LineString`): [/sdk/examples/add-geojson-line](/sdk/examples/add-geojson-line) — draw a path or route from coordinates - **Add a GeoJSON Polygon** (`fill` + `line` layers): [/sdk/examples/add-geojson-polygon](/sdk/examples/add-geojson-polygon) — draw a filled shape with an outline - **Draw GeoJSON Points** (`FeatureCollection` + `circle` layer): [/sdk/examples/draw-geojson-points](/sdk/examples/draw-geojson-points) — plot multiple points from a dataset - **Multiple Geometries from One Source** (`geometry-type` filter): [/sdk/examples/multiple-geometries](/sdk/examples/multiple-geometries) — render points, lines, polygons from one GeoJSON source - **Cluster Points with Custom Styling** (`cluster: true` + step expressions): [/sdk/examples/html-clusters](/sdk/examples/html-clusters) — group dense points into size-based clusters - **Arc Layer — Flight Routes / Flow Maps** (bezier curves + `line` layer + dash animation): [/sdk/examples/arc-layer](/sdk/examples/arc-layer) — animated curved arcs connecting origin/destination pairs - **Hexagon Layer — Data Aggregation** (`fill-extrusion` + hex grid binning): [/sdk/examples/hexagon-layer](/sdk/examples/hexagon-layer) — 3D hexagonal grid aggregating point density with hover popup **When to use which:** | User asks for... | Use this example | |---|---| | "draw line", "add route", "linestring", "path on map" | `add-geojson-line` | | "draw polygon", "fill area", "shape on map", "geojson polygon" | `add-geojson-polygon` | | "plot points", "add multiple markers", "geojson points", "circle layer" | `draw-geojson-points` | | "mixed geometries", "points and polygons", "single source", "multiple layer types" | `multiple-geometries` | | "cluster", "group points", "cluster count", "expand cluster" | `html-clusters` | | "arc layer", "flight routes", "connections", "flow map", "origin destination", "animated arcs" | `arc-layer` | | "hexagon layer", "hex grid", "density map", "aggregation", "accident heatmap", "data binning" | `hexagon-layer` | ### Lines & Polygons - **Animate a Line** (`setData` + `requestAnimationFrame`): [/sdk/examples/animate-a-line](/sdk/examples/animate-a-line) — progressively draw a line path step by step - **Animate a Point Along a Route** (`setData` + interpolation): [/sdk/examples/animate-point-along-route](/sdk/examples/animate-point-along-route) — move a marker smoothly along a route - **Style Lines with Data-Driven Property** (`match`, `step`, `interpolate` expressions): [/sdk/examples/data-driven-lines](/sdk/examples/data-driven-lines) — color/width lines based on feature properties - **Draw a Gradient Line** (`line-gradient` + `lineMetrics: true`): [/sdk/examples/gradient-line](/sdk/examples/gradient-line) — smooth color transition along a line - **Draw a Circle** (`circle` layer / polygon): [/sdk/examples/draw-a-circle](/sdk/examples/draw-a-circle) — add circle shapes using pixel radius or geographic radius - **Add a Pattern to a Polygon** (`fill-pattern` + `addImage`): [/sdk/examples/add-pattern-to-polygon](/sdk/examples/add-pattern-to-polygon) — fill polygon with repeating texture - **Filter Within a Layer** (`setFilter`): [/sdk/examples/filter-within-layer](/sdk/examples/filter-within-layer) — show/hide features dynamically without reloading data - **Add Layer Below Labels** (`beforeId`): [/sdk/examples/add-layer-below-labels](/sdk/examples/add-layer-below-labels) — insert layer so text labels stay readable - **Change Layer Color at Runtime** (`setPaintProperty`): [/sdk/examples/change-layer-color](/sdk/examples/change-layer-color) — update layer styles dynamically **When to use which:** | User asks for... | Use this example | |---|---| | "animate line", "draw route", "path animation", "trace route" | `animate-a-line` | | "moving marker", "animate along route", "vehicle tracking" | `animate-point-along-route` | | "color by value", "style by property", "data driven", "line width by speed" | `data-driven-lines` | | "gradient line", "color gradient", "line-gradient", "progress color" | `gradient-line` | | "draw circle", "radius area", "circle on map", "coverage area" | `draw-a-circle` | | "pattern fill", "hatch polygon", "texture polygon", "fill-pattern" | `add-pattern-to-polygon` | | "filter features", "show only", "hide features", "setFilter" | `filter-within-layer` | | "layer below labels", "insert below text", "beforeId", "polygon under labels" | `add-layer-below-labels` | | "change color", "update style", "setPaintProperty", "runtime style" | `change-layer-color` | ### Animations - **Animate a Point** (`setData` + `requestAnimationFrame`): [/sdk/examples/animate-point](/sdk/examples/animate-point) — move a GeoJSON point continuously with smooth animation - **Animate a Marker** (`Marker` + `setLngLat`): [/sdk/examples/animate-marker](/sdk/examples/animate-marker) — smoothly move a Marker element along a path - **Update a Feature in Realtime** (`setInterval` + `setData`): [/sdk/examples/update-feature-realtime](/sdk/examples/update-feature-realtime) — live position updates with a trail **When to use which:** | User asks for... | Use this example | |---|---| | "animate point", "moving dot", "orbit animation", "requestAnimationFrame" | `animate-point` | | "animate marker", "moving marker", "smooth marker movement" | `animate-marker` | | "realtime update", "live tracking", "setInterval", "websocket position", "moving vehicle" | `update-feature-realtime` | ### Popups & Info - **Display a Popup** (`Popup` + `setHTML`): [/sdk/examples/display-popup](/sdk/examples/display-popup) — show popup at a location programmatically or on click - **Show Polygon Info on Click** (`on('click')` + `Popup`): [/sdk/examples/show-polygon-info-on-click](/sdk/examples/show-polygon-info-on-click) — click polygon to see its properties - **Get Features Under Mouse** (`queryRenderedFeatures`): [/sdk/examples/get-features-under-mouse](/sdk/examples/get-features-under-mouse) — inspect features at click position - **Measure Distances** (Haversine formula + `on('click')`): [/sdk/examples/measure-distances](/sdk/examples/measure-distances) — click to place points and measure cumulative distance **When to use which:** | User asks for... | Use this example | |---|---| | "show popup", "display popup", "programmatic popup", "popup at location" | `display-popup` | | "click polygon", "polygon info", "click to show properties" | `show-polygon-info-on-click` | | "features under mouse", "inspect features", "queryRenderedFeatures", "click to inspect" | `get-features-under-mouse` | | "measure distance", "ruler", "haversine", "distance between points" | `measure-distances` | ### Filtering & Search - **Filter by Text Input** (`setFilter` + `in` expression): [/sdk/examples/filter-by-text-input](/sdk/examples/filter-by-text-input) — live search filter as user types - **Filter by Toggle List** (`setFilter` + `literal` array): [/sdk/examples/filter-by-toggle-list](/sdk/examples/filter-by-toggle-list) — toggle category buttons to show/hide feature types **When to use which:** | User asks for... | Use this example | |---|---| | "search filter", "text search on map", "filter as you type", "live search" | `filter-by-text-input` | | "toggle categories", "category buttons", "show/hide types", "filter by category" | `filter-by-toggle-list` | ### Camera & Bounds - **Fit Map to a LineString** (`fitBounds` + `LngLatBounds`): [/sdk/examples/fit-to-linestring](/sdk/examples/fit-to-linestring) — auto-zoom to fit a route or path - **Restrict Map Panning** (`maxBounds` / `setMaxBounds`): [/sdk/examples/restrict-map-panning](/sdk/examples/restrict-map-panning) — limit map to a geographic region **When to use which:** | User asks for... | Use this example | |---|---| | "fit to route", "fit linestring", "auto zoom to path", "show full route" | `fit-to-linestring` | | "restrict panning", "limit map area", "maxBounds", "lock to region" | `restrict-map-panning` | ### Map Controls - **Navigation Controls** (`NavigationControl`, `ScaleControl`, `FullscreenControl`): [/sdk/examples/navigation-controls](/sdk/examples/navigation-controls) — add zoom, compass, scale bar, fullscreen buttons - **Disable Map Rotation** (`dragRotate.disable()`): [/sdk/examples/disable-map-rotation](/sdk/examples/disable-map-rotation) — lock map orientation, prevent rotation - **Disable Scroll Zoom** (`scrollZoom.disable()`): [/sdk/examples/disable-scroll-zoom](/sdk/examples/disable-scroll-zoom) — prevent accidental zoom when scrolling page - **Fullscreen Map** (`FullscreenControl`): [/sdk/examples/fullscreen-map](/sdk/examples/fullscreen-map) — expand map to fill entire screen - **Toggle Map Interactions** (`scrollZoom`, `dragPan`, `dragRotate`): [/sdk/examples/toggle-interactions](/sdk/examples/toggle-interactions) — enable/disable any interaction at runtime **When to use which:** | User asks for... | Use this example | |---|---| | "add controls", "zoom buttons", "compass", "scale bar" | `navigation-controls` | | "disable rotation", "lock rotation", "fix map orientation", "no rotate" | `disable-map-rotation` | | "disable scroll zoom", "prevent zoom", "scroll page not map", "embed map" | `disable-scroll-zoom` | | "fullscreen", "expand map", "full screen button", "immersive map" | `fullscreen-map` | | "toggle interactions", "enable/disable", "readonly map", "lock interactions" | `toggle-interactions` | ### Advanced / User Interaction - **Create a Draggable Point** (`mousedown` + `mousemove` + `dragPan.disable()`): [/sdk/examples/draggable-point](/sdk/examples/draggable-point) — drag a GeoJSON point to new positions **When to use which:** | User asks for... | Use this example | |---|---| | "draggable point", "drag geojson", "drag to move", "interactive point editor" | `draggable-point` | ### Icons & Images - **Add an Icon to the Map** (`loadImage` + `addImage` + symbol layer): [/sdk/examples/add-an-icon-to-the-map](/sdk/examples/add-an-icon-to-the-map) — load external PNG as map icon - **Add a Generated Icon** (Canvas API + `addImage`): [/sdk/examples/add-generated-icon](/sdk/examples/add-generated-icon) — create icon programmatically with canvas - **Add an Animated Icon** (`addImage` + `updateImage` + `requestAnimationFrame`): [/sdk/examples/add-animated-icon](/sdk/examples/add-animated-icon) — pulsing canvas animation icon - **Add a Stretchable Image** (`stretchX` + `stretchY` + `content` + `icon-text-fit`): [/sdk/examples/add-stretchable-image](/sdk/examples/add-stretchable-image) — nine-patch style scalable icon - **Add Custom Icons with Markers** (HTML element + `Marker({ element })`): [/sdk/examples/add-custom-icons-markers](/sdk/examples/add-custom-icons-markers) — emoji/div/img as marker - **Display a Remote SVG Symbol** (fetch + Blob + Image + canvas + `addImage`): [/sdk/examples/display-remote-svg-symbol](/sdk/examples/display-remote-svg-symbol) — SVG icon on map **When to use which:** | User asks for... | Use this example | |---|---| | "add icon", "custom icon symbol", "icon from URL", "loadImage", "addImage" | `add-an-icon-to-the-map` | | "generate icon", "canvas icon", "programmatic icon", "draw icon" | `add-generated-icon` | | "animated icon", "pulsing icon", "blinking marker", "animate icon" | `add-animated-icon` | | "stretchable image", "nine-patch", "pill badge icon", "icon-text-fit", "scale icon" | `add-stretchable-image` | | "custom marker icon", "html marker", "emoji marker", "div marker", "image marker" | `add-custom-icons-markers` | | "svg icon", "svg symbol", "remote svg", "svg marker" | `display-remote-svg-symbol` | ### Labels & Text - **RTL Script Support** (`setRTLTextPlugin`): [/sdk/examples/rtl-support](/sdk/examples/rtl-support) — Arabic, Hebrew, Persian right-to-left text - **Building Color by Zoom** (`fill-extrusion` + `interpolate` + `zoom`): [/sdk/examples/building-color-zoom](/sdk/examples/building-color-zoom) — 3D building color changes with zoom - **Change Label Case** (`text-transform` on all symbol layers): [/sdk/examples/change-label-case](/sdk/examples/change-label-case) — uppercase/lowercase/default labels **When to use which:** | User asks for... | Use this example | |---|---| | "Arabic map", "RTL text", "Hebrew labels", "right-to-left", "setRTLTextPlugin" | `rtl-support` | | "building color", "3D building zoom", "fill-extrusion color", "zoom building" | `building-color-zoom` | | "uppercase labels", "lowercase text", "label case", "text-transform" | `change-label-case` | ### Terrain & Elevation > No three.js or external libraries needed — all built into MapMetrics GL. - **3D Terrain** (`raster-dem` + `terrain` + `TerrainControl`): [/sdk/examples/3d-terrain](/sdk/examples/3d-terrain) — push land surface into 3D using elevation tiles - **Sky, Fog and Terrain** (`sky` + `fog` + `terrain`): [/sdk/examples/sky-fog-terrain](/sdk/examples/sky-fog-terrain) — immersive sky background + atmospheric haze + 3D terrain - **Add a Hillshade Layer** (`hillshade` layer type): [/sdk/examples/add-a-hillshade-layer](/sdk/examples/add-a-hillshade-layer) — terrain shadow shading on a flat map - **Color Relief Layer** (hillshade with colored shadow/highlight): [/sdk/examples/add-a-color-relief-layer](/sdk/examples/add-a-color-relief-layer) — green valleys → brown hills → white peaks - **Contour Lines** (GeoJSON lines + `symbol-placement: line`): [/sdk/examples/add-contour-lines](/sdk/examples/add-contour-lines) — topographic elevation lines with labels - **Satellite Map with Terrain** (ESRI tiles + `raster-dem` + `terrain`): [/sdk/examples/satellite-terrain](/sdk/examples/satellite-terrain) — satellite imagery with 3D elevation **When to use which:** | User asks for... | Use this example | |---|---| | "3D terrain", "elevation 3D", "terrain map", "mountain 3D", "raster-dem" | `3d-terrain` | | "sky", "fog", "atmosphere", "terrain fog", "immersive map" | `sky-fog-terrain` | | "hillshade", "terrain shadow", "elevation shading", "hill shadow" | `add-a-hillshade-layer` | | "color relief", "hypsometric", "elevation color", "altitude color", "color by height" | `add-a-color-relief-layer` | | "contour lines", "topographic", "elevation lines", "isoline", "topo map" | `add-contour-lines` | | "satellite map", "aerial map", "satellite 3D", "satellite terrain", "ESRI imagery" | `satellite-terrain` | ### REST APIs - **Geocoding (v1)**: [/overview/geocoder/forwardgeocode](/overview/geocoder/forwardgeocode), [/overview/geocoder/reversegeocode](/overview/geocoder/reversegeocode) - **Geocoding (v2 — autocomplete/retrieve, forward/reverse)**: [/overview/geocoder/v2/autocomplete](/overview/geocoder/v2/autocomplete), [/overview/geocoder/v2/retrieve](/overview/geocoder/v2/retrieve), [/overview/geocoder/v2/forwardgeocode](/overview/geocoder/v2/forwardgeocode), [/overview/geocoder/v2/reversegeocode](/overview/geocoder/v2/reversegeocode), [/overview/geocoder/v2/sessions](/overview/geocoder/v2/sessions) - **API Keys & Security**: [/overview/api-keys](/overview/api-keys) - **Directions**: [/overview/directions/directions](/overview/directions/directions) - **Matrix**: [/overview/directions/matrix](/overview/directions/matrix) - **Isochrone**: [/overview/directions/isochrone](/overview/directions/isochrone) - **Optimization**: [/overview/directions/optimization](/overview/directions/optimization) - **Elevation**: [/overview/directions/elevation](/overview/directions/elevation) ## ⚠️ PACKAGE NAMING - READ THIS FIRST! **The #1 mistake AI tools make: Using the wrong package!** ### Correct Package ```bash npm install @mapmetrics/mapmetrics-gl ``` ```javascript import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; ``` ### WRONG Package (DO NOT USE!) ```bash # ❌ This is MapLibre, NOT MapMetrics! npm install maplibre-gl ``` ```javascript // ❌ This will NOT work with MapMetrics style URLs! import maplibregl from 'maplibre-gl'; ``` ### Why This Matters - MapMetrics GL is a **custom fork** of MapLibre with proprietary features - MapMetrics style URLs **only work** with `@mapmetrics/mapmetrics-gl` - Using `maplibre-gl` will cause authentication errors and missing features - The packages are **NOT interchangeable** ### Quick Check ✅ **Correct code always has:** - Package: `@mapmetrics/mapmetrics-gl` - Import: `mapmetricsgl` (not `maplibregl`) - NPM link: https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl ❌ **Wrong code has:** - Package: `maplibre-gl` or `@maplibre/maplibre-gl` - Import: `maplibregl` --- ## 🎯 Common Code Patterns ### Pattern 1: Basic Map (Vanilla JS with CDN - With Error Handling) ```html
``` ### Pattern 2: React Map Component (with Error Handling) ```jsx import React, { useEffect, useRef, useState } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; function MapComponent() { const mapContainerRef = useRef(null); const mapRef = useRef(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { if (!mapContainerRef.current || mapRef.current) return; const styleUrl = 'YOUR_STYLE_URL_WITH_TOKEN'; // Check if user forgot to replace the placeholder if (styleUrl === 'YOUR_STYLE_URL_WITH_TOKEN' || styleUrl.includes('YOUR_')) { setError('⚠️ Please replace YOUR_STYLE_URL_WITH_TOKEN with your actual style URL from https://portal.mapmetrics.org/'); setLoading(false); return; } try { const map = new mapmetricsgl.Map({ container: mapContainerRef.current, style: styleUrl, center: [longitude, latitude], zoom: 12 }); // Handle map load success map.on('load', () => { setLoading(false); setError(null); }); // Handle CRITICAL errors only (authentication, style loading failures) // Ignore non-critical errors like individual tile loading failures map.on('error', (e) => { console.error('Map error:', e); // Only show UI errors for critical failures that prevent map from working // Ignore tile loading errors and other minor issues that don't break the map const isCriticalError = e.error?.status === 401 || e.error?.message?.includes('401') || (e.error?.message?.includes('Failed to fetch') && !loading) === false || (e.sourceId === undefined && e.error); // Style loading errors have no sourceId if (isCriticalError) { setLoading(false); // Authentication errors (401) if (e.error?.message?.includes('401') || e.error?.status === 401) { setError('❌ Invalid API token. Get a valid token from https://portal.mapmetrics.org/'); } // Network/style URL errors during initial load else if (e.error?.message?.includes('Failed to fetch') && loading) { setError('❌ Cannot load map style. Check your style URL from https://portal.mapmetrics.org/'); } // Generic critical error before map loads else if (loading) { setError('❌ Map failed to load. Check your style URL and token at https://portal.mapmetrics.org/'); } // If map already loaded, just log the error (don't hide the map) else { console.warn('Map error after load (non-critical):', e); } } else { // Non-critical errors (tile loading, etc.) - just log them console.warn('Non-critical map error:', e); } }); mapRef.current = map; return () => { if (map) { map.remove(); mapRef.current = null; } }; } catch (err) { console.error('Map initialization error:', err); setError('❌ Failed to initialize map. Check your style URL from https://portal.mapmetrics.org/'); setLoading(false); } }, []); // Display error message if something went wrong if (error) { return (
{error}
Need help? Check the Discord
); } // Optional: Show loading state if (loading) { return (
Loading map...
); } return
; } export default MapComponent; ``` ### Pattern 3: Add Marker ```javascript // Vanilla JS const marker = new mapmetricsgl.Marker() .setLngLat([longitude, latitude]) .addTo(map); // With popup const marker = new mapmetricsgl.Marker() .setLngLat([longitude, latitude]) .setPopup(new mapmetricsgl.Popup().setHTML('

Location Name

')) .addTo(map); // Draggable marker const marker = new mapmetricsgl.Marker({ draggable: true }) .setLngLat([longitude, latitude]) .addTo(map); ``` ### Pattern 4: REST API Call (Directions) ```javascript // POST request to Directions API const response = await fetch('https://gateway.mapmetrics-atlas.net/directions/?token=YOUR_TOKEN', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ locations: [ { lat: 40.744014, lon: -73.990508 }, { lat: 40.752522, lon: -73.985015 } ], costing: 'auto' }) }); const data = await response.json(); // data.trip contains route information ``` ### Pattern 5: Geocoding (Forward) ```javascript // GET request to Forward Geocode API const address = encodeURIComponent('1600 Pennsylvania Avenue NW, Washington, DC'); const response = await fetch( `https://gateway.mapmetrics-atlas.net/forward-geocode/?token=YOUR_TOKEN&text=${address}` ); const data = await response.json(); // data.features[0].geometry.coordinates contains [lon, lat] ``` ### Pattern 6: Matrix API ```javascript // POST request to Matrix API const response = await fetch('https://gateway.mapmetrics-atlas.net/matrix/?token=YOUR_TOKEN', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sources: [ { lat: 40.744014, lon: -73.990508 }, { lat: 40.739735, lon: -73.979713 } ], targets: [ { lat: 40.752522, lon: -73.985015 }, { lat: 40.750117, lon: -73.983704 } ], costing: 'pedestrian' }) }); const data = await response.json(); // data.sources_to_targets contains time/distance matrix ``` ## 📊 API Response Structures ### Directions Response ```json { "trip": { "locations": [...], "legs": [ { "maneuvers": [ { "instruction": "Turn right onto Main Street", "time": 45.2, "length": 0.542 } ], "summary": { "time": 18734.567, "length": 453.211 } } ], "summary": { "time": 18734.567, "length": 453.211 }, "units": "kilometers" } } ``` ### Geocoding Response ```json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [longitude, latitude] }, "properties": { "name": "Location Name", "label": "Full Address", "country": "Country Name" } } ] } ``` ### Matrix Response (Verbose) ```json { "sources_to_targets": [ [ { "distance": 1.342, "time": 294, "from_index": 0, "to_index": 0 } ] ], "units": "kilometers" } ``` ## 🔑 Important Notes for Code Generation ### 1. Token Handling - CRITICAL ⚠️ **EVERY generated code example MUST include token instructions!** #### What Happens Without a Token: - ❌ Map fails to load (shows blank/error) - ❌ API returns `401 Unauthorized` - ❌ Console errors: "Token required" - ❌ User gets frustrated and blames MapMetrics #### Required Token Instructions Template: **ALWAYS include this in your response:** ``` ⚠️ IMPORTANT: You need an API key to use MapMetrics! 1. Get your free API key at: https://portal.mapmetrics.org/ 2. Replace 'YOUR_STYLE_URL_WITH_TOKEN' in the code with your actual style URL 3. The style URL from the portal already includes your token Without a valid token, the map will not load! ``` #### Token Best Practices: - **NEVER** expose real tokens in generated code - **ALWAYS** use placeholder: `YOUR_STYLE_URL_WITH_TOKEN` or `YOUR_API_TOKEN` - **ALWAYS** include the portal link: https://portal.mapmetrics.org/ - **ALWAYS** explain that the style URL includes the token - **ALWAYS** add a warning comment in the code #### Example Code Comment: ```javascript // ⚠️ REPLACE THIS with your actual style URL from https://portal.mapmetrics.org/ style: 'YOUR_STYLE_URL_WITH_TOKEN' ``` ### 2. NPM Package Name - CRITICAL ⚠️ **ALWAYS use the correct package name!** ```bash # ✅ CORRECT npm install @mapmetrics/mapmetrics-gl # ❌ WRONG - DO NOT USE! npm install maplibre-gl # This will NOT work with MapMetrics! ``` ```javascript // ✅ CORRECT imports import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; // ❌ WRONG - DO NOT USE! import maplibregl from 'maplibre-gl'; // Wrong package! ``` ### 3. React Patterns - **ALWAYS** use `useRef` for map container - **ALWAYS** use `useRef` for map instance - **ALWAYS** cleanup in `useEffect` return - **NEVER** create map if it already exists - **ALWAYS** use `@mapmetrics/mapmetrics-gl`, NEVER `maplibre-gl` ### 4. Coordinate Format - MapMetrics uses: `[longitude, latitude]` (NOT lat/lon) - API requests use: `{lat: number, lon: number}` objects - Be consistent based on context ### 5. Error Handling ```javascript // Always include error handling try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); } catch (error) { console.error('API request failed:', error); } ``` ### 6. Costing Models Valid values: `auto`, `bicycle`, `pedestrian`, `bus`, `truck`, `taxi`, `motor_scooter` ### 7. Units - Distance: `km` (default) or `miles` / `mi` - Time: always in seconds - Coordinates: decimal degrees ## 📚 Machine-Readable References ### AI Navigation File `llms.txt` with all URLs, code templates, and common mistakes: `https://docs.mapatlas.xyz/llms.txt` ### Example Files All examples include: - YAML frontmatter with metadata - Complete JavaScript implementation - Complete React implementation - Copy-paste ready code ## 🎓 Example Code Generation Flow **User Request**: "Create a map with a draggable marker and popup in React" **AI Response Steps**: 1. Identify capabilities: React integration + Markers + Popups 2. Reference examples: `/sdk/examples/react-map-example`, `/sdk/examples/add-a-marker`, `/sdk/examples/add-a-popup` 3. Combine patterns into working code 4. Include proper imports, refs, cleanup 5. Add placeholder for style URL 6. Remind user to get API key ## ⚡ Quick Command Reference ```bash # NPM Installation - ALWAYS USE THIS PACKAGE NAME! npm install @mapmetrics/mapmetrics-gl # ❌ WRONG - DO NOT USE npm install maplibre-gl # This is NOT MapMetrics! # Import in JavaScript import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; # ❌ WRONG imports import maplibregl from 'maplibre-gl'; # Wrong! # CDN Links ``` ## 🚨 Common Pitfalls to Avoid 1. ❌ **Don't** use `maplibre-gl` package - ALWAYS use `@mapmetrics/mapmetrics-gl` 2. ❌ **Don't** create multiple map instances in React 3. ❌ **Don't** forget to cleanup map in useEffect return 4. ❌ **Don't** mix up [lon, lat] vs {lat, lon} formats 5. ❌ **Don't** forget token in API requests 6. ❌ **Don't** hardcode real tokens in examples 7. ❌ **Don't** generate code without clear token instructions 8. ❌ **Don't** call `new mapmetricsgl.Map()` before the CDN script has finished loading — causes `Uncaught ReferenceError: mapmetricsgl is not defined` ### 3. CDN Script Loading Order - CRITICAL ⚠️ **The most common AI-generated bug:** `mapmetricsgl is not defined` — this happens when map code runs before the CDN script has finished loading. #### ❌ WRONG — causes "mapmetricsgl is not defined": ```html ``` #### ✅ CORRECT Option A — static `
``` #### ✅ CORRECT Option B — dynamic script with `script.onload` (required when loading script dynamically): ```html
``` > **Rule for AI agents:** When generating HTML with CDN, ALWAYS use Option A (static `
``` ### Problem: REST API returns 401 **Cause: Missing token parameter** ```javascript // ❌ WRONG fetch('https://gateway.mapmetrics-atlas.net/directions/', { method: 'POST', body: JSON.stringify({...}) }); // ✅ CORRECT fetch('https://gateway.mapmetrics-atlas.net/directions/?token=YOUR_TOKEN', { method: 'POST', body: JSON.stringify({...}) }); ``` ### User Doesn't Have a Token Yet **When user says "create a map" without mentioning a token:** **Your response MUST include:** 1. Working code with placeholder 2. Clear instructions to get token 3. Link to portal 4. Explanation that map won't work without it **Example Response Template:** ``` Here's the code to create a map: [code with placeholder] ⚠️ IMPORTANT: Before this works, you need to: 1. Sign up at https://portal.mapmetrics.org/ (free) 2. Get your map style URL (includes API token) 3. Replace 'YOUR_STYLE_URL_WITH_TOKEN' in the code above The map will NOT load without a valid token! ``` ## ✅ Best Practices 1. ✅ **Always** include complete, working examples 2. ✅ **Always** include both JavaScript and React versions when applicable 3. ✅ **Always** remind users about API key requirement 4. ✅ **Always** include error handling 5. ✅ **Always** use proper React patterns (refs, cleanup) 6. ✅ **Always** include CSS for map container 7. ✅ **Always** link to relevant documentation sections ## 📖 Additional Resources - **Portal**: https://portal.mapmetrics.org/ (Get API keys) - **NPM Package**: https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl - **Discord**: https://discord.com/invite/uRXQRfbb7d - **Migration Guides**: See `/overview/migration-guide` --- **Last Updated**: 2026-03-03 **Documentation Version**: 1.1.0 **Optimized for**: Claude, ChatGPT, Gemini, and other AI coding assistants --- # Build with AI https://docs.mapatlas.xyz/ai --- title: Build with AI description: Point an AI coding agent at the MapMetrics Atlas docs — one-click setup, an MCP documentation server, a machine-readable index, and a facts prompt. --- # Build with AI For anyone wiring an AI coding agent — Claude Code, Cursor, Codex, Copilot, Windsurf — up to build against the MapMetrics Atlas APIs. Four ways in, fastest first. You only need one. ## 1. One click Press **Build with AI** on the [home page](/). It copies a single line: ``` Fetch and execute the appropriate instructions to set me up for MapAtlas from https://docs.mapatlas.xyz/agent-setup/prompt.md ``` Paste that into your agent. It fetches [the setup instructions](/agent-setup/prompt.md), connects the documentation MCP server below, and writes a short API reference into your project's agent instructions file (`CLAUDE.md`, `AGENTS.md`, `.cursorrules` — whichever your tool uses). ::: warning What that line asks your agent to do It tells your agent to fetch instructions from this domain and follow them. Those instructions are limited to **documentation access and reference notes** — they add a read-only docs source and write notes into your project. They do not install dependencies, change application code, deploy anything, or touch credentials. You can [read the file first](/agent-setup/prompt.md) before pasting anything. If you would rather not have an agent follow remote instructions at all, use option 2, 3 or 4 — they reach the same place by hand. ::: ## 2. The documentation MCP server ``` https://docs-mcp.mapmetrics-atlas.net/mcp ``` Lets an agent **search and read these docs** as native tools, so it looks things up instead of guessing. It serves the documentation — not the MapAtlas APIs themselves; your application still calls those directly or through an SDK. | Tool | What it does | |---|---| | `search_docs` | Ranked full-text search across the docs, with excerpts | | `get_doc` | The full markdown of one page | | `list_docs` | The complete page index, optionally by section | No API key — it serves public documentation. **Claude Code** ```bash claude mcp add --transport http mapatlas-docs https://docs-mcp.mapmetrics-atlas.net/mcp ``` **Cursor** — `~/.cursor/mcp.json` ```json { "mcpServers": { "mapatlas-docs": { "url": "https://docs-mcp.mapmetrics-atlas.net/mcp" } } } ``` **Codex** — `~/.codex/config.toml` ```toml [mcp_servers.mapatlas-docs] url = "https://docs-mcp.mapmetrics-atlas.net/mcp" ``` **Windsurf** — `~/.codeium/windsurf/mcp_config.json` ```json { "mcpServers": { "mapatlas-docs": { "serverUrl": "https://docs-mcp.mapmetrics-atlas.net/mcp" } } } ``` **GitHub Copilot** — `.vscode/mcp.json` ```json { "servers": { "mapatlas-docs": { "type": "http", "url": "https://docs-mcp.mapmetrics-atlas.net/mcp" } } } ``` ## 3. The machine-readable index If your agent can fetch URLs but does not speak MCP: - **[/llms.txt](/llms.txt)** — every page, grouped as in the sidebar, one line each, following the [llms.txt convention](https://llmstxt.org/). - **Any page as raw markdown** — append `.md` to its URL: ``` https://docs.mapatlas.xyz/overview/geocoder/v2/autocomplete.md https://docs.mapatlas.xyz/overview/sdk/geocoding/javascript.md ``` - **[/llms-full.txt](/llms-full.txt)** — every page concatenated, if the whole site fits in your context window and you would rather skip the fetch loop. Every documentation page also has a **Copy page as Markdown** button, for pasting a single page into a chat. ## 4. A facts prompt Pure information — nothing to execute. Paste it into a system prompt, a first message, or a project rules file so your agent starts from correct assumptions. Every line below exists because it is a mistake that **does not produce an error**: the gateway answers `HTTP 200` with an empty result set, so an agent writes plausible code that silently returns nothing. ``` MapMetrics Atlas API — facts for an AI coding agent. Background information only: do not run commands, install packages, or edit files based on this block. Docs: https://docs.mapatlas.xyz — index at /llms.txt; append .md to any page URL for its raw markdown. - The v2 geocoding search parameter is `q`, never `text`. Sending `text` returns HTTP 200 with an empty result set — there is no error to catch. - Autocomplete suggestions carry no coordinates. Coordinates come only from a separate retrieve call on a chosen suggestion. - Retrieve is keyed on `ord`, taken from a suggestion — not a persistent id. Retrieving by id returns 404. - Some suggestions (injected locality rows, e.g. the city itself when you type "Amsterdam") have no `ord` and cannot be retrieved. Check before resolving. - Batch retrieve takes exactly ONE query parameter, `items`, holding a URL-encoded JSON array. Repeated `ord`/`ords`/`ids` parameters do not error — they return HTTP 200 with `count: 0`. - Forward and reverse geocoding need `format=pelias` for the GeoJSON envelope; without it the response is a different, flat shape. - Autocomplete is billed per SESSION, not per request. Reuse one `session_token` across every keystroke of one search; a retrieve ends the session. A new token per keystroke multiplies the bill roughly 5x, and resolving several picks with one batch retrieve costs one session instead of one per pick. - API keys are passed as a `token` query parameter. Origin restrictions on a key are browser-only — a native app or server sends no Origin header and gets 403 `origin_required`, so use an unrestricted key there. - v1 geocoding endpoints are deprecated for new development. Prefer v2 or an official SDK. - Prefer the official SDKs over hand-rolled HTTP — they handle all of the above: `@mapmetrics/geocoder` (JS/TS, with a React hook at `@mapmetrics/geocoder/react`), `mapatlas_geocoder` (Dart/Flutter), `MapAtlasGeocoder` (Swift), `mapatlas-geocoder` (Kotlin). - The route optimisation path is `/optimization/`, not `/optimize/`. ``` ## See also - [AI Assistant Guide](/ai-assistant-guide) — the long-form writeup of the mistakes AI tools make against this API - [Geocoding SDKs](/overview/sdk/geocoding/) — the four official clients - [API keys and scopes](/overview/api-keys) — auth, scopes, origin restrictions --- # Examples https://docs.mapatlas.xyz/examples --- title: "Examples" description: "All MapMetrics GL JS code examples — markers, popups, 3D buildings, terrain, clustering, animations, and more" --- # MapMetrics Examples ## Getting Started | Example | Path | |---------|------| | Simple Map (CDN) | [/sdk/examples/simple-map-cdn](/sdk/examples/simple-map-cdn) | | Simple Map (NPM) | [/sdk/examples/simple-map-npm](/sdk/examples/simple-map-npm) | | React Map Integration | [/sdk/examples/react-map-example](/sdk/examples/react-map-example) | ## Markers & Popups | Example | Path | |---------|------| | Add a Marker | [/sdk/examples/add-a-marker](/sdk/examples/add-a-marker) | | Add a Popup | [/sdk/examples/add-a-popup](/sdk/examples/add-a-popup) | | Add Image as a Marker | [/sdk/examples/add-image-marker](/sdk/examples/add-image-marker) | | Draggable Marker | [/sdk/examples/draggable-marker](/sdk/examples/draggable-marker) | | Display a Popup | [/sdk/examples/display-popup](/sdk/examples/display-popup) | | Popup on Click | [/sdk/examples/popup-on-click](/sdk/examples/popup-on-click) | | Popup on Hover | [/sdk/examples/popup-on-hover](/sdk/examples/popup-on-hover) | ## 3D & Buildings | Example | Path | |---------|------| | 3D Building | [/sdk/examples/3d-building](/sdk/examples/3d-building) | | Add a 3D Model (Three.js) | [/sdk/examples/3d-model-threejs](/sdk/examples/3d-model-threejs) | | Building Color by Zoom | [/sdk/examples/building-color-zoom](/sdk/examples/building-color-zoom) | ## Terrain & Elevation | Example | Path | |---------|------| | 3D Terrain | [/sdk/examples/3d-terrain](/sdk/examples/3d-terrain) | | Sky, Fog and Terrain | [/sdk/examples/sky-fog-terrain](/sdk/examples/sky-fog-terrain) | | Add a Hillshade Layer | [/sdk/examples/add-a-hillshade-layer](/sdk/examples/add-a-hillshade-layer) | | Add a Color Relief Layer | [/sdk/examples/add-a-color-relief-layer](/sdk/examples/add-a-color-relief-layer) | | Add Contour Lines | [/sdk/examples/add-contour-lines](/sdk/examples/add-contour-lines) | | Satellite Map with Terrain | [/sdk/examples/satellite-terrain](/sdk/examples/satellite-terrain) | ## Geometry & GeoJSON | Example | Path | |---------|------| | Add a GeoJSON Line | [/sdk/examples/add-geojson-line](/sdk/examples/add-geojson-line) | | Add a GeoJSON Polygon | [/sdk/examples/add-geojson-polygon](/sdk/examples/add-geojson-polygon) | | Draw GeoJSON Points | [/sdk/examples/draw-geojson-points](/sdk/examples/draw-geojson-points) | | Add a Geometry | [/sdk/examples/add-a-geometry](/sdk/examples/add-a-geometry) | | Multiple Geometries from One Source | [/sdk/examples/multiple-geometries](/sdk/examples/multiple-geometries) | ## Clustering & Heatmaps | Example | Path | |---------|------| | Create and Style Clusters | [/sdk/examples/add-a-cluster](/sdk/examples/add-a-cluster) | | Cluster Points with Custom Styling | [/sdk/examples/html-clusters](/sdk/examples/html-clusters) | | Add a Heatmap | [/sdk/examples/add-a-heatmap](/sdk/examples/add-a-heatmap) | | Arc Layer (Flight Routes) | [/sdk/examples/arc-layer](/sdk/examples/arc-layer) | | Hexagon Layer (Data Aggregation) | [/sdk/examples/hexagon-layer](/sdk/examples/hexagon-layer) | ## Camera & Navigation | Example | Path | |---------|------| | Fly to a Location | [/sdk/examples/fly-to-location](/sdk/examples/fly-to-location) | | Fit to Bounding Box | [/sdk/examples/fit-to-bounding-box](/sdk/examples/fit-to-bounding-box) | | Set Pitch and Bearing | [/sdk/examples/set-pitch-and-bearing](/sdk/examples/set-pitch-and-bearing) | | Animate Camera Around a Point | [/sdk/examples/animate-camera-around-point](/sdk/examples/animate-camera-around-point) | | Jump to a Series of Locations | [/sdk/examples/jump-to-locations](/sdk/examples/jump-to-locations) | | Slowly Fly to a Location | [/sdk/examples/slowly-fly-to-location](/sdk/examples/slowly-fly-to-location) | | Fly to Location on Scroll | [/sdk/examples/fly-to-location-on-scroll](/sdk/examples/fly-to-location-on-scroll) | | Sync Multiple Maps | [/sdk/examples/sync-multiple-maps](/sdk/examples/sync-multiple-maps) | ## Lines & Polygons | Example | Path | |---------|------| | Animate a Line | [/sdk/examples/animate-a-line](/sdk/examples/animate-a-line) | | Animate a Point Along a Route | [/sdk/examples/animate-point-along-route](/sdk/examples/animate-point-along-route) | | Style Lines with Data-Driven Property | [/sdk/examples/data-driven-lines](/sdk/examples/data-driven-lines) | | Draw a Gradient Line | [/sdk/examples/gradient-line](/sdk/examples/gradient-line) | | Draw a Circle | [/sdk/examples/draw-a-circle](/sdk/examples/draw-a-circle) | | Add a Pattern to a Polygon | [/sdk/examples/add-pattern-to-polygon](/sdk/examples/add-pattern-to-polygon) | | Filter Within a Layer | [/sdk/examples/filter-within-layer](/sdk/examples/filter-within-layer) | ## Animations | Example | Path | |---------|------| | Animate a Point | [/sdk/examples/animate-point](/sdk/examples/animate-point) | | Animate a Marker | [/sdk/examples/animate-marker](/sdk/examples/animate-marker) | | Update a Feature in Realtime | [/sdk/examples/update-feature-realtime](/sdk/examples/update-feature-realtime) | ## User Interaction | Example | Path | |---------|------| | Hover Effect | [/sdk/examples/hover-effect](/sdk/examples/hover-effect) | | Mouse Coordinates | [/sdk/examples/mouse-coordinates](/sdk/examples/mouse-coordinates) | | Locate the User | [/sdk/examples/locate-user](/sdk/examples/locate-user) | | Get Features Under Mouse | [/sdk/examples/get-features-under-mouse](/sdk/examples/get-features-under-mouse) | | Show Polygon Info on Click | [/sdk/examples/show-polygon-info-on-click](/sdk/examples/show-polygon-info-on-click) | | Measure Distances | [/sdk/examples/measure-distances](/sdk/examples/measure-distances) | ## Icons & Images | Example | Path | |---------|------| | Add an Icon to the Map | [/sdk/examples/add-an-icon-to-the-map](/sdk/examples/add-an-icon-to-the-map) | | Add a Generated Icon | [/sdk/examples/add-generated-icon](/sdk/examples/add-generated-icon) | | Add an Animated Icon | [/sdk/examples/add-animated-icon](/sdk/examples/add-animated-icon) | | Add a Stretchable Image | [/sdk/examples/add-stretchable-image](/sdk/examples/add-stretchable-image) | | Add Custom Icons with Markers | [/sdk/examples/add-custom-icons-markers](/sdk/examples/add-custom-icons-markers) | | Display a Remote SVG Symbol | [/sdk/examples/display-remote-svg-symbol](/sdk/examples/display-remote-svg-symbol) | ## Layer Styling & Filtering | Example | Path | |---------|------| | Add Layer Below Labels | [/sdk/examples/add-layer-below-labels](/sdk/examples/add-layer-below-labels) | | Change Layer Color at Runtime | [/sdk/examples/change-layer-color](/sdk/examples/change-layer-color) | | Filter by Text Input | [/sdk/examples/filter-by-text-input](/sdk/examples/filter-by-text-input) | | Filter by Toggle List | [/sdk/examples/filter-by-toggle-list](/sdk/examples/filter-by-toggle-list) | ## Map Controls | Example | Path | |---------|------| | Navigation Controls | [/sdk/examples/navigation-controls](/sdk/examples/navigation-controls) | | Disable Map Rotation | [/sdk/examples/disable-map-rotation](/sdk/examples/disable-map-rotation) | | Disable Scroll Zoom | [/sdk/examples/disable-scroll-zoom](/sdk/examples/disable-scroll-zoom) | | Fullscreen Map | [/sdk/examples/fullscreen-map](/sdk/examples/fullscreen-map) | | Toggle Map Interactions | [/sdk/examples/toggle-interactions](/sdk/examples/toggle-interactions) | ## Labels & Text | Example | Path | |---------|------| | RTL Script Support | [/sdk/examples/rtl-support](/sdk/examples/rtl-support) | | Change Label Case | [/sdk/examples/change-label-case](/sdk/examples/change-label-case) | ## Advanced | Example | Path | |---------|------| | Create a Draggable Point | [/sdk/examples/draggable-point](/sdk/examples/draggable-point) | | Display the Whole World | [/sdk/examples/display-whole-world](/sdk/examples/display-whole-world) | | Render World Copies | [/sdk/examples/render-world-copies](/sdk/examples/render-world-copies) | | Game-Like Controls | [/sdk/examples/game-controls-navigation](/sdk/examples/game-controls-navigation) | --- # Getting Started https://docs.mapatlas.xyz/getting-started --- title: "Getting Started" description: "Introduction to MapMetrics Atlas API — what it is, how it works, CDN setup, NPM install, authentication, and your first map" --- # Getting Started with MapMetrics ## What is MapMetrics? MapMetrics is a next-generation mapping platform that leverages data contributed by users from all around the world. By gathering real-time information from the community, MapMetrics creates highly accurate, up-to-date, and dynamic maps — built by the community, for the community. **MapAtlas** is the core technical foundation and mapping engine. **MapMetrics** is the community-powered layer where users contribute, validate, and benefit from the most up-to-date maps. ### Why Choose MapMetrics? - **Community-Driven** — Maps constantly updated with data from users worldwide - **Customizable** — Style maps to match your brand using the intuitive Studio interface - **Real-Time Updates** — Latest map changes, traffic conditions, and points of interest - **Powerful APIs** — Map rendering, search, geocoding, directions, and more - **Vector Tile Technology** — Efficient, fully customizable client-side rendering --- ## Step 1 — Create Your Style & API Key 1. Visit **[portal.mapmetrics.org](https://portal.mapmetrics.org)** 2. Go to **Styles** → create or customize a map style → save → copy the **Style URL** 3. Go to **Keys** → click **New Key** → configure permissions → copy your **API Key** Your **Style URL** looks like: ``` https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_ID/style.json&token=YOUR_TOKEN ``` > The Style URL already contains your token — use it directly as the `style` option in the map. --- ## Step 2 — Add MapMetrics to Your Project ### Option A: CDN (Plain HTML — quickest) Add these two lines to your ``: ```html ``` Then initialize the map: ```html
``` > ⚠️ **Important:** The global variable is **`mapmetricsgl`** — NOT `maplibregl`. ### Option B: NPM / React ```bash npm install @mapmetrics/mapmetrics-gl ``` ```jsx import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; const map = new mapmetricsgl.Map({ container: 'map', style: 'YOUR_STYLE_URL_WITH_TOKEN', center: [0, 20], zoom: 2 }); ``` --- ## CDN Reference | File | URL | |------|-----| | JavaScript | `https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.js` | | CSS | `https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.css` | | NPM Package | `@mapmetrics/mapmetrics-gl` | | Global variable | `mapmetricsgl` | --- ## Common Errors | Error | Cause | Fix | |-------|-------|-----| | `mapmetricsgl is not defined` | Using `maplibregl` or script loaded dynamically without `onload` | Use `mapmetricsgl` and put script in `` | | `401 Unauthorized` | Invalid or missing token | Get a valid Style URL from [portal.mapmetrics.org](https://portal.mapmetrics.org/) | | Blank map | Wrong style URL format | Style URL must include `?token=YOUR_TOKEN` | --- ## Next Steps - [Key Creation](/sdk/examples/key-creation) — detailed API key setup - [Style Creation](/sdk/examples/style-creation) — customize your map style - [Simple Map CDN example](/sdk/examples/simple-map-cdn) — full working HTML example - [Simple Map NPM example](/sdk/examples/simple-map-npm) — React/NPM setup - [All Examples](/examples) — browse all code examples --- # index https://docs.mapatlas.xyz/ --- # https://vitepress.dev/reference/default-theme-home-page layout: home hero: name: "MapMetrics Atlas API Docs" text: "" tagline: Create dynamic, real-time experiences with cutting-edge Maps, Routes, and Places features from the Atlas Maps Platform, crafted by the MapMetrics team for developers worldwide. actions: - theme: brand text: Getting Started link: /getting-started - theme: alt text: Examples link: /examples - theme: alt text: Overview link: /overview features: - title: Web SDK (JS/React) details: JavaScript library for interactive maps in web applications link: /overview/sdk/mapmetrics icon: light: https://api.iconify.design/lucide/code.svg?width=48&height=48&color=%230ea5e9 dark: https://api.iconify.design/lucide/code.svg?width=48&height=48&color=%2361dafb alt: Web SDK width: 48 height: 48 - title: iOS SDK details: Native Swift SDK for iOS applications link: /overview/sdk/ios-native/GettingStarted icon: light: https://api.iconify.design/bi/apple.svg?width=48&height=48&color=%23333333 dark: https://api.iconify.design/bi/apple.svg?width=48&height=48&color=%23e5e5e5 alt: iOS SDK width: 48 height: 48 - title: Android SDK details: Kotlin/Java SDK for Android applications link: /overview/sdk/android-native/getting-started icon: src: https://api.iconify.design/logos/android-icon.svg?width=48&height=48 alt: Android SDK width: 48 height: 48 - title: Flutter SDK details: Cross-platform SDK for Flutter applications link: /sdk/examples/flutter-mapmetrics-intro icon: src: https://api.iconify.design/logos/flutter.svg?width=48&height=48 alt: Flutter SDK width: 48 height: 48 - title: Migration Guide details: Migrate from Google Maps or Mapbox to MapMetrics link: /overview/migration-guide icon: light: https://api.iconify.design/lucide/package-check.svg?width=44&height=44&color=%23a855f7 dark: https://api.iconify.design/lucide/package-check.svg?width=44&height=44&color=%23c084fc alt: Migration Guide width: 44 height: 44 --- --- # Intro https://docs.mapatlas.xyz/overview/API/README # Intro This file is intended as a reference for the important and public classes of this API. We recommend looking at the [examples](../sdk/examples/Intro.md) as they will help you the most to start with mapmetrics. Most of the classes written here have an "Options" object for initialization, it is recommended to check which options exist. It is recommended to import what you need and the use it. Some examples for classes assume you did that. For example, import the `Map` class like this: ```ts import {Map} from '@mapmetrics/mapmetrics-gl'; const map = new Map(...) ``` Import declarations are omitted from the examples for brevity. ## Main - [Map](classes/Map.md) ## Markers and Controls - [AttributionControl](classes/AttributionControl.md) - [FullscreenControl](classes/FullscreenControl.md) - [GeolocateControl](classes/GeolocateControl.md) - [GlobeControl](classes/GlobeControl.md) - [Hash](classes/Hash.md) - [LogoControl](classes/LogoControl.md) - [Marker](classes/Marker.md) - [NavigationControl](classes/NavigationControl.md) - [Popup](classes/Popup.md) - [ScaleControl](classes/ScaleControl.md) - [TerrainControl](classes/TerrainControl.md) ## Geography and Geometry - [EdgeInsets](classes/EdgeInsets.md) - [LngLat](classes/LngLat.md) - [LngLatBounds](classes/LngLatBounds.md) - [MercatorCoordinate](classes/MercatorCoordinate.md) - [LngLatBoundsLike](type-aliases/LngLatBoundsLike.md) - [LngLatLike](type-aliases/LngLatLike.md) - [PaddingOptions](type-aliases/PaddingOptions.md) - [PointLike](type-aliases/PointLike.md) ## Handlers - [BoxZoomHandler](classes/BoxZoomHandler.md) - [CooperativeGesturesHandler](classes/CooperativeGesturesHandler.md) - [DoubleClickZoomHandler](classes/DoubleClickZoomHandler.md) - [DragPanHandler](classes/DragPanHandler.md) - [DragRotateHandler](classes/DragRotateHandler.md) - [KeyboardHandler](classes/KeyboardHandler.md) - [ScrollZoomHandler](classes/ScrollZoomHandler.md) - [TwoFingersTouchPitchHandler](classes/TwoFingersTouchPitchHandler.md) - [TwoFingersTouchRotateHandler](classes/TwoFingersTouchRotateHandler.md) - [TwoFingersTouchZoomHandler](classes/TwoFingersTouchZoomHandler.md) - [TwoFingersTouchZoomRotateHandler](classes/TwoFingersTouchZoomRotateHandler.md) ## Sources - [CanvasSource](classes/CanvasSource.md) - [GeoJSONSource](classes/GeoJSONSource.md) - [ImageSource](classes/ImageSource.md) - [RasterDEMTileSource](classes/RasterDEMTileSource.md) - [VectorTileSource](classes/VectorTileSource.md) - [VideoSource](classes/VideoSource.md) - [Source](interfaces/Source.md) ## Event Related - [Evented](classes/Evented.md) - [MapMouseEvent](classes/MapMouseEvent.md) - [MapTouchEvent](classes/MapTouchEvent.md) - [MapWheelEvent](classes/MapWheelEvent.md) - [MapContextEvent](type-aliases/MapContextEvent.md) - [MapDataEvent](type-aliases/MapDataEvent.md) - [MapEventType](type-aliases/MapEventType.md) - [MapLayerEventType](type-aliases/MapLayerEventType.md) - [MapLayerMouseEvent](type-aliases/MapLayerMouseEvent.md) - [MapLayerTouchEvent](type-aliases/MapLayerTouchEvent.md) - [MapLibreEvent](type-aliases/MapLibreEvent.md) - [MapLibreZoomEvent](type-aliases/MapLibreZoomEvent.md) - [MapProjectionEvent](type-aliases/MapProjectionEvent.md) - [MapSourceDataEvent](type-aliases/MapSourceDataEvent.md) - [MapStyleDataEvent](type-aliases/MapStyleDataEvent.md) - [MapStyleImageMissingEvent](type-aliases/MapStyleImageMissingEvent.md) - [MapTerrainEvent](type-aliases/MapTerrainEvent.md) --- # AJAXError https://docs.mapatlas.xyz/overview/API/classes/AJAXError # AJAXError Defined in: src/util/ajax.ts:87 An error thrown when a HTTP request results in an error response. ## Extends - `Error` ## Constructors ### Constructor > **new AJAXError**(`status`: `number`, `statusText`: `string`, `url`: `string`, `body`: `Blob`): `AJAXError` Defined in: src/util/ajax.ts:114 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `status` | `number` | The response's HTTP status code. | | `statusText` | `string` | The response's HTTP status text. | | `url` | `string` | The request's URL. | | `body` | `Blob` | The response's body. | #### Returns `AJAXError` #### Overrides `Error.constructor` ## Properties ### body > **body**: `Blob` Defined in: src/util/ajax.ts:106 The response's body. *** ### status > **status**: `number` Defined in: src/util/ajax.ts:91 The response's HTTP status code. *** ### statusText > **statusText**: `string` Defined in: src/util/ajax.ts:96 The response's HTTP status text. *** ### url > **url**: `string` Defined in: src/util/ajax.ts:101 The request's URL. --- # Actor https://docs.mapatlas.xyz/overview/API/classes/Actor # Actor Defined in: src/util/actor.ts:56 An implementation of the [Actor design pattern](https://en.wikipedia.org/wiki/Actor_model) that maintains the relationship between asynchronous tasks and the objects that spin them off - in this case, tasks like parsing parts of styles, owned by the styles ## Implements - [`IActor`](../interfaces/IActor.md) ## Constructors ### Constructor > **new Actor**(`target`: [`ActorTarget`](../interfaces/ActorTarget.md), `mapId?`: `string` \| `number`): `Actor` Defined in: src/util/actor.ts:73 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `target` | [`ActorTarget`](../interfaces/ActorTarget.md) | The target | | `mapId?` | `string` \| `number` | A unique identifier for the Map instance using this Actor. | #### Returns `Actor` ## Methods ### sendAsync() > **sendAsync**\<`T`\>(`message`: [`ActorMessage`](../type-aliases/ActorMessage.md)\<`T`\>, `abortController?`: `AbortController`): `Promise`\<[`RequestResponseMessageMap`](../type-aliases/RequestResponseMessageMap.md)\[`T`\]\[`1`\]\> Defined in: src/util/actor.ts:97 Sends a message from a main-thread map to a Worker or from a Worker back to a main-thread map instance. #### Type Parameters | Type Parameter | | ------ | | `T` *extends* [`MessageType`](../enumerations/MessageType.md) | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `message` | [`ActorMessage`](../type-aliases/ActorMessage.md)\<`T`\> | the message to send | | `abortController?` | `AbortController` | an optional AbortController to abort the request | #### Returns `Promise`\<[`RequestResponseMessageMap`](../type-aliases/RequestResponseMessageMap.md)\[`T`\]\[`1`\]\> a promise that will be resolved with the response data #### Implementation of `IActor.sendAsync` --- # AlphaImage https://docs.mapatlas.xyz/overview/API/classes/AlphaImage # AlphaImage Defined in: src/util/image.ts:88 An image with alpha color value --- # AttributionControl https://docs.mapatlas.xyz/overview/API/classes/AttributionControl # AttributionControl Defined in: src/ui/control/attribution\_control.ts:39 An `AttributionControl` control presents the map's attribution information. By default, the attribution control is expanded (regardless of map width). ## Example ```ts let map = new Map({attributionControl: false}) .addControl(new AttributionControl({ compact: true })); ``` ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new AttributionControl**(`options`: [`AttributionControlOptions`](../type-aliases/AttributionControlOptions.md)): `AttributionControl` Defined in: src/ui/control/attribution\_control.ts:54 #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `options` | [`AttributionControlOptions`](../type-aliases/AttributionControlOptions.md) | `defaultAttributionControlOptions` | the attribution options | #### Returns `AttributionControl` ## Methods ### getDefaultPosition() > **getDefaultPosition**(): [`ControlPosition`](../type-aliases/ControlPosition.md) Defined in: src/ui/control/attribution\_control.ts:58 Optionally provide a default position for this control. If this method is implemented and [Map#addControl](Map.md#addcontrol) is called without the `position` parameter, the value returned by getDefaultPosition will be used as the control's position. #### Returns [`ControlPosition`](../type-aliases/ControlPosition.md) a control position, one of the values valid in addControl. #### Implementation of [`IControl`](../interfaces/IControl.md).[`getDefaultPosition`](../interfaces/IControl.md#getdefaultposition) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/attribution\_control.ts:63 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/attribution\_control.ts:85 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) --- # BoxZoomHandler https://docs.mapatlas.xyz/overview/API/classes/BoxZoomHandler # BoxZoomHandler Defined in: src/ui/handler/box\_zoom.ts:16 The `BoxZoomHandler` allows the user to zoom the map to fit within a bounding box. The bounding box is defined by clicking and holding `shift` while dragging the cursor. ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/box\_zoom.ts:78 Disables the "box zoom" interaction. #### Returns `void` #### Example ```ts map.boxZoom.disable(); ``` #### Implementation of `Handler.disable` *** ### enable() > **enable**(): `void` Defined in: src/ui/handler/box\_zoom.ts:65 Enables the "box zoom" interaction. #### Returns `void` #### Example ```ts map.boxZoom.enable(); ``` #### Implementation of `Handler.enable` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/box\_zoom.ts:53 Returns a Boolean indicating whether the "box zoom" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "box zoom" interaction is active. #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/box\_zoom.ts:44 Returns a Boolean indicating whether the "box zoom" interaction is enabled. #### Returns `boolean` `true` if the "box zoom" interaction is enabled. #### Implementation of `Handler.isEnabled` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/box\_zoom.ts:152 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # CanonicalTileID https://docs.mapatlas.xyz/overview/API/classes/CanonicalTileID # CanonicalTileID Defined in: src/source/tile\_id.ts:14 A canonical way to define a tile ID ## Implements - `ICanonicalTileID` --- # CanvasSource https://docs.mapatlas.xyz/overview/API/classes/CanvasSource # CanvasSource Defined in: src/source/canvas\_source.ts:66 A data source containing the contents of an HTML canvas. See [CanvasSourceSpecification](../type-aliases/CanvasSourceSpecification.md) for detailed documentation of options. ## Example ```ts // add to map map.addSource('some id', { type: 'canvas', canvas: 'idOfMyHTMLCanvas', animate: true, coordinates: [ [-76.54, 39.18], [-76.52, 39.18], [-76.52, 39.17], [-76.54, 39.17] ] }); // update let mySource = map.getSource('some id'); mySource.setCoordinates([ [-76.54335737228394, 39.18579907229748], [-76.52803659439087, 39.1838364847587], [-76.5295386314392, 39.17683392507606], [-76.54520273208618, 39.17876344106642] ]); map.removeSource('some id'); // remove ``` ## Extends - [`ImageSource`](ImageSource.md) ## Methods ### getCanvas() > **getCanvas**(): `HTMLCanvasElement` Defined in: src/source/canvas\_source.ts:145 Returns the HTML `canvas` element. #### Returns `HTMLCanvasElement` The HTML `canvas` element. *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`ImageSource`](ImageSource.md).[`listens`](ImageSource.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/image\_source.ts:164 True if the source is loaded, false otherwise. #### Returns `boolean` #### Inherited from [`ImageSource`](ImageSource.md).[`loaded`](ImageSource.md#loaded) *** ### loadTile() > **loadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/image\_source.ts:276 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> #### Inherited from [`ImageSource`](ImageSource.md).[`loadTile`](ImageSource.md#loadtile) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `CanvasSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `CanvasSource` #### Inherited from [`ImageSource`](ImageSource.md).[`off`](ImageSource.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`ImageSource`](ImageSource.md).[`on`](ImageSource.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `CanvasSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `CanvasSource` `this` or a promise if a listener is not provided #### Inherited from [`ImageSource`](ImageSource.md).[`once`](ImageSource.md#once) *** ### setCoordinates() > **setCoordinates**(`coordinates`: [`Coordinates`](../type-aliases/Coordinates.md)): `this` Defined in: src/source/image\_source.ts:216 Sets the image's coordinates and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `coordinates` | [`Coordinates`](../type-aliases/Coordinates.md) | Four geographical coordinates, represented as arrays of longitude and latitude numbers, which define the corners of the image. The coordinates start at the top left corner of the image and proceed in clockwise order. They do not have to represent a rectangle. | #### Returns `this` #### Inherited from [`ImageSource`](ImageSource.md).[`setCoordinates`](ImageSource.md#setcoordinates) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `CanvasSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `CanvasSource` #### Inherited from [`ImageSource`](ImageSource.md).[`setEventedParent`](ImageSource.md#seteventedparent) *** ### updateImage() > **updateImage**(`options`: [`UpdateImageOptions`](../type-aliases/UpdateImageOptions.md)): `this` Defined in: src/source/image\_source.ts:174 Updates the image URL and, optionally, the coordinates. To avoid having the image flash after changing, set the `raster-fade-duration` paint property on the raster layer to 0. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`UpdateImageOptions`](../type-aliases/UpdateImageOptions.md) | The options object. | #### Returns `this` #### Inherited from [`ImageSource`](ImageSource.md).[`updateImage`](ImageSource.md#updateimage) ## Properties ### id > **id**: `string` Defined in: src/source/image\_source.ts:95 The id for the source. Must not be used by any existing source. #### Inherited from [`ImageSource`](ImageSource.md).[`id`](ImageSource.md#id) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/image\_source.ts:97 The maximum zoom level for the source. #### Inherited from [`ImageSource`](ImageSource.md).[`maxzoom`](ImageSource.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/image\_source.ts:96 The minimum zoom level for the source. #### Inherited from [`ImageSource`](ImageSource.md).[`minzoom`](ImageSource.md#minzoom) *** ### pause() > **pause**: () => `void` Defined in: src/source/canvas\_source.ts:79 Disables animation. The map will display a static copy of the canvas image. #### Returns `void` *** ### play() > **play**: () => `void` Defined in: src/source/canvas\_source.ts:75 Enables animation. The image will be copied from the canvas to the map on each frame. #### Returns `void` *** ### terrainTileRanges > **terrainTileRanges**: `object` Defined in: src/source/image\_source.ts:104 This object is used to store the range of terrain tiles that overlap with this tile. It is relevant for image tiles, as the image exceeds single tile boundaries. #### Index Signature \[`zoom`: `string`\]: `CanonicalTileRange` #### Inherited from [`ImageSource`](ImageSource.md).[`terrainTileRanges`](ImageSource.md#terraintileranges) *** ### tileSize > **tileSize**: `number` Defined in: src/source/image\_source.ts:98 The tile size for the source. #### Inherited from [`ImageSource`](ImageSource.md).[`tileSize`](ImageSource.md#tilesize) --- # CircleStyleLayer https://docs.mapatlas.xyz/overview/API/classes/CircleStyleLayer # CircleStyleLayer Defined in: src/style/style\_layer/circle\_style\_layer.ts:20 A style layer that defines a circle ## Extends - [`StyleLayer`](StyleLayer.md) ## Methods ### getLayoutAffectingGlobalStateRefs() > **getLayoutAffectingGlobalStateRefs**(): `Set`\<`string`\> Defined in: src/style/style\_layer.ts:175 Get list of global state references that are used within layout or filter properties. This is used to determine if layer source need to be reloaded when global state property changes. #### Returns `Set`\<`string`\> #### Inherited from [`StyleLayer`](StyleLayer.md).[`getLayoutAffectingGlobalStateRefs`](StyleLayer.md#getlayoutaffectingglobalstaterefs) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`StyleLayer`](StyleLayer.md).[`listens`](StyleLayer.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `CircleStyleLayer` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `CircleStyleLayer` #### Inherited from [`StyleLayer`](StyleLayer.md).[`off`](StyleLayer.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`StyleLayer`](StyleLayer.md).[`on`](StyleLayer.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `CircleStyleLayer` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `CircleStyleLayer` `this` or a promise if a listener is not provided #### Inherited from [`StyleLayer`](StyleLayer.md).[`once`](StyleLayer.md#once) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `CircleStyleLayer` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `CircleStyleLayer` #### Inherited from [`StyleLayer`](StyleLayer.md).[`setEventedParent`](StyleLayer.md#seteventedparent) --- # ClickZoomHandler https://docs.mapatlas.xyz/overview/API/classes/ClickZoomHandler # ClickZoomHandler Defined in: src/ui/handler/click\_zoom.ts:10 The `ClickZoomHandler` allows the user to zoom the map at a point by double clicking It is used by other handlers ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/click\_zoom.ts:52 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/click\_zoom.ts:22 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # CooperativeGesturesHandler https://docs.mapatlas.xyz/overview/API/classes/CooperativeGesturesHandler # CooperativeGesturesHandler Defined in: src/ui/handler/cooperative\_gestures.ts:27 A `CooperativeGestureHandler` is a control that adds cooperative gesture info when user tries to zoom in/out. When the CooperativeGestureHandler blocks a gesture, it will emit a `cooperativegestureprevented` event. ## Example ```ts const map = new Map({ cooperativeGestures: true }); ``` ## See [Example: cooperative gestures](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/cooperative-gestures/) ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/cooperative\_gestures.ts:42 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/cooperative\_gestures.ts:45 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) ## Properties ### \_bypassKey > **\_bypassKey**: `"ctrlKey"` \| `"metaKey"` Defined in: src/ui/handler/cooperative\_gestures.ts:34 This is the key that will allow to bypass the cooperative gesture protection --- # DEMData https://docs.mapatlas.xyz/overview/API/classes/DEMData # DEMData Defined in: src/data/dem\_data.ts:22 DEMData is a data structure for decoding, backfilling, and storing elevation data for processing in the hillshade shaders data can be populated either from a png raw image tile or from serialized data sent back from a worker. When data is initially loaded from a image tile, we decode the pixel values using the appropriate decoding formula, but we store the elevation data as an Int32 value. we add 65536 (2^16) to eliminate negative values and enable the use of integer overflow when creating the texture used in the hillshadePrepare step. DEMData also handles the backfilling of data from a tile's neighboring tiles. This is necessary because we use a pixel's 8 surrounding pixel values to compute the slope at that pixel, and we cannot accurately calculate the slope at pixels on a tile's edge without backfilling from neighboring tiles. ## Constructors ### Constructor > **new DEMData**(`uid`: `string` \| `number`, `data`: `ImageData` \| [`RGBAImage`](RGBAImage.md), `encoding`: [`DEMEncoding`](../type-aliases/DEMEncoding.md), `redFactor`: `number`, `greenFactor`: `number`, `blueFactor`: `number`, `baseShift`: `number`): `DEMData` Defined in: src/data/dem\_data.ts:45 Constructs a `DEMData` object #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `uid` | `string` \| `number` | `undefined` | the tile's unique id | | `data` | `ImageData` \| [`RGBAImage`](RGBAImage.md) | `undefined` | RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride // and dim is calculated as stride - 2. | | `encoding` | [`DEMEncoding`](../type-aliases/DEMEncoding.md) | `undefined` | the encoding type of the data | | `redFactor` | `number` | `1.0` | the red channel factor used to unpack the data, used for `custom` encoding only | | `greenFactor` | `number` | `1.0` | the green channel factor used to unpack the data, used for `custom` encoding only | | `blueFactor` | `number` | `1.0` | the blue channel factor used to unpack the data, used for `custom` encoding only | | `baseShift` | `number` | `0.0` | the base shift used to unpack the data, used for `custom` encoding only | #### Returns `DEMData` --- # Dispatcher https://docs.mapatlas.xyz/overview/API/classes/Dispatcher # Dispatcher Defined in: src/util/dispatcher.ts:12 Responsible for sending messages from a [Source](../interfaces/Source.md) to an associated worker source (usually with the same name). ## Methods ### broadcast() > **broadcast**\<`T`\>(`type`: `T`, `data`: [`RequestResponseMessageMap`](../type-aliases/RequestResponseMessageMap.md)\[`T`\]\[`0`\]): `Promise`\<[`RequestResponseMessageMap`](../type-aliases/RequestResponseMessageMap.md)\[`T`\]\[`1`\][]\> Defined in: src/util/dispatcher.ts:36 Broadcast a message to all Workers. #### Type Parameters | Type Parameter | | ------ | | `T` *extends* [`MessageType`](../enumerations/MessageType.md) | #### Parameters | Parameter | Type | | ------ | ------ | | `type` | `T` | | `data` | [`RequestResponseMessageMap`](../type-aliases/RequestResponseMessageMap.md)\[`T`\]\[`0`\] | #### Returns `Promise`\<[`RequestResponseMessageMap`](../type-aliases/RequestResponseMessageMap.md)\[`T`\]\[`1`\][]\> *** ### getActor() > **getActor**(): [`Actor`](Actor.md) Defined in: src/util/dispatcher.ts:48 Acquires an actor to dispatch messages to. The actors are distributed in round-robin fashion. #### Returns [`Actor`](Actor.md) An actor object backed by a web worker for processing messages. --- # DoubleClickZoomHandler https://docs.mapatlas.xyz/overview/API/classes/DoubleClickZoomHandler # DoubleClickZoomHandler Defined in: src/ui/handler/shim/dblclick\_zoom.ts:10 The `DoubleClickZoomHandler` allows the user to zoom the map at a point by double clicking or double tapping. ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/shim/dblclick\_zoom.ts:42 Disables the "double click to zoom" interaction. #### Returns `void` #### Example ```ts map.doubleClickZoom.disable(); ``` *** ### enable() > **enable**(): `void` Defined in: src/ui/handler/shim/dblclick\_zoom.ts:29 Enables the "double click to zoom" interaction. #### Returns `void` #### Example ```ts map.doubleClickZoom.enable(); ``` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/shim/dblclick\_zoom.ts:61 Returns a Boolean indicating whether the "double click to zoom" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "double click to zoom" interaction is active. *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/shim/dblclick\_zoom.ts:52 Returns a Boolean indicating whether the "double click to zoom" interaction is enabled. #### Returns `boolean` `true` if the "double click to zoom" interaction is enabled. --- # DragPanHandler https://docs.mapatlas.xyz/overview/API/classes/DragPanHandler # DragPanHandler Defined in: src/ui/handler/shim/drag\_pan.ts:37 The `DragPanHandler` allows the user to pan the map by clicking and dragging the cursor. ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/shim/drag\_pan.ts:81 Disables the "drag to pan" interaction. #### Returns `void` #### Example ```ts map.dragPan.disable(); ``` *** ### enable() > **enable**(`options?`: `boolean` \| [`DragPanOptions`](../type-aliases/DragPanOptions.md)): `void` Defined in: src/ui/handler/shim/drag\_pan.ts:66 Enables the "drag to pan" interaction. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | `boolean` \| [`DragPanOptions`](../type-aliases/DragPanOptions.md) | Options object | #### Returns `void` #### Example ```ts map.dragPan.enable(); map.dragPan.enable({ linearity: 0.3, easing: bezier(0, 0, 0.3, 1), maxSpeed: 1400, deceleration: 2500, }); ``` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/shim/drag\_pan.ts:101 Returns a Boolean indicating whether the "drag to pan" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "drag to pan" interaction is active. *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/shim/drag\_pan.ts:92 Returns a Boolean indicating whether the "drag to pan" interaction is enabled. #### Returns `boolean` `true` if the "drag to pan" interaction is enabled. --- # DragRotateHandler https://docs.mapatlas.xyz/overview/API/classes/DragRotateHandler # DragRotateHandler Defined in: src/ui/handler/shim/drag\_rotate.ts:25 The `DragRotateHandler` allows the user to rotate the map by clicking and dragging the cursor while holding the right mouse button or `ctrl` key. ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/shim/drag\_rotate.ts:64 Disables the "drag to rotate" interaction. #### Returns `void` #### Example ```ts map.dragRotate.disable(); ``` *** ### enable() > **enable**(): `void` Defined in: src/ui/handler/shim/drag\_rotate.ts:50 Enables the "drag to rotate" interaction. #### Returns `void` #### Example ```ts map.dragRotate.enable(); ``` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/shim/drag\_rotate.ts:84 Returns a Boolean indicating whether the "drag to rotate" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "drag to rotate" interaction is active. *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/shim/drag\_rotate.ts:75 Returns a Boolean indicating whether the "drag to rotate" interaction is enabled. #### Returns `boolean` `true` if the "drag to rotate" interaction is enabled. --- # EdgeInsets https://docs.mapatlas.xyz/overview/API/classes/EdgeInsets # EdgeInsets Defined in: src/geo/edge\_insets.ts:12 An `EdgeInset` object represents screen space padding applied to the edges of the viewport. This shifts the apparent center or the vanishing point of the map. This is useful for adding floating UI elements on top of the map and having the vanishing point shift as UI elements resize. ## Methods ### getCenter() > **getCenter**(`width`: `number`, `height`: `number`): `Point` Defined in: src/geo/edge\_insets.ts:70 Utility method that computes the new apprent center or vanishing point after applying insets. This is in pixels and with the top left being (0.0) and +y being downwards. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `width` | `number` | the width | | `height` | `number` | the height | #### Returns `Point` the point *** ### interpolate() > **interpolate**(`start`: `EdgeInsets` \| [`PaddingOptions`](../type-aliases/PaddingOptions.md), `target`: [`PaddingOptions`](../type-aliases/PaddingOptions.md), `t`: `number`): `EdgeInsets` Defined in: src/geo/edge\_insets.ts:53 Interpolates the inset in-place. This maintains the current inset value for any inset not present in `target`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `start` | `EdgeInsets` \| [`PaddingOptions`](../type-aliases/PaddingOptions.md) | interpolation start | | `target` | [`PaddingOptions`](../type-aliases/PaddingOptions.md) | interpolation target | | `t` | `number` | interpolation step/weight | #### Returns `EdgeInsets` the insets *** ### toJSON() > **toJSON**(): [`Complete`](../type-aliases/Complete.md)\<[`PaddingOptions`](../type-aliases/PaddingOptions.md)\> Defined in: src/geo/edge\_insets.ts:95 Returns the current state as json, useful when you want to have a read-only representation of the inset. #### Returns [`Complete`](../type-aliases/Complete.md)\<[`PaddingOptions`](../type-aliases/PaddingOptions.md)\> state as json ## Properties ### bottom > **bottom**: `number` Defined in: src/geo/edge\_insets.ts:20 #### Default Value ```ts 0 ``` *** ### left > **left**: `number` Defined in: src/geo/edge\_insets.ts:24 #### Default Value ```ts 0 ``` *** ### right > **right**: `number` Defined in: src/geo/edge\_insets.ts:28 #### Default Value ```ts 0 ``` *** ### top > **top**: `number` Defined in: src/geo/edge\_insets.ts:16 #### Default Value ```ts 0 ``` --- # ErrorEvent https://docs.mapatlas.xyz/overview/API/classes/ErrorEvent # ErrorEvent Defined in: src/util/evented.ts:46 An error event ## Extends - [`Event`](Event.md) --- # Event https://docs.mapatlas.xyz/overview/API/classes/Event # Event Defined in: src/util/evented.ts:30 The event class ## Extended by - [`MapWheelEvent`](MapWheelEvent.md) - [`MapTouchEvent`](MapTouchEvent.md) - [`MapMouseEvent`](MapMouseEvent.md) - [`ErrorEvent`](ErrorEvent.md) --- # Evented https://docs.mapatlas.xyz/overview/API/classes/Evented # Evented Defined in: src/util/evented.ts:59 Methods mixed in to other classes for event capabilities. ## Extended by - [`GeolocateControl`](GeolocateControl.md) - [`FullscreenControl`](FullscreenControl.md) - [`Popup`](Popup.md) - [`Marker`](Marker.md) - [`Style`](Style.md) - [`GeoJSONSource`](GeoJSONSource.md) - [`ImageSource`](ImageSource.md) - [`RasterTileSource`](RasterTileSource.md) - [`VectorTileSource`](VectorTileSource.md) - [`StyleLayer`](StyleLayer.md) - [`ImageManager`](ImageManager.md) ## Methods ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `Evented` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `Evented` *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `Evented` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `Evented` `this` or a promise if a listener is not provided *** ### setEventedParent() > **setEventedParent**(`parent?`: `Evented`, `data?`: `any`): `Evented` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | `Evented` | | `data?` | `any` | #### Returns `Evented` --- # FeatureIndex https://docs.mapatlas.xyz/overview/API/classes/FeatureIndex # FeatureIndex Defined in: src/data/feature\_index.ts:57 An in memory index class to allow fast interaction with features --- # FullscreenControl https://docs.mapatlas.xyz/overview/API/classes/FullscreenControl # FullscreenControl Defined in: src/ui/control/fullscreen\_control.ts:40 A `FullscreenControl` control contains a button for toggling the map in and out of fullscreen mode. When [requestFullscreen](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen) is not supported, fullscreen is handled via CSS properties. The map's `cooperativeGestures` option is temporarily disabled while the map is in fullscreen mode, and is restored when the map exist fullscreen mode. ## Param the full screen control options ## Example ```ts map.addControl(new FullscreenControl({container: document.querySelector('body')})); ``` ## See [View a fullscreen map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/fullscreen/) ## Events **Event** `fullscreenstart` of type [Event](Event.md) will be fired when fullscreen mode has started. **Event** `fullscreenend` of type [Event](Event.md) will be fired when fullscreen mode has ended. ## Extends - [`Evented`](Evented.md) ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new FullscreenControl**(`options`: [`FullscreenControlOptions`](../type-aliases/FullscreenControlOptions.md)): `FullscreenControl` Defined in: src/ui/control/fullscreen\_control.ts:52 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`FullscreenControlOptions`](../type-aliases/FullscreenControlOptions.md) | the control's options | #### Returns `FullscreenControl` #### Overrides `Evented.constructor` ## Methods ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `FullscreenControl` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `FullscreenControl` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/fullscreen\_control.ts:76 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `FullscreenControl` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `FullscreenControl` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/fullscreen\_control.ts:85 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `FullscreenControl` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `FullscreenControl` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) --- # GeoJSONFeature https://docs.mapatlas.xyz/overview/API/classes/GeoJSONFeature # GeoJSONFeature Defined in: src/util/vectortile\_to\_geojson.ts:28 A geojson feature --- # GeoJSONSource https://docs.mapatlas.xyz/overview/API/classes/GeoJSONSource # GeoJSONSource Defined in: src/source/geojson\_source.ts:111 A source containing GeoJSON. (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/#sources-geojson) for detailed documentation of options.) ## Examples ```ts map.addSource('some id', { type: 'geojson', data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_ports.geojson' }); ``` ```ts map.addSource('some id', { type: 'geojson', data: { "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": {}, "geometry": { "type": "Point", "coordinates": [ -76.53063297271729, 39.18174077994108 ] } }] } }); ``` ```ts map.getSource('some id').setData({ "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": { "name": "Null Island" }, "geometry": { "type": "Point", "coordinates": [ 0, 0 ] } }] }); ``` ## See - [Draw GeoJSON points](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/geojson-markers/) - [Add a GeoJSON line](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/geojson-line/) - [Create a heatmap from points](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/heatmap-layer/) - [Create and style clusters](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/cluster/) ## Extends - [`Evented`](Evented.md) ## Implements - [`Source`](../interfaces/Source.md) ## Methods ### \_updateWorkerData() > **\_updateWorkerData**(`diff?`: [`GeoJSONSourceDiff`](../type-aliases/GeoJSONSourceDiff.md)): `Promise`\<`void`\> Defined in: src/source/geojson\_source.ts:380 Responsible for invoking WorkerSource's geojson.loadData target, which handles loading the geojson data and preparing to serve it up as tiles, using geojson-vt or supercluster as appropriate. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `diff?` | [`GeoJSONSourceDiff`](../type-aliases/GeoJSONSourceDiff.md) | the diff object | #### Returns `Promise`\<`void`\> *** ### abortTile() > **abortTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/geojson\_source.ts:456 Allows to abort a tile loading. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to abort | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`abortTile`](../interfaces/Source.md#aborttile) *** ### getBounds() > **getBounds**(): `Promise`\<[`LngLatBounds`](LngLatBounds.md)\> Defined in: src/source/geojson\_source.ts:274 Allows getting the source's boundaries. If there's a problem with the source's data, it will return an empty [LngLatBounds](LngLatBounds.md). #### Returns `Promise`\<[`LngLatBounds`](LngLatBounds.md)\> a promise which resolves to the source's boundaries *** ### getClusterChildren() > **getClusterChildren**(`clusterId`: `number`): `Promise`\<`Feature`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>[]\> Defined in: src/source/geojson\_source.ts:335 For clustered sources, fetches the children of the given cluster on the next zoom level (as an array of GeoJSON features). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `clusterId` | `number` | The value of the cluster's `cluster_id` property. | #### Returns `Promise`\<`Feature`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>[]\> a promise that is resolved when the features are retrieved *** ### getClusterExpansionZoom() > **getClusterExpansionZoom**(`clusterId`: `number`): `Promise`\<`number`\> Defined in: src/source/geojson\_source.ts:325 For clustered sources, fetches the zoom at which the given cluster expands. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `clusterId` | `number` | The value of the cluster's `cluster_id` property. | #### Returns `Promise`\<`number`\> a promise that is resolved with the zoom number *** ### getClusterLeaves() > **getClusterLeaves**(`clusterId`: `number`, `limit`: `number`, `offset`: `number`): `Promise`\<`Feature`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>[]\> Defined in: src/source/geojson\_source.ts:364 For clustered sources, fetches the original points that belong to the cluster (as an array of GeoJSON features). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `clusterId` | `number` | The value of the cluster's `cluster_id` property. | | `limit` | `number` | The maximum number of features to return. | | `offset` | `number` | The number of features to skip (e.g. for pagination). | #### Returns `Promise`\<`Feature`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>[]\> a promise that is resolved when the features are retrieved #### Example Retrieve cluster leaves on click ```ts map.on('click', 'clusters', (e) => { let features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] }); let clusterId = features[0].properties.cluster_id; let pointCount = features[0].properties.point_count; let clusterSource = map.getSource('clusters'); const features = await clusterSource.getClusterLeaves(clusterId, pointCount); // Print cluster leaves in the console console.log('Cluster leaves:', features); }); ``` *** ### getData() > **getData**(): `Promise`\<`GeoJSON`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>\> Defined in: src/source/geojson\_source.ts:257 Allows to get the source's actual GeoJSON data. #### Returns `Promise`\<`GeoJSON`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>\> a promise which resolves to the source's actual GeoJSON data *** ### hasTransition() > **hasTransition**(): `boolean` Defined in: src/source/geojson\_source.ts:481 True if the source has transition, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTransition`](../interfaces/Source.md#hastransition) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/geojson\_source.ts:424 True if the source is loaded, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`loaded`](../interfaces/Source.md#loaded) *** ### loadTile() > **loadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/geojson\_source.ts:428 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`loadTile`](../interfaces/Source.md#loadtile) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `GeoJSONSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `GeoJSONSource` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `void` Defined in: src/source/geojson\_source.ts:215 This method is called when the source is added to the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The map instance | #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onAdd`](../interfaces/Source.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `GeoJSONSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `GeoJSONSource` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/source/geojson\_source.ts:469 This method is called when the source is removed from the map. #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onRemove`](../interfaces/Source.md#onremove) *** ### serialize() > **serialize**(): `GeoJSONSourceSpecification` Defined in: src/source/geojson\_source.ts:474 #### Returns `GeoJSONSourceSpecification` A plain (stringifiable) JS object representing the current state of the source. Creating a source using the returned object as the `options` should result in a Source that is equivalent to this one. #### Implementation of [`Source`](../interfaces/Source.md).[`serialize`](../interfaces/Source.md#serialize) *** ### setClusterOptions() > **setClusterOptions**(`options`: [`SetClusterOptions`](../type-aliases/SetClusterOptions.md)): `this` Defined in: src/source/geojson\_source.ts:307 To disable/enable clustering on the source options #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`SetClusterOptions`](../type-aliases/SetClusterOptions.md) | The options to set | #### Returns `this` #### Example ```ts map.getSource('some id').setClusterOptions({cluster: false}); map.getSource('some id').setClusterOptions({cluster: false, clusterRadius: 50, clusterMaxZoom: 14}); ``` *** ### setData() > **setData**(`data`: `string` \| `GeoJSON`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>): `this` Defined in: src/source/geojson\_source.ts:225 Sets the GeoJSON data and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `data` | `string` \| `GeoJSON`\<`Geometry`, \{[`name`: `string`]: `any`; \}\> | A GeoJSON data object or a URL to one. The latter is preferable in the case of large GeoJSON files. | #### Returns `this` *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `GeoJSONSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `GeoJSONSource` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### unloadTile() > **unloadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/geojson\_source.ts:464 Allows to unload a tile. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to unload | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`unloadTile`](../interfaces/Source.md#unloadtile) *** ### updateData() > **updateData**(`diff`: [`GeoJSONSourceDiff`](../type-aliases/GeoJSONSourceDiff.md)): `this` Defined in: src/source/geojson\_source.ts:246 Updates the source's GeoJSON, and re-renders the map. For sources with lots of features, this method can be used to make updates more quickly. This approach requires unique IDs for every feature in the source. The IDs can either be specified on the feature, or by using the promoteId option to specify which property should be used as the ID. It is an error to call updateData on a source that did not have unique IDs for each of its features already. Updates are applied on a best-effort basis, updating an ID that does not exist will not result in an error. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `diff` | [`GeoJSONSourceDiff`](../type-aliases/GeoJSONSourceDiff.md) | The changes that need to be applied. | #### Returns `this` ## Properties ### attribution > **attribution**: `string` Defined in: src/source/geojson\_source.ts:117 The attribution for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`attribution`](../interfaces/Source.md#attribution) *** ### id > **id**: `string` Defined in: src/source/geojson\_source.ts:113 The id for the source. Must not be used by any existing source. #### Implementation of [`Source`](../interfaces/Source.md).[`id`](../interfaces/Source.md#id) *** ### isTileClipped > **isTileClipped**: `boolean` Defined in: src/source/geojson\_source.ts:120 `false` if tiles can be drawn outside their boundaries, `true` if they cannot. #### Implementation of [`Source`](../interfaces/Source.md).[`isTileClipped`](../interfaces/Source.md#istileclipped) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/geojson\_source.ts:115 The maximum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`maxzoom`](../interfaces/Source.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/geojson\_source.ts:114 The minimum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`minzoom`](../interfaces/Source.md#minzoom) *** ### reparseOverscaled > **reparseOverscaled**: `boolean` Defined in: src/source/geojson\_source.ts:121 `true` if tiles should be sent back to the worker for each overzoomed zoom level, `false` if not. #### Implementation of [`Source`](../interfaces/Source.md).[`reparseOverscaled`](../interfaces/Source.md#reparseoverscaled) *** ### tileSize > **tileSize**: `number` Defined in: src/source/geojson\_source.ts:116 The tile size for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`tileSize`](../interfaces/Source.md#tilesize) --- # GeolocateControl https://docs.mapatlas.xyz/overview/API/classes/GeolocateControl # GeolocateControl Defined in: src/ui/control/geolocate\_control.ts:240 A `GeolocateControl` control provides a button that uses the browser's geolocation API to locate the user on the map. Not all browsers support geolocation, and some users may disable the feature. Geolocation support for modern browsers including Chrome requires sites to be served over HTTPS. If geolocation support is not available, the `GeolocateControl` will show as disabled. The zoom level applied will depend on the accuracy of the geolocation provided by the device. The `GeolocateControl` has two modes. If `trackUserLocation` is `false` (default) the control acts as a button, which when pressed will set the map's camera to target the user location. If the user moves, the map won't update. This is most suited for the desktop. If `trackUserLocation` is `true` the control acts as a toggle button that when active the user's location is actively monitored for changes. In this mode the `GeolocateControl` has three interaction states: * active - the map's camera automatically updates as the user's location changes, keeping the location dot in the center. Initial state and upon clicking the `GeolocateControl` button. * passive - the user's location dot automatically updates, but the map's camera does not. Occurs upon the user initiating a map movement. * disabled - occurs if Geolocation is not available, disabled or denied. These interaction states can't be controlled programmatically, rather they are set based on user interactions. ## State Diagram ![GeolocateControl state diagram](https://github.com/mapmetrics/mapmetrics-gl-js/assets/3269297/78e720e5-d781-4da8-9803-a7a0e6aaaa9f) ## Examples ```ts map.addControl(new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true })); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when a trackuserlocationend event occurs. geolocate.on('trackuserlocationend', () => { console.log('A trackuserlocationend event has occurred.') }); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when a trackuserlocationstart event occurs. geolocate.on('trackuserlocationstart', () => { console.log('A trackuserlocationstart event has occurred.') }); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when an userlocationlostfocus event occurs. geolocate.on('userlocationlostfocus', function() { console.log('An userlocationlostfocus event has occurred.') }); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when an userlocationfocus event occurs. geolocate.on('userlocationfocus', function() { console.log('An userlocationfocus event has occurred.') }); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when a geolocate event occurs. geolocate.on('geolocate', () => { console.log('A geolocate event has occurred.') }); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when an error event occurs. geolocate.on('error', () => { console.log('An error event has occurred.') }); ``` ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); // Set an event listener that fires // when an outofmaxbounds event occurs. geolocate.on('outofmaxbounds', () => { console.log('An outofmaxbounds event has occurred.') }); ``` ## See [Locate the user](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/locate-user/) ## Events **Event** `trackuserlocationend` of type [Event](Event.md) will be fired when the `GeolocateControl` changes to the background state, which happens when a user changes the camera during an active position lock. This only applies when `trackUserLocation` is `true`. In the background state, the dot on the map will update with location updates but the camera will not. **Event** `trackuserlocationstart` of type [Event](Event.md) will be fired when the `GeolocateControl` changes to the active lock state, which happens either upon first obtaining a successful Geolocation API position for the user (a `geolocate` event will follow), or the user clicks the geolocate button when in the background state which uses the last known position to recenter the map and enter active lock state (no `geolocate` event will follow unless the users's location changes). **Event** `userlocationlostfocus` of type [Event](Event.md) will be fired when the `GeolocateControl` changes to the background state, which happens when a user changes the camera during an active position lock. This only applies when `trackUserLocation` is `true`. In the background state, the dot on the map will update with location updates but the camera will not. **Event** `userlocationfocus` of type [Event](Event.md) will be fired when the `GeolocateControl` changes to the active lock state, which happens upon the user clicks the geolocate button when in the background state which uses the last known position to recenter the map and enter active lock state. **Event** `geolocate` of type [Event](Event.md) will be fired on each Geolocation API position update which returned as success. `data` - The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). **Event** `error` of type [Event](Event.md) will be fired on each Geolocation API position update which returned as an error. `data` - The returned [PositionError](https://developer.mozilla.org/en-US/docs/Web/API/PositionError) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). **Event** `outofmaxbounds` of type [Event](Event.md) will be fired on each Geolocation API position update which returned as success but user position is out of map `maxBounds`. `data` - The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). ## Extends - [`Evented`](Evented.md) ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new GeolocateControl**(`options`: [`GeolocateControlOptions`](../type-aliases/GeolocateControlOptions.md)): `GeolocateControl` Defined in: src/ui/control/geolocate\_control.ts:275 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`GeolocateControlOptions`](../type-aliases/GeolocateControlOptions.md) | the control's options | #### Returns `GeolocateControl` #### Overrides `Evented.constructor` ## Methods ### \_isOutOfMapMaxBounds() > **\_isOutOfMapMaxBounds**(`position`: `GeolocationPosition`): `boolean` Defined in: src/ui/control/geolocate\_control.ts:318 Check if the Geolocation API Position is outside the map's `maxBounds`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `position` | `GeolocationPosition` | the Geolocation API Position | #### Returns `boolean` `true` if position is outside the map's `maxBounds`, otherwise returns `false`. *** ### \_onSuccess() > **\_onSuccess**(`position`: `GeolocationPosition`): `void` Defined in: src/ui/control/geolocate\_control.ts:363 When the Geolocation API returns a new location, update the `GeolocateControl`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `position` | `GeolocationPosition` | the Geolocation API Position | #### Returns `void` *** ### \_updateCamera() > **\_updateCamera**(`position`: `GeolocationPosition`): `void` Defined in: src/ui/control/geolocate\_control.ts:430 Update the camera location to center on the current position #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `position` | `GeolocationPosition` | the Geolocation API Position | #### Returns `void` *** ### \_updateMarker() > **\_updateMarker**(`position?`: `GeolocationPosition`): `void` Defined in: src/ui/control/geolocate\_control.ts:447 Update the user location dot Marker to the current position #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `position?` | `GeolocationPosition` | the Geolocation API Position | #### Returns `void` *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `GeolocateControl` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `GeolocateControl` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/geolocate\_control.ts:281 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `GeolocateControl` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `GeolocateControl` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/geolocate\_control.ts:290 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `GeolocateControl` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `GeolocateControl` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### trigger() > **trigger**(): `boolean` Defined in: src/ui/control/geolocate\_control.ts:618 Programmatically request and move the map to the user's location. #### Returns `boolean` `false` if called before control was added to a map, otherwise returns `true`. #### Example ```ts // Initialize the geolocate control. let geolocate = new GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }); // Add the control to the map. map.addControl(geolocate); map.on('load', () => { geolocate.trigger(); }); ``` --- # GlobeControl https://docs.mapatlas.xyz/overview/API/classes/GlobeControl # GlobeControl Defined in: src/ui/control/globe\_control.ts:19 A `GlobeControl` control contains a button for toggling the map projection between "mercator" and "globe". ## Example ```ts let map = new Map() .addControl(new GlobeControl()); ``` ## See [Display a globe with a fill extrusion layer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/globe-fill-extrusion/) ## Implements - [`IControl`](../interfaces/IControl.md) ## Methods ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/globe\_control.ts:25 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/globe\_control.ts:39 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) --- # Hash https://docs.mapatlas.xyz/overview/API/classes/Hash # Hash Defined in: src/ui/hash.ts:12 Adds the map's position to its page's location hash. Passed as an option to the map object. ## Methods ### addTo() > **addTo**(`map`: [`Map`](Map.md)): `Hash` Defined in: src/ui/hash.ts:25 Map element to listen for coordinate changes #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The map object | #### Returns `Hash` *** ### remove() > **remove**(): `Hash` Defined in: src/ui/hash.ts:35 Removes hash #### Returns `Hash` ## Properties ### \_updateHash() > **\_updateHash**: () => `Timeout` Defined in: src/ui/hash.ts:156 Mobile Safari doesn't allow updating the hash more than 100 times per 30 seconds. #### Returns `Timeout` --- # HeatmapStyleLayer https://docs.mapatlas.xyz/overview/API/classes/HeatmapStyleLayer # HeatmapStyleLayer Defined in: src/style/style\_layer/heatmap\_style\_layer.ts:21 A style layer that defines a heatmap ## Extends - [`StyleLayer`](StyleLayer.md) ## Methods ### getLayoutAffectingGlobalStateRefs() > **getLayoutAffectingGlobalStateRefs**(): `Set`\<`string`\> Defined in: src/style/style\_layer.ts:175 Get list of global state references that are used within layout or filter properties. This is used to determine if layer source need to be reloaded when global state property changes. #### Returns `Set`\<`string`\> #### Inherited from [`StyleLayer`](StyleLayer.md).[`getLayoutAffectingGlobalStateRefs`](StyleLayer.md#getlayoutaffectingglobalstaterefs) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`StyleLayer`](StyleLayer.md).[`listens`](StyleLayer.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `HeatmapStyleLayer` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `HeatmapStyleLayer` #### Inherited from [`StyleLayer`](StyleLayer.md).[`off`](StyleLayer.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`StyleLayer`](StyleLayer.md).[`on`](StyleLayer.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `HeatmapStyleLayer` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `HeatmapStyleLayer` `this` or a promise if a listener is not provided #### Inherited from [`StyleLayer`](StyleLayer.md).[`once`](StyleLayer.md#once) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `HeatmapStyleLayer` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `HeatmapStyleLayer` #### Inherited from [`StyleLayer`](StyleLayer.md).[`setEventedParent`](StyleLayer.md#seteventedparent) --- # ImageAtlas https://docs.mapatlas.xyz/overview/API/classes/ImageAtlas # ImageAtlas Defined in: src/render/image\_atlas.ts:74 A class holding all the images --- # ImageManager https://docs.mapatlas.xyz/overview/API/classes/ImageManager # ImageManager Defined in: src/render/image\_manager.ts:40 ImageManager does three things: 1. Tracks requests for icon images from tile workers and sends responses when the requests are fulfilled. 2. Builds a texture atlas for pattern images. 3. Rerenders renderable images once per frame These are disparate responsibilities and should eventually be handled by different classes. When we implement data-driven support for `*-pattern`, we'll likely use per-bucket pattern atlases, and that would be a good time to refactor this. ## Extends - [`Evented`](Evented.md) ## Methods ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `ImageManager` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `ImageManager` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `ImageManager` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `ImageManager` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `ImageManager` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `ImageManager` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) ## Properties ### requestors > **requestors**: `object`[] Defined in: src/render/image\_manager.ts:49 This is used to track requests for images that are not yet available. When the image is loaded, the requestors will be notified. #### ids > **ids**: `string`[] #### promiseResolve() > **promiseResolve**: (`value`: [`GetImagesResponse`](../type-aliases/GetImagesResponse.md)) => `void` ##### Parameters | Parameter | Type | | ------ | ------ | | `value` | [`GetImagesResponse`](../type-aliases/GetImagesResponse.md) | ##### Returns `void` --- # ImageSource https://docs.mapatlas.xyz/overview/API/classes/ImageSource # ImageSource Defined in: src/source/image\_source.ts:93 A data source containing an image. (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/#sources-image) for detailed documentation of options.) ## Example ```ts // add to map map.addSource('some id', { type: 'image', url: 'https://www.mapmetrics.org/images/foo.png', coordinates: [ [-76.54, 39.18], [-76.52, 39.18], [-76.52, 39.17], [-76.54, 39.17] ] }); // update coordinates let mySource = map.getSource('some id'); mySource.setCoordinates([ [-76.54335737228394, 39.18579907229748], [-76.52803659439087, 39.1838364847587], [-76.5295386314392, 39.17683392507606], [-76.54520273208618, 39.17876344106642] ]); // update url and coordinates simultaneously mySource.updateImage({ url: 'https://www.mapmetrics.org/images/bar.png', coordinates: [ [-76.54335737228394, 39.18579907229748], [-76.52803659439087, 39.1838364847587], [-76.5295386314392, 39.17683392507606], [-76.54520273208618, 39.17876344106642] ] }) map.removeSource('some id'); // remove ``` ## Extends - [`Evented`](Evented.md) ## Extended by - [`CanvasSource`](CanvasSource.md) - [`VideoSource`](VideoSource.md) ## Implements - [`Source`](../interfaces/Source.md) ## Methods ### hasTransition() > **hasTransition**(): `boolean` Defined in: src/source/image\_source.ts:299 True if the source has transition, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTransition`](../interfaces/Source.md#hastransition) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/image\_source.ts:164 True if the source is loaded, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`loaded`](../interfaces/Source.md#loaded) *** ### loadTile() > **loadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/image\_source.ts:276 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`loadTile`](../interfaces/Source.md#loadtile) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `ImageSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `ImageSource` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `void` Defined in: src/source/image\_source.ts:196 This method is called when the source is added to the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The map instance | #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onAdd`](../interfaces/Source.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `ImageSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `ImageSource` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/source/image\_source.ts:201 This method is called when the source is removed from the map. #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onRemove`](../interfaces/Source.md#onremove) *** ### prepare() > **prepare**(): `void` Defined in: src/source/image\_source.ts:248 Allows to execute a prepare step before the source is used. #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`prepare`](../interfaces/Source.md#prepare) *** ### serialize() > **serialize**(): `VideoSourceSpecification` \| `ImageSourceSpecification` \| [`CanvasSourceSpecification`](../type-aliases/CanvasSourceSpecification.md) Defined in: src/source/image\_source.ts:291 #### Returns `VideoSourceSpecification` \| `ImageSourceSpecification` \| [`CanvasSourceSpecification`](../type-aliases/CanvasSourceSpecification.md) A plain (stringifiable) JS object representing the current state of the source. Creating a source using the returned object as the `options` should result in a Source that is equivalent to this one. #### Implementation of [`Source`](../interfaces/Source.md).[`serialize`](../interfaces/Source.md#serialize) *** ### setCoordinates() > **setCoordinates**(`coordinates`: [`Coordinates`](../type-aliases/Coordinates.md)): `this` Defined in: src/source/image\_source.ts:216 Sets the image's coordinates and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `coordinates` | [`Coordinates`](../type-aliases/Coordinates.md) | Four geographical coordinates, represented as arrays of longitude and latitude numbers, which define the corners of the image. The coordinates start at the top left corner of the image and proceed in clockwise order. They do not have to represent a rectangle. | #### Returns `this` *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `ImageSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `ImageSource` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### updateImage() > **updateImage**(`options`: [`UpdateImageOptions`](../type-aliases/UpdateImageOptions.md)): `this` Defined in: src/source/image\_source.ts:174 Updates the image URL and, optionally, the coordinates. To avoid having the image flash after changing, set the `raster-fade-duration` paint property on the raster layer to 0. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`UpdateImageOptions`](../type-aliases/UpdateImageOptions.md) | The options object. | #### Returns `this` ## Properties ### id > **id**: `string` Defined in: src/source/image\_source.ts:95 The id for the source. Must not be used by any existing source. #### Implementation of [`Source`](../interfaces/Source.md).[`id`](../interfaces/Source.md#id) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/image\_source.ts:97 The maximum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`maxzoom`](../interfaces/Source.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/image\_source.ts:96 The minimum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`minzoom`](../interfaces/Source.md#minzoom) *** ### terrainTileRanges > **terrainTileRanges**: `object` Defined in: src/source/image\_source.ts:104 This object is used to store the range of terrain tiles that overlap with this tile. It is relevant for image tiles, as the image exceeds single tile boundaries. #### Index Signature \[`zoom`: `string`\]: `CanonicalTileRange` *** ### tileSize > **tileSize**: `number` Defined in: src/source/image\_source.ts:98 The tile size for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`tileSize`](../interfaces/Source.md#tilesize) --- # KeyboardHandler https://docs.mapatlas.xyz/overview/API/classes/KeyboardHandler # KeyboardHandler Defined in: src/ui/handler/keyboard.ts:27 The `KeyboardHandler` allows the user to zoom, rotate, and pan the map using the following keyboard shortcuts: - `=` / `+`: Increase the zoom level by 1. - `Shift-=` / `Shift-+`: Increase the zoom level by 2. - `-`: Decrease the zoom level by 1. - `Shift--`: Decrease the zoom level by 2. - Arrow keys: Pan by 100 pixels. - `Shift+⇢`: Increase the rotation by 15 degrees. - `Shift+⇠`: Decrease the rotation by 15 degrees. - `Shift+⇡`: Increase the pitch by 10 degrees. - `Shift+⇣`: Decrease the pitch by 10 degrees. ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/keyboard.ts:156 Disables the "keyboard rotate and zoom" interaction. #### Returns `void` #### Example ```ts map.keyboard.disable(); ``` #### Implementation of `Handler.disable` *** ### disableRotation() > **disableRotation**(): `void` Defined in: src/ui/handler/keyboard.ts:192 Disables the "keyboard pan/rotate" interaction, leaving the "keyboard zoom" interaction enabled. #### Returns `void` #### Example ```ts map.keyboard.disableRotation(); ``` *** ### enable() > **enable**(): `void` Defined in: src/ui/handler/keyboard.ts:144 Enables the "keyboard rotate and zoom" interaction. #### Returns `void` #### Example ```ts map.keyboard.enable(); ``` #### Implementation of `Handler.enable` *** ### enableRotation() > **enableRotation**(): `void` Defined in: src/ui/handler/keyboard.ts:205 Enables the "keyboard pan/rotate" interaction. #### Returns `void` #### Example ```ts map.keyboard.enable(); map.keyboard.enableRotation(); ``` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/keyboard.ts:179 Returns true if the handler is enabled and has detected the start of a zoom/rotate gesture. #### Returns `boolean` `true` if the handler is enabled and has detected the start of a zoom/rotate gesture. #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/keyboard.ts:168 Returns a Boolean indicating whether the "keyboard rotate and zoom" interaction is enabled. #### Returns `boolean` `true` if the "keyboard rotate and zoom" interaction is enabled. #### Implementation of `Handler.isEnabled` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/keyboard.ts:46 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # Layout\ https://docs.mapatlas.xyz/overview/API/classes/Layout # Layout\ Defined in: src/style/properties.ts:315 Because layout properties are not transitionable, they have a simpler representation and evaluation chain than paint properties: `PropertyValue`s are possibly evaluated, producing possibly evaluated values, which are then fully evaluated. `Layout` stores a map of all (property name, `PropertyValue`) pairs for layout properties of a given layer type. It can calculate the possibly-evaluated values for all of them at once, producing a `PossiblyEvaluated` instance for the same set of properties. ## Type Parameters | Type Parameter | | ------ | | `Props` | --- # LngLat https://docs.mapatlas.xyz/overview/API/classes/LngLat # LngLat Defined in: src/geo/lng\_lat.ts:53 A `LngLat` object represents a given longitude and latitude coordinate, measured in degrees. These coordinates are based on the [WGS84 (EPSG:4326) standard](https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84). mapmetrics GL JS uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match the [GeoJSON specification](https://tools.ietf.org/html/rfc7946). Note that any mapmetrics GL JS method that accepts a `LngLat` object as an argument or option can also accept an `Array` of two numbers and will perform an implicit conversion. This flexible type is documented as [LngLatLike](../type-aliases/LngLatLike.md). ## Example ```ts let ll = new LngLat(-123.9749, 40.7736); ll.lng; // = -123.9749 ``` ## See - [Get coordinates of the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/mouse-position/) - [Display a popup](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup/) - [Create a timeline animation](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/timeline-animation/) ## Constructors ### Constructor > **new LngLat**(`lng`: `number`, `lat`: `number`): `LngLat` Defined in: src/geo/lng\_lat.ts:68 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lng` | `number` | Longitude, measured in degrees. | | `lat` | `number` | Latitude, measured in degrees. | #### Returns `LngLat` ## Methods ### distanceTo() > **distanceTo**(`lngLat`: `LngLat`): `number` Defined in: src/geo/lng\_lat.ts:135 Returns the approximate distance between a pair of coordinates in meters Uses the Haversine Formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159) #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lngLat` | `LngLat` | coordinates to compute the distance to | #### Returns `number` Distance in meters between the two coordinates. #### Example ```ts let new_york = new LngLat(-74.0060, 40.7128); let los_angeles = new LngLat(-118.2437, 34.0522); new_york.distanceTo(los_angeles); // = 3935751.690893987, "true distance" using a non-spherical approximation is ~3966km ``` *** ### toArray() > **toArray**(): \[`number`, `number`\] Defined in: src/geo/lng\_lat.ts:104 Returns the coordinates represented as an array of two numbers. #### Returns \[`number`, `number`\] The coordinates represented as an array of longitude and latitude. #### Example ```ts let ll = new LngLat(-73.9749, 40.7736); ll.toArray(); // = [-73.9749, 40.7736] ``` *** ### toString() > **toString**(): `string` Defined in: src/geo/lng\_lat.ts:118 Returns the coordinates represent as a string. #### Returns `string` The coordinates represented as a string of the format `'LngLat(lng, lat)'`. #### Example ```ts let ll = new LngLat(-73.9749, 40.7736); ll.toString(); // = "LngLat(-73.9749, 40.7736)" ``` *** ### wrap() > **wrap**(): `LngLat` Defined in: src/geo/lng\_lat.ts:90 Returns a new `LngLat` object whose longitude is wrapped to the range (-180, 180). #### Returns `LngLat` The wrapped `LngLat` object. #### Example ```ts let ll = new LngLat(286.0251, 40.7736); let wrapped = ll.wrap(); wrapped.lng; // = -73.9749 ``` *** ### convert() > `static` **convert**(`input`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `LngLat` Defined in: src/geo/lng\_lat.ts:160 Converts an array of two numbers or an object with `lng` and `lat` or `lon` and `lat` properties to a `LngLat` object. If a `LngLat` object is passed in, the function returns it unchanged. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | [`LngLatLike`](../type-aliases/LngLatLike.md) | An array of two numbers or object to convert, or a `LngLat` object to return. | #### Returns `LngLat` A new `LngLat` object, if a conversion occurred, or the original `LngLat` object. #### Example ```ts let arr = [-73.9749, 40.7736]; let ll = LngLat.convert(arr); ll; // = LngLat {lng: -73.9749, lat: 40.7736} ``` ## Properties ### lat > **lat**: `number` Defined in: src/geo/lng\_lat.ts:62 Latitude, measured in degrees. *** ### lng > **lng**: `number` Defined in: src/geo/lng\_lat.ts:57 Longitude, measured in degrees. --- # LngLatBounds https://docs.mapatlas.xyz/overview/API/classes/LngLatBounds # LngLatBounds Defined in: src/geo/lng\_lat\_bounds.ts:41 A `LngLatBounds` object represents a geographical bounding box, defined by its southwest and northeast points in longitude and latitude. If no arguments are provided to the constructor, a `null` bounding box is created. Note that any Mapbox GL method that accepts a `LngLatBounds` object as an argument or option can also accept an `Array` of two [LngLatLike](../type-aliases/LngLatLike.md) constructs and will perform an implicit conversion. This flexible type is documented as [LngLatBoundsLike](../type-aliases/LngLatBoundsLike.md). ## Example ```ts let sw = new LngLat(-73.9876, 40.7661); let ne = new LngLat(-73.9397, 40.8002); let llb = new LngLatBounds(sw, ne); ``` ## Constructors ### Constructor > **new LngLatBounds**(`sw?`: \[`number`, `number`, `number`, `number`\] \| [`LngLatLike`](../type-aliases/LngLatLike.md) \| \[[`LngLatLike`](../type-aliases/LngLatLike.md), [`LngLatLike`](../type-aliases/LngLatLike.md)\], `ne?`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `LngLatBounds` Defined in: src/geo/lng\_lat\_bounds.ts:65 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sw?` | \[`number`, `number`, `number`, `number`\] \| [`LngLatLike`](../type-aliases/LngLatLike.md) \| \[[`LngLatLike`](../type-aliases/LngLatLike.md), [`LngLatLike`](../type-aliases/LngLatLike.md)\] | The southwest corner of the bounding box. OR array of 4 numbers in the order of west, south, east, north OR array of 2 LngLatLike: [sw,ne] | | `ne?` | [`LngLatLike`](../type-aliases/LngLatLike.md) | The northeast corner of the bounding box. | #### Returns `LngLatBounds` #### Example ```ts let sw = new LngLat(-73.9876, 40.7661); let ne = new LngLat(-73.9397, 40.8002); let llb = new LngLatBounds(sw, ne); ``` OR ```ts let llb = new LngLatBounds([-73.9876, 40.7661, -73.9397, 40.8002]); ``` OR ```ts let llb = new LngLatBounds([sw, ne]); ``` ## Methods ### adjustAntiMeridian() > **adjustAntiMeridian**(): `LngLatBounds` Defined in: src/geo/lng\_lat\_bounds.ts:342 Adjusts the given bounds to handle the case where the bounds cross the 180th meridian (antimeridian). #### Returns `LngLatBounds` The adjusted LngLatBounds #### Example ```ts let bounds = new LngLatBounds([175.813127, -20.157768], [-178. 340903, -15.449124]); let adjustedBounds = bounds.adjustAntiMeridian(); // adjustedBounds will be: [[175.813127, -20.157768], [181.659097, -15.449124]] ``` *** ### contains() > **contains**(`lnglat`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `boolean` Defined in: src/geo/lng\_lat\_bounds.ts:277 Check if the point is within the bounding box. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lnglat` | [`LngLatLike`](../type-aliases/LngLatLike.md) | geographic point to check against. | #### Returns `boolean` `true` if the point is within the bounding box. #### Example ```ts let llb = new LngLatBounds( new LngLat(-73.9876, 40.7661), new LngLat(-73.9397, 40.8002) ); let ll = new LngLat(-73.9567, 40.7789); console.log(llb.contains(ll)); // = true ``` *** ### extend() > **extend**(`obj`: [`LngLatLike`](../type-aliases/LngLatLike.md) \| [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md)): `this` Defined in: src/geo/lng\_lat\_bounds.ts:105 Extend the bounds to include a given LngLatLike or LngLatBoundsLike. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `obj` | [`LngLatLike`](../type-aliases/LngLatLike.md) \| [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md) | object to extend to | #### Returns `this` *** ### getCenter() > **getCenter**(): [`LngLat`](LngLat.md) Defined in: src/geo/lng\_lat\_bounds.ts:161 Returns the geographical coordinate equidistant from the bounding box's corners. #### Returns [`LngLat`](LngLat.md) The bounding box's center. #### Example ```ts let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); llb.getCenter(); // = LngLat {lng: -73.96365, lat: 40.78315} ``` *** ### getEast() > **getEast**(): `number` Defined in: src/geo/lng\_lat\_bounds.ts:212 Returns the east edge of the bounding box. #### Returns `number` The east edge of the bounding box. *** ### getNorth() > **getNorth**(): `number` Defined in: src/geo/lng\_lat\_bounds.ts:219 Returns the north edge of the bounding box. #### Returns `number` The north edge of the bounding box. *** ### getNorthEast() > **getNorthEast**(): [`LngLat`](LngLat.md) Defined in: src/geo/lng\_lat\_bounds.ts:177 Returns the northeast corner of the bounding box. #### Returns [`LngLat`](LngLat.md) The northeast corner of the bounding box. *** ### getNorthWest() > **getNorthWest**(): [`LngLat`](LngLat.md) Defined in: src/geo/lng\_lat\_bounds.ts:184 Returns the northwest corner of the bounding box. #### Returns [`LngLat`](LngLat.md) The northwest corner of the bounding box. *** ### getSouth() > **getSouth**(): `number` Defined in: src/geo/lng\_lat\_bounds.ts:205 Returns the south edge of the bounding box. #### Returns `number` The south edge of the bounding box. *** ### getSouthEast() > **getSouthEast**(): [`LngLat`](LngLat.md) Defined in: src/geo/lng\_lat\_bounds.ts:191 Returns the southeast corner of the bounding box. #### Returns [`LngLat`](LngLat.md) The southeast corner of the bounding box. *** ### getSouthWest() > **getSouthWest**(): [`LngLat`](LngLat.md) Defined in: src/geo/lng\_lat\_bounds.ts:170 Returns the southwest corner of the bounding box. #### Returns [`LngLat`](LngLat.md) The southwest corner of the bounding box. *** ### getWest() > **getWest**(): `number` Defined in: src/geo/lng\_lat\_bounds.ts:198 Returns the west edge of the bounding box. #### Returns `number` The west edge of the bounding box. *** ### isEmpty() > **isEmpty**(): `boolean` Defined in: src/geo/lng\_lat\_bounds.ts:256 Check if the bounding box is an empty/`null`-type box. #### Returns `boolean` True if bounds have been defined, otherwise false. *** ### setNorthEast() > **setNorthEast**(`ne`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `this` Defined in: src/geo/lng\_lat\_bounds.ts:85 Set the northeast corner of the bounding box #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `ne` | [`LngLatLike`](../type-aliases/LngLatLike.md) | a [LngLatLike](../type-aliases/LngLatLike.md) object describing the northeast corner of the bounding box. | #### Returns `this` *** ### setSouthWest() > **setSouthWest**(`sw`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `this` Defined in: src/geo/lng\_lat\_bounds.ts:95 Set the southwest corner of the bounding box #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sw` | [`LngLatLike`](../type-aliases/LngLatLike.md) | a [LngLatLike](../type-aliases/LngLatLike.md) object describing the southwest corner of the bounding box. | #### Returns `this` *** ### toArray() > **toArray**(): \[`number`, `number`\][] Defined in: src/geo/lng\_lat\_bounds.ts:232 Returns the bounding box represented as an array. #### Returns \[`number`, `number`\][] The bounding box represented as an array, consisting of the southwest and northeast coordinates of the bounding represented as arrays of numbers. #### Example ```ts let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]] ``` *** ### toString() > **toString**(): `string` Defined in: src/geo/lng\_lat\_bounds.ts:247 Return the bounding box represented as a string. #### Returns `string` The bounding box represents as a string of the format `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`. #### Example ```ts let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))" ``` *** ### convert() > `static` **convert**(`input`: [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md)): `LngLatBounds` Defined in: src/geo/lng\_lat\_bounds.ts:304 Converts an array to a `LngLatBounds` object. If a `LngLatBounds` object is passed in, the function returns it unchanged. Internally, the function calls `LngLat#convert` to convert arrays to `LngLat` values. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md) | An array of two coordinates to convert, or a `LngLatBounds` object to return. | #### Returns `LngLatBounds` A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object. #### Example ```ts let arr = [[-73.9876, 40.7661], [-73.9397, 40.8002]]; let llb = LngLatBounds.convert(arr); // = LngLatBounds {_sw: LngLat {lng: -73.9876, lat: 40.7661}, _ne: LngLat {lng: -73.9397, lat: 40.8002}} ``` *** ### fromLngLat() > `static` **fromLngLat**(`center`: [`LngLat`](LngLat.md), `radius`: `number`): `LngLatBounds` Defined in: src/geo/lng\_lat\_bounds.ts:322 Returns a `LngLatBounds` from the coordinates extended by a given `radius`. The returned `LngLatBounds` completely contains the `radius`. #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `center` | [`LngLat`](LngLat.md) | `undefined` | center coordinates of the new bounds. | | `radius` | `number` | `0` | Distance in meters from the coordinates to extend the bounds. | #### Returns `LngLatBounds` A new `LngLatBounds` object representing the coordinates extended by the `radius`. #### Example ```ts let center = new LngLat(-73.9749, 40.7736); LngLatBounds.fromLngLat(100).toArray(); // = [[-73.97501862141328, 40.77351016847229], [-73.97478137858673, 40.77368983152771]] ``` --- # LogoControl https://docs.mapatlas.xyz/overview/API/classes/LogoControl # LogoControl Defined in: src/ui/control/logo\_control.ts:27 A `LogoControl` is a control that adds the watermark. ## Example ```ts map.addControl(new LogoControl({compact: false})); ``` ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new LogoControl**(`options`: [`LogoControlOptions`](../type-aliases/LogoControlOptions.md)): `LogoControl` Defined in: src/ui/control/logo\_control.ts:36 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`LogoControlOptions`](../type-aliases/LogoControlOptions.md) | the control's options | #### Returns `LogoControl` ## Methods ### getDefaultPosition() > **getDefaultPosition**(): [`ControlPosition`](../type-aliases/ControlPosition.md) Defined in: src/ui/control/logo\_control.ts:40 Optionally provide a default position for this control. If this method is implemented and [Map#addControl](Map.md#addcontrol) is called without the `position` parameter, the value returned by getDefaultPosition will be used as the control's position. #### Returns [`ControlPosition`](../type-aliases/ControlPosition.md) a control position, one of the values valid in addControl. #### Implementation of [`IControl`](../interfaces/IControl.md).[`getDefaultPosition`](../interfaces/IControl.md#getdefaultposition) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/logo\_control.ts:45 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/logo\_control.ts:65 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) --- # Map https://docs.mapatlas.xyz/overview/API/classes/Map # Map Defined in: src/ui/map.ts:476 The `Map` object represents the map on your page. It exposes methods and properties that enable you to programmatically change the map, and fires events as users interact with it. You create a `Map` by specifying a `container` and other options, see [MapOptions](../type-aliases/MapOptions.md) for the full list. Then mapmetrics GL JS initializes the map on the page and returns your `Map` object. ## Example ```ts let map = new Map({ container: 'map', center: [-122.420679, 37.772537], zoom: 13, style: style_object, hash: true, transformRequest: (url, resourceType)=> { if(resourceType === 'Source' && url.startsWith('http://myHost')) { return { url: url.replace('http', 'https'), headers: { 'my-custom-header': true}, credentials: 'include' // Include cookies for cross-origin requests } } } }); ``` ## See [Display a map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/simple-map/) ## Accessors ### repaint #### Get Signature > **get** **repaint**(): `boolean` Defined in: src/ui/map.ts:3565 Gets and sets a Boolean indicating whether the map will continuously repaint. This information is useful for analyzing performance. ##### Returns `boolean` *** ### showCollisionBoxes #### Get Signature > **get** **showCollisionBoxes**(): `boolean` Defined in: src/ui/map.ts:3533 Gets and sets a Boolean indicating whether the map will render boxes around all symbols in the data source, revealing which symbols were rendered or which were hidden due to collisions. This information is useful for debugging. ##### Returns `boolean` *** ### showOverdrawInspector #### Get Signature > **get** **showOverdrawInspector**(): `boolean` Defined in: src/ui/map.ts:3554 Gets and sets a Boolean indicating whether the map should color-code each fragment to show how many times it has been shaded. White fragments have been shaded 8 or more times. Black fragments have been shaded 0 times. This information is useful for debugging. ##### Returns `boolean` *** ### showPadding #### Get Signature > **get** **showPadding**(): `boolean` Defined in: src/ui/map.ts:3520 Gets and sets a Boolean indicating whether the map will visualize the padding offsets. ##### Returns `boolean` *** ### showTileBoundaries #### Get Signature > **get** **showTileBoundaries**(): `boolean` Defined in: src/ui/map.ts:3509 Gets and sets a Boolean indicating whether the map will render an outline around each tile and the tile ID. These tile boundaries are useful for debugging. The uncompressed file size of the first vector source is drawn in the top left corner of each tile, next to the tile ID. ##### Example ```ts map.showTileBoundaries = true; ``` ##### Returns `boolean` *** ### version #### Get Signature > **get** **version**(): `string` Defined in: src/ui/map.ts:3580 Returns the package version of the library ##### Returns `string` Package version of the library ## Events ### off() #### Call Signature > **off**\<`T`\>(`type`: `T`, `layer`: `string`, `listener`: (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void`): `this` Defined in: src/ui/map.ts:1628 Removes an event listener for events previously added with `Map#on`. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapLayerEventType`](../type-aliases/MapLayerEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The event type previously used to install the listener. | | `layer` | `string` | The layer ID or listener previously used to install the listener. | | `listener` | (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void` | The function previously installed as a listener. | ##### Returns `this` ##### Overrides `Camera.off` #### Call Signature > **off**\<`T`\>(`type`: `T`, `layers`: `string`[], `listener`: (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void`): `this` Defined in: src/ui/map.ts:1641 Overload of the `off` method that allows to remove an event created with multiple layers. Provide the same layer IDs as to `on` or `once`, when the listener was registered. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapLayerEventType`](../type-aliases/MapLayerEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The type of the event. | | `layers` | `string`[] | The layer IDs previously used to install the listener. | | `listener` | (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void` | The function previously installed as a listener. | ##### Returns `this` ##### Overrides `Camera.off` #### Call Signature > **off**\<`T`\>(`type`: `T`, `listener`: (`ev`: [`MapEventType`](../type-aliases/MapEventType.md)\[`T`\] & `Object`) => `void`): `this` Defined in: src/ui/map.ts:1652 Overload of the `off` method that allows to remove an event created without specifying a layer. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapEventType`](../type-aliases/MapEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The type of the event. | | `listener` | (`ev`: [`MapEventType`](../type-aliases/MapEventType.md)\[`T`\] & `Object`) => `void` | The function previously installed as a listener. | ##### Returns `this` ##### Overrides `Camera.off` #### Call Signature > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `this` Defined in: src/ui/map.ts:1659 Overload of the `off` method that allows to remove an event created without specifying a layer. ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The type of the event. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function previously installed as a listener. | ##### Returns `this` ##### Overrides `Camera.off` *** ### on() #### Call Signature > **on**\<`T`\>(`type`: `T`, `layer`: `string`, `listener`: (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void`): [`Subscription`](../interfaces/Subscription.md) Defined in: src/ui/map.ts:1494 Adds a listener for events of a specified type, optionally limited to features in a specified style layer(s). See [MapEventType](../type-aliases/MapEventType.md) and [MapLayerEventType](../type-aliases/MapLayerEventType.md) for a full list of events and their description. | Event | Compatible with `layerId` | |------------------------|---------------------------| | `mousedown` | yes | | `mouseup` | yes | | `mouseover` | yes | | `mouseout` | yes | | `mousemove` | yes | | `mouseenter` | yes (required) | | `mouseleave` | yes (required) | | `click` | yes | | `dblclick` | yes | | `contextmenu` | yes | | `touchstart` | yes | | `touchend` | yes | | `touchcancel` | yes | | `wheel` | | | `resize` | | | `remove` | | | `touchmove` | | | `movestart` | | | `move` | | | `moveend` | | | `dragstart` | | | `drag` | | | `dragend` | | | `zoomstart` | | | `zoom` | | | `zoomend` | | | `rotatestart` | | | `rotate` | | | `rotateend` | | | `pitchstart` | | | `pitch` | | | `pitchend` | | | `boxzoomstart` | | | `boxzoomend` | | | `boxzoomcancel` | | | `webglcontextlost` | | | `webglcontextrestored` | | | `load` | | | `render` | | | `idle` | | | `error` | | | `data` | | | `styledata` | | | `sourcedata` | | | `dataloading` | | | `styledataloading` | | | `sourcedataloading` | | | `styleimagemissing` | | | `dataabort` | | | `sourcedataabort` | | ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapLayerEventType`](../type-aliases/MapLayerEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The event type to listen for. Events compatible with the optional `layerId` parameter are triggered when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas. | | `layer` | `string` | The ID of a style layer or a listener if no ID is provided. Event will only be triggered if its location is within a visible feature in this layer. The event will have a `features` property containing an array of the matching features. If `layer` is not supplied, the event will not have a `features` property. Please note that many event types are not compatible with the optional `layer` parameter. | | `listener` | (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void` | The function to be called when the event is fired. | ##### Returns [`Subscription`](../interfaces/Subscription.md) ##### Examples ```ts // Set an event listener that will fire // when the map has finished loading map.on('load', () => { // Once the map has finished loading, // add a new layer map.addLayer({ id: 'points-of-interest', source: { type: 'vector', url: 'https://mapmetrics.org/mapmetrics-style-spec/' }, 'source-layer': 'poi_label', type: 'circle', paint: { // mapmetrics Style Specification paint properties }, layout: { // mapmetrics Style Specification layout properties } }); }); ``` ```ts // Set an event listener that will fire // when a feature on the countries layer of the map is clicked map.on('click', 'countries', (e) => { new Popup() .setLngLat(e.lngLat) .setHTML(`Country name: ${e.features[0].properties.name}`) .addTo(map); }); ``` ##### See - [Display popup on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-click/) - [Center the map on a clicked symbol](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/center-on-symbol/) - [Create a hover effect](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) - [Create a draggable marker](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) ##### Overrides `Camera.on` #### Call Signature > **on**\<`T`\>(`type`: `T`, `layerIds`: `string`[], `listener`: (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void`): [`Subscription`](../interfaces/Subscription.md) Defined in: src/ui/map.ts:1506 Overload of the `on` method that allows to listen to events specifying multiple layers. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapLayerEventType`](../type-aliases/MapLayerEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The type of the event. | | `layerIds` | `string`[] | The array of style layer IDs. | | `listener` | (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void` | The listener callback. | ##### Returns [`Subscription`](../interfaces/Subscription.md) ##### Overrides `Camera.on` #### Call Signature > **on**\<`T`\>(`type`: `T`, `listener`: (`ev`: [`MapEventType`](../type-aliases/MapEventType.md)\[`T`\] & `Object`) => `void`): [`Subscription`](../interfaces/Subscription.md) Defined in: src/ui/map.ts:1517 Overload of the `on` method that allows to listen to events without specifying a layer. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapEventType`](../type-aliases/MapEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The type of the event. | | `listener` | (`ev`: [`MapEventType`](../type-aliases/MapEventType.md)\[`T`\] & `Object`) => `void` | The listener callback. | ##### Returns [`Subscription`](../interfaces/Subscription.md) ##### Overrides `Camera.on` #### Call Signature > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/ui/map.ts:1524 Overload of the `on` method that allows to listen to events without specifying a layer. ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The type of the event. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener callback. | ##### Returns [`Subscription`](../interfaces/Subscription.md) ##### Overrides `Camera.on` *** ### once() #### Call Signature > **once**\<`T`\>(`type`: `T`, `layer`: `string`, `listener?`: (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void`): `Map` \| `Promise`\<[`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`\> Defined in: src/ui/map.ts:1563 Adds a listener that will be called only once to a specified event type, optionally limited to features in a specified style layer. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapLayerEventType`](../type-aliases/MapLayerEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The event type to listen for; one of `'mousedown'`, `'mouseup'`, `'click'`, `'dblclick'`, `'mousemove'`, `'mouseenter'`, `'mouseleave'`, `'mouseover'`, `'mouseout'`, `'contextmenu'`, `'touchstart'`, `'touchend'`, or `'touchcancel'`. `mouseenter` and `mouseover` events are triggered when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave` and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves the map canvas. | | `layer` | `string` | The ID of a style layer or a listener if no ID is provided. Only events whose location is within a visible feature in this layer will trigger the listener. The event will have a `features` property containing an array of the matching features. | | `listener?` | (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void` | The function to be called when the event is fired. | ##### Returns `Map` \| `Promise`\<[`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`\> `this` if listener is provided, promise otherwise to allow easier usage of async/await ##### Overrides `Camera.once` #### Call Signature > **once**\<`T`\>(`type`: `T`, `layerIds`: `string`[], `listener?`: (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void`): `Promise`\<`any`\> \| `Map` Defined in: src/ui/map.ts:1575 Overload of the `once` method that allows to listen to events specifying multiple layers. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapLayerEventType`](../type-aliases/MapLayerEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The type of the event. | | `layerIds` | `string`[] | The array of style layer IDs. | | `listener?` | (`ev`: [`MapLayerEventType`](../type-aliases/MapLayerEventType.md)\[`T`\] & `Object`) => `void` | The listener callback. | ##### Returns `Promise`\<`any`\> \| `Map` ##### Overrides `Camera.once` #### Call Signature > **once**\<`T`\>(`type`: `T`, `listener?`: (`ev`: [`MapEventType`](../type-aliases/MapEventType.md)\[`T`\] & `Object`) => `void`): `Promise`\<`any`\> \| `Map` Defined in: src/ui/map.ts:1586 Overload of the `once` method that allows to listen to events without specifying a layer. ##### Type Parameters | Type Parameter | | ------ | | `T` *extends* keyof [`MapEventType`](../type-aliases/MapEventType.md) | ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `T` | The type of the event. | | `listener?` | (`ev`: [`MapEventType`](../type-aliases/MapEventType.md)\[`T`\] & `Object`) => `void` | The listener callback. | ##### Returns `Promise`\<`any`\> \| `Map` ##### Overrides `Camera.once` #### Call Signature > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `Map` Defined in: src/ui/map.ts:1593 Overload of the `once` method that allows to listen to events without specifying a layer. ##### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The type of the event. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The listener callback. | ##### Returns `Promise`\<`any`\> \| `Map` ##### Overrides `Camera.once` ## Methods ### addControl() > **addControl**(`control`: [`IControl`](../interfaces/IControl.md), `position?`: [`ControlPosition`](../type-aliases/ControlPosition.md)): `Map` Defined in: src/ui/map.ts:817 Adds an [IControl](../interfaces/IControl.md) to the map, calling `control.onAdd(this)`. An [ErrorEvent](ErrorEvent.md) will be fired if the image parameter is invalid. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `control` | [`IControl`](../interfaces/IControl.md) | The [IControl](../interfaces/IControl.md) to add. | | `position?` | [`ControlPosition`](../type-aliases/ControlPosition.md) | position on the map to which the control will be added. Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`. | #### Returns `Map` #### Example Add zoom and rotation controls to the map. ```ts map.addControl(new NavigationControl()); ``` #### See [Display map navigation controls](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/navigation/) *** ### addImage() > **addImage**(`id`: `string`, `image`: `ImageBitmap` \| `HTMLImageElement` \| `ImageData` \| [`StyleImageInterface`](../interfaces/StyleImageInterface.md) \| \{ `data`: `Uint8Array`\<`ArrayBufferLike`\> \| `Uint8ClampedArray`\<`ArrayBufferLike`\>; `height`: `number`; `width`: `number`; \}, `options`: `Partial`\<[`StyleImageMetadata`](../type-aliases/StyleImageMetadata.md)\>): `this` Defined in: src/ui/map.ts:2299 Add an image to the style. This image can be displayed on the map like any other icon in the style's sprite using the image's ID with [`icon-image`](https://mapmetrics.org/mapmetrics-style-spec/layers/#layout-symbol-icon-image), [`background-pattern`](https://mapmetrics.org/mapmetrics-style-spec/layers/#paint-background-background-pattern), [`fill-pattern`](https://mapmetrics.org/mapmetrics-style-spec/layers/#paint-fill-fill-pattern), or [`line-pattern`](https://mapmetrics.org/mapmetrics-style-spec/layers/#paint-line-line-pattern). A [ErrorEvent](ErrorEvent.md) event will be fired if the image parameter is invalid or there is not enough space in the sprite to add this image. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the image. | | `image` | `ImageBitmap` \| `HTMLImageElement` \| `ImageData` \| [`StyleImageInterface`](../interfaces/StyleImageInterface.md) \| \{ `data`: `Uint8Array`\<`ArrayBufferLike`\> \| `Uint8ClampedArray`\<`ArrayBufferLike`\>; `height`: `number`; `width`: `number`; \} | The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data` properties with the same format as `ImageData`. | | `options` | `Partial`\<[`StyleImageMetadata`](../type-aliases/StyleImageMetadata.md)\> | Options object. | #### Returns `this` #### Example ```ts // If the style's sprite does not already contain an image with ID 'cat', // add the image 'cat-icon.png' to the style's sprite with the ID 'cat'. const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png'); if (!map.hasImage('cat')) map.addImage('cat', image.data); // Add a stretchable image that can be used with `icon-text-fit` // In this example, the image is 600px wide by 400px high. const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/8/89/Black_and_White_Boxed_%28bordered%29.png'); if (map.hasImage('border-image')) return; map.addImage('border-image', image.data, { content: [16, 16, 300, 384], // place text over left half of image, avoiding the 16px border stretchX: [[16, 584]], // stretch everything horizontally except the 16px border stretchY: [[16, 384]], // stretch everything vertically except the 16px border }); ``` #### See - Use `HTMLImageElement`: [Add an icon to the map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image/) - Use `ImageData`: [Add a generated icon to the map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image-generated/) *** ### addLayer() > **addLayer**(`layer`: [`AddLayerObject`](../type-aliases/AddLayerObject.md), `beforeId?`: `string`): `Map` Defined in: src/ui/map.ts:2579 Adds a [mapmetrics style layer](https://mapmetrics.org/mapmetrics-style-spec/layers) to the map's style. A layer defines how data from a specified source will be styled. Read more about layer types and available paint and layout properties in the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/layers). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layer` | [`AddLayerObject`](../type-aliases/AddLayerObject.md) | The layer to add, conforming to either the mapmetrics Style Specification's [layer definition](https://mapmetrics.org/mapmetrics-style-spec/layers) or, less commonly, the [CustomLayerInterface](../interfaces/CustomLayerInterface.md) specification. Can also be a layer definition with an embedded source definition. The mapmetrics Style Specification's layer definition is appropriate for most layers. | | `beforeId?` | `string` | The ID of an existing layer to insert the new layer before, resulting in the new layer appearing visually beneath the existing layer. If this argument is not specified, the layer will be appended to the end of the layers array and appear visually above all other layers. | #### Returns `Map` #### Examples Add a circle layer with a vector source ```ts map.addLayer({ id: 'points-of-interest', source: { type: 'vector', url: 'https://demotiles.mapmetrics.org/tiles/tiles.json' }, 'source-layer': 'poi_label', type: 'circle', paint: { // mapmetrics Style Specification paint properties }, layout: { // mapmetrics Style Specification layout properties } }); ``` Define a source before using it to create a new layer ```ts map.addSource('state-data', { type: 'geojson', data: 'path/to/data.geojson' }); map.addLayer({ id: 'states', // References the GeoJSON source defined above // and does not require a `source-layer` source: 'state-data', type: 'symbol', layout: { // Set the label content to the // feature's `name` property text-field: ['get', 'name'] } }); ``` Add a new symbol layer before an existing layer ```ts map.addLayer({ id: 'states', // References a source that's already been defined source: 'state-data', type: 'symbol', layout: { // Set the label content to the // feature's `name` property text-field: ['get', 'name'] } // Add the layer before the existing `cities` layer }, 'cities'); ``` #### See - [Create and style clusters](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/cluster/) - [Add a vector tile source](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/vector-source/) - [Add a WMS source](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/wms/) *** ### addSource() > **addSource**(`id`: `string`, `source`: [`SourceSpecification`](https://mapmetrics.org/mapmetrics-style-spec/sources/) \| [`CanvasSourceSpecification`](../type-aliases/CanvasSourceSpecification.md)): `this` Defined in: src/ui/map.ts:2032 Adds a source to the map's style. Events triggered: Triggers the `source.add` event. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the source to add. Must not conflict with existing sources. | | `source` | [`SourceSpecification`](https://mapmetrics.org/mapmetrics-style-spec/sources/) \| [`CanvasSourceSpecification`](../type-aliases/CanvasSourceSpecification.md) | The source object, conforming to the mapmetrics Style Specification's [source definition](https://mapmetrics.org/mapmetrics-style-spec/sources) or [CanvasSourceSpecification](../type-aliases/CanvasSourceSpecification.md). | #### Returns `this` #### Examples ```ts map.addSource('my-data', { type: 'vector', url: 'https://demotiles.mapmetrics.org/tiles/tiles.json' }); ``` ```ts map.addSource('my-data', { "type": "geojson", "data": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-77.0323, 38.9131] }, "properties": { "title": "Mapbox DC", "marker-symbol": "monument" } } }); ``` #### See GeoJSON source: [Add live realtime data](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/live-geojson/) *** ### addSprite() > **addSprite**(`id`: `string`, `url`: `string`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `this` Defined in: src/ui/map.ts:2821 Adds a sprite to the map's style. Fires the `style` event. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the sprite to add. Must not conflict with existing sprites. | | `url` | `string` | The URL to load the sprite from | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `this` #### Example ```ts map.addSprite('sprite-two', 'http://example.com/sprite-two'); ``` *** ### areTilesLoaded() > **areTilesLoaded**(): `boolean` Defined in: src/ui/map.ts:2154 Returns a Boolean indicating whether all tiles in the viewport from all sources on the style are loaded. #### Returns `boolean` A Boolean indicating whether all tiles are loaded. #### Example ```ts let tilesLoaded = map.areTilesLoaded(); ``` *** ### calculateCameraOptionsFromCameraLngLatAltRotation() > **calculateCameraOptionsFromCameraLngLatAltRotation**(`cameraLngLat`: [`LngLatLike`](../type-aliases/LngLatLike.md), `cameraAlt`: `number`, `bearing`: `number`, `pitch`: `number`, `roll?`: `number`): [`CameraOptions`](../type-aliases/CameraOptions.md) Defined in: src/ui/camera.ts:1055 Given a camera position and rotation, calculates zoom and center point and returns them as [CameraOptions](../type-aliases/CameraOptions.md). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `cameraLngLat` | [`LngLatLike`](../type-aliases/LngLatLike.md) | The lng, lat of the camera to look from | | `cameraAlt` | `number` | The altitude of the camera to look from, in meters above sea level | | `bearing` | `number` | Bearing of the camera, in degrees | | `pitch` | `number` | Pitch of the camera, in degrees | | `roll?` | `number` | Roll of the camera, in degrees | #### Returns [`CameraOptions`](../type-aliases/CameraOptions.md) the calculated camera options #### Example ```ts // Calculate options to look from camera position(1°, 0°, 1000m) with bearing = 90°, pitch = 30°, and roll = 45° const cameraLngLat = new LngLat(1, 0); const cameraAltitude = 1000; const bearing = 90; const pitch = 30; const roll = 45; const cameraOptions = map.calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAltitude, bearing, pitch, roll); // Apply calculated options map.jumpTo(cameraOptions); ``` #### Inherited from `Camera.calculateCameraOptionsFromCameraLngLatAltRotation` *** ### calculateCameraOptionsFromTo() > **calculateCameraOptionsFromTo**(`from`: [`LngLat`](LngLat.md), `altitudeFrom`: `number`, `to`: [`LngLat`](LngLat.md), `altitudeTo?`: `number`): [`CameraOptions`](../type-aliases/CameraOptions.md) Defined in: src/ui/map.ts:887 Given a camera 'from' position and a position to look at (`to`), calculates zoom and camera rotation and returns them as [CameraOptions](../type-aliases/CameraOptions.md). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `from` | [`LngLat`](LngLat.md) | The camera to look from | | `altitudeFrom` | `number` | The altitude of the camera to look from | | `to` | [`LngLat`](LngLat.md) | The center to look at | | `altitudeTo?` | `number` | Optional altitude of the center to look at. If none given the ground height will be used. | #### Returns [`CameraOptions`](../type-aliases/CameraOptions.md) the calculated camera options #### Example ```ts // Calculate options to look from (1°, 0°, 1000m) to (1°, 1°, 0m) const cameraLngLat = new LngLat(1, 0); const cameraAltitude = 1000; const targetLngLat = new LngLat(1, 1); const targetAltitude = 0; const cameraOptions = map.calculateCameraOptionsFromTo(cameraLngLat, cameraAltitude, targetLngLat, targetAltitude); // Apply calculated options map.jumpTo(cameraOptions); ``` #### Overrides `Camera.calculateCameraOptionsFromTo` *** ### cameraForBounds() > **cameraForBounds**(`bounds`: [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md), `options?`: [`CameraForBoundsOptions`](../type-aliases/CameraForBoundsOptions.md)): [`CenterZoomBearing`](../type-aliases/CenterZoomBearing.md) Defined in: src/ui/camera.ts:764 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `bounds` | [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md) | Calculate the center for these bounds in the viewport and use the highest zoom level up to and including `Map#getMaxZoom()` that fits in the viewport. LngLatBounds represent a box that is always axis-aligned with bearing 0. Bounds will be taken in [sw, ne] order. Southwest point will always be to the left of the northeast point. | | `options?` | [`CameraForBoundsOptions`](../type-aliases/CameraForBoundsOptions.md) | Options object | #### Returns [`CenterZoomBearing`](../type-aliases/CenterZoomBearing.md) If map is able to fit to provided bounds, returns `center`, `zoom`, and `bearing`. If map is unable to fit, method will warn and return undefined. #### Example ```ts let bbox = [[-79, 43], [-73, 45]]; let newCameraTransform = map.cameraForBounds(bbox, { padding: {top: 10, bottom:25, left: 15, right: 5} }); ``` #### Inherited from `Camera.cameraForBounds` *** ### easeTo() > **easeTo**(`options`: [`EaseToOptions`](../type-aliases/EaseToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:1084 Changes any combination of `center`, `zoom`, `bearing`, `pitch`, `roll`, and `padding` with an animated transition between old and new values. The map will retain its current values for any details not specified in `options`. Note: The transition will happen instantly if the user has enabled the `reduced motion` accessibility feature enabled in their operating system, unless `options` includes `essential: true`. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`EaseToOptions`](../type-aliases/EaseToOptions.md) | Options describing the destination and animation of the transition. Accepts [CameraOptions](../type-aliases/CameraOptions.md) and [AnimationOptions](../type-aliases/AnimationOptions.md). | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### See [Navigate the map with game-like controls](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/game-controls/) #### Inherited from `Camera.easeTo` *** ### fitBounds() > **fitBounds**(`bounds`: [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md), `options?`: [`FitBoundsOptions`](../type-aliases/FitBoundsOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:843 Pans and zooms the map to contain its visible area within the specified geographical bounds. This function will also reset the map's bearing to 0 if bearing is nonzero. Triggers the following events: `movestart` and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `bounds` | [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md) | Center these bounds in the viewport and use the highest zoom level up to and including `Map#getMaxZoom()` that fits them in the viewport. Bounds will be taken in [sw, ne] order. Southwest point will always be to the left of the northeast point. | | `options?` | [`FitBoundsOptions`](../type-aliases/FitBoundsOptions.md) | Options supports all properties from [AnimationOptions](../type-aliases/AnimationOptions.md) and [CameraOptions](../type-aliases/CameraOptions.md) in addition to the fields below. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example ```ts let bbox = [[-79, 43], [-73, 45]]; map.fitBounds(bbox, { padding: {top: 10, bottom:25, left: 15, right: 5} }); ``` #### See [Fit a map to a bounding box](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/fitbounds/) #### Inherited from `Camera.fitBounds` *** ### fitScreenCoordinates() > **fitScreenCoordinates**(`p0`: [`PointLike`](../type-aliases/PointLike.md), `p1`: [`PointLike`](../type-aliases/PointLike.md), `bearing`: `number`, `options?`: [`FitBoundsOptions`](../type-aliases/FitBoundsOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:872 Pans, rotates and zooms the map to to fit the box made by points p0 and p1 once the map is rotated to the specified bearing. To zoom without rotating, pass in the current map bearing. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend` and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `p0` | [`PointLike`](../type-aliases/PointLike.md) | First point on screen, in pixel coordinates | | `p1` | [`PointLike`](../type-aliases/PointLike.md) | Second point on screen, in pixel coordinates | | `bearing` | `number` | Desired map bearing at end of animation, in degrees | | `options?` | [`FitBoundsOptions`](../type-aliases/FitBoundsOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example ```ts let p0 = [220, 400]; let p1 = [500, 900]; map.fitScreenCoordinates(p0, p1, map.getBearing(), { padding: {top: 10, bottom:25, left: 15, right: 5} }); ``` #### See Used by [BoxZoomHandler](BoxZoomHandler.md) #### Inherited from `Camera.fitScreenCoordinates` *** ### flyTo() > **flyTo**(`options`: [`FlyToOptions`](../type-aliases/FlyToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:1379 Changes any combination of center, zoom, bearing, pitch, and roll, animating the transition along a curve that evokes flight. The animation seamlessly incorporates zooming and panning to help the user maintain her bearings even after traversing a great distance. Note: The animation will be skipped, and this will behave equivalently to `jumpTo` if the user has the `reduced motion` accessibility feature enabled in their operating system, unless 'options' includes `essential: true`. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`FlyToOptions`](../type-aliases/FlyToOptions.md) | Options describing the destination and animation of the transition. Accepts [CameraOptions](../type-aliases/CameraOptions.md), [AnimationOptions](../type-aliases/AnimationOptions.md), and the following additional options. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example ```ts // fly with default options to null island map.flyTo({center: [0, 0], zoom: 9}); // using flyTo options map.flyTo({ center: [0, 0], zoom: 9, speed: 0.2, curve: 1, easing(t) { return t; } }); ``` #### See - [Fly to a location](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/flyto/) - [Slowly fly to a location](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/flyto-options/) - [Fly to a location based on scroll position](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/scroll-fly-to/) #### Inherited from `Camera.flyTo` *** ### getBearing() > **getBearing**(): `number` Defined in: src/ui/camera.ts:595 Returns the map's current bearing. The bearing is the compass direction that is "up"; for example, a bearing of 90° orients the map so that east is up. #### Returns `number` The map's current bearing. #### See [Navigate the map with game-like controls](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/game-controls/) #### Inherited from `Camera.getBearing` *** ### getBounds() > **getBounds**(): [`LngLatBounds`](LngLatBounds.md) Defined in: src/ui/map.ts:1003 Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region. #### Returns [`LngLatBounds`](LngLatBounds.md) The geographical bounds of the map as [LngLatBounds](LngLatBounds.md). #### Example ```ts let bounds = map.getBounds(); ``` *** ### getCameraTargetElevation() > **getCameraTargetElevation**(): `number` Defined in: src/ui/map.ts:3590 Returns the elevation for the point where the camera is looking. This value corresponds to: "meters above sea level" * "exaggeration" #### Returns `number` The elevation. *** ### getCanvas() > **getCanvas**(): `HTMLCanvasElement` Defined in: src/ui/map.ts:3087 Returns the map's `` element. #### Returns `HTMLCanvasElement` The map's `` element. #### See - [Measure distances](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/measure/) - [Display a popup on hover](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) - [Center the map on a clicked symbol](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/center-on-symbol/) *** ### getCanvasContainer() > **getCanvasContainer**(): `HTMLElement` Defined in: src/ui/map.ts:3075 Returns the HTML element containing the map's `` element. If you want to add non-GL overlays to the map, you should append them to this element. This is the element to which event bindings for map interactivity (such as panning and zooming) are attached. It will receive bubbled events from child elements such as the ``, but not from map controls. #### Returns `HTMLElement` The container of the map's ``. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### getCenter() > **getCenter**(): [`LngLat`](LngLat.md) Defined in: src/ui/camera.ts:365 Returns the map's geographical centerpoint. #### Returns [`LngLat`](LngLat.md) The map's geographical centerpoint. #### Example Return a LngLat object such as `{lng: 0, lat: 0}` ```ts let center = map.getCenter(); // access longitude and latitude values directly let {lng, lat} = map.getCenter(); ``` #### Inherited from `Camera.getCenter` *** ### getCenterClampedToGround() > **getCenterClampedToGround**(): `boolean` Defined in: src/ui/camera.ts:411 Returns the value of `centerClampedToGround`. If true, the elevation of the center point will automatically be set to the terrain elevation (or zero if terrain is not enabled). If false, the elevation of the center point will default to sea level and will not automatically update. Defaults to true. Needs to be set to false to keep the camera above ground when pitch \> 90 degrees. #### Returns `boolean` #### Inherited from `Camera.getCenterClampedToGround` *** ### getCenterElevation() > **getCenterElevation**(): `number` Defined in: src/ui/camera.ts:388 Returns the elevation of the map's center point. #### Returns `number` The elevation of the map's center point, in meters above sea level. #### Inherited from `Camera.getCenterElevation` *** ### getContainer() > **getContainer**(): `HTMLElement` Defined in: src/ui/map.ts:3059 Returns the map's containing HTML element. #### Returns `HTMLElement` The map's container. *** ### getFeatureState() > **getFeatureState**(`feature`: [`FeatureIdentifier`](../type-aliases/FeatureIdentifier.md)): `any` Defined in: src/ui/map.ts:3050 Gets the `state` of a feature. A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime. Features are identified by their `feature.id` attribute, which can be any number or string. _Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://mapmetrics.org/mapmetrics-style-spec/expressions/#feature-state)._ #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `feature` | [`FeatureIdentifier`](../type-aliases/FeatureIdentifier.md) | Feature identifier. Feature objects returned from [Map#queryRenderedFeatures](#queryrenderedfeatures) or event handlers can be used as feature identifiers. | #### Returns `any` The state of the feature: a set of key-value pairs that was assigned to the feature at runtime. #### Example When the mouse moves over the `my-layer` layer, get the feature state for the feature under the mouse ```ts map.on('mousemove', 'my-layer', (e) => { if (e.features.length > 0) { map.getFeatureState({ source: 'my-source', sourceLayer: 'my-source-layer', id: e.features[0].id }); } }); ``` *** ### getFilter() > **getFilter**(`layerId`: `string`): `void` \| `FilterSpecification` Defined in: src/ui/map.ts:2721 Returns the filter applied to the specified style layer. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the style layer whose filter to get. | #### Returns `void` \| `FilterSpecification` The layer's filter. *** ### getGlobalState() > **getGlobalState**(): `Record`\<`string`, `any`\> Defined in: src/ui/map.ts:798 Returns the global map state #### Returns `Record`\<`string`, `any`\> The map state object. *** ### getGlyphs() > **getGlyphs**(): `string` Defined in: src/ui/map.ts:2806 Returns the value of the style's glyphs URL #### Returns `string` glyphs Style's glyphs url *** ### getImage() > **getImage**(`id`: `string`): [`StyleImage`](../type-aliases/StyleImage.md) Defined in: src/ui/map.ts:2417 Returns an image, specified by ID, currently available in the map. This includes both images from the style's original sprite and any images that have been added at runtime using [Map#addImage](#addimage). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the image. | #### Returns [`StyleImage`](../type-aliases/StyleImage.md) An image in the map with the specified ID. #### Example ```ts let coffeeShopIcon = map.getImage("coffee_cup"); ``` *** ### getLayer() > **getLayer**(`id`: `string`): [`StyleLayer`](StyleLayer.md) Defined in: src/ui/map.ts:2634 Returns the layer with the specified ID in the map's style. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the layer to get. | #### Returns [`StyleLayer`](StyleLayer.md) The layer with the specified ID, or `undefined` if the ID corresponds to no existing layers. #### Example ```ts let stateDataLayer = map.getLayer('state-data'); ``` #### See - [Filter symbols by toggling a list](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/filter-markers/) - [Filter symbols by text input](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/filter-markers-by-input/) *** ### getLayersOrder() > **getLayersOrder**(): `string`[] Defined in: src/ui/map.ts:2648 Return the ids of all layers currently in the style, including custom layers, in order. #### Returns `string`[] ids of layers, in order #### Example ```ts const orderedLayerIds = map.getLayersOrder(); ``` *** ### getLayoutProperty() > **getLayoutProperty**(`layerId`: `string`, `name`: `string`): `any` Defined in: src/ui/map.ts:2781 Returns the value of a layout property in the specified style layer. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the layer to get the layout property from. | | `name` | `string` | The name of the layout property to get. | #### Returns `any` The value of the specified layout property. *** ### getLight() > **getLight**(): `LightSpecification` Defined in: src/ui/map.ts:2898 Returns the value of the light object. #### Returns `LightSpecification` light Light properties of the style. *** ### getMaxBounds() > **getMaxBounds**(): [`LngLatBounds`](LngLatBounds.md) Defined in: src/ui/map.ts:1015 Returns the maximum geographical bounds the map is constrained to, or `null` if none set. #### Returns [`LngLatBounds`](LngLatBounds.md) The map object. #### Example ```ts let maxBounds = map.getMaxBounds(); ``` *** ### getMaxPitch() > **getMaxPitch**(): `number` Defined in: src/ui/map.ts:1200 Returns the map's maximum allowable pitch. #### Returns `number` The maxPitch *** ### getMaxZoom() > **getMaxZoom**(): `number` Defined in: src/ui/map.ts:1128 Returns the map's maximum allowable zoom level. #### Returns `number` The maxZoom #### Example ```ts let maxZoom = map.getMaxZoom(); ``` *** ### getMinPitch() > **getMinPitch**(): `number` Defined in: src/ui/map.ts:1164 Returns the map's minimum allowable pitch. #### Returns `number` The minPitch *** ### getMinZoom() > **getMinZoom**(): `number` Defined in: src/ui/map.ts:1088 Returns the map's minimum allowable zoom level. #### Returns `number` minZoom #### Example ```ts let minZoom = map.getMinZoom(); ``` *** ### getPadding() > **getPadding**(): [`PaddingOptions`](../type-aliases/PaddingOptions.md) Defined in: src/ui/camera.ts:623 Returns the current padding applied around the map viewport. #### Returns [`PaddingOptions`](../type-aliases/PaddingOptions.md) The current padding around the map viewport. #### Inherited from `Camera.getPadding` *** ### getPaintProperty() > **getPaintProperty**(`layerId`: `string`, `name`: `string`): `unknown` Defined in: src/ui/map.ts:2753 Returns the value of a paint property in the specified style layer. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the layer to get the paint property from. | | `name` | `string` | The name of a paint property to get. | #### Returns `unknown` The value of the specified paint property. *** ### getPitch() > **getPitch**(): `number` Defined in: src/ui/camera.ts:713 Returns the map's current pitch (tilt). #### Returns `number` The map's current pitch, measured in degrees away from the plane of the screen. #### Inherited from `Camera.getPitch` *** ### getPixelRatio() > **getPixelRatio**(): `number` Defined in: src/ui/map.ts:977 Returns the map's pixel ratio. Note that the pixel ratio actually applied may be lower to respect maxCanvasSize. #### Returns `number` The pixel ratio. *** ### getProjection() > **getProjection**(): [`ProjectionSpecification`](https://mapmetrics.org/mapmetrics-style-spec/projection/) Defined in: src/ui/map.ts:3602 Gets the [ProjectionSpecification](https://mapmetrics.org/mapmetrics-style-spec/projection/). #### Returns [`ProjectionSpecification`](https://mapmetrics.org/mapmetrics-style-spec/projection/) the projection specification. #### Example ```ts let projection = map.getProjection(); ``` *** ### getRenderWorldCopies() > **getRenderWorldCopies**(): `boolean` Defined in: src/ui/map.ts:1216 Returns the state of `renderWorldCopies`. If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire container, there will be blank space beyond 180 and -180 degrees longitude. - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the map and the other on the left edge of the map) at every zoom level. #### Returns `boolean` The renderWorldCopies #### Example ```ts let worldCopiesRendered = map.getRenderWorldCopies(); ``` #### See [Render world copies](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/render-world-copies/) *** ### getRoll() > **getRoll**(): `number` Defined in: src/ui/camera.ts:733 Returns the map's current roll angle. #### Returns `number` The map's current roll, measured in degrees about the camera boresight. #### Inherited from `Camera.getRoll` *** ### getSky() > **getSky**(): `SkySpecification` Defined in: src/ui/map.ts:2928 Returns the value of the style's sky. #### Returns `SkySpecification` the sky properties of the style. #### Example ```ts map.getSky(); ``` *** ### getSource() > **getSource**\<`TSource`\>(`id`: `string`): `TSource` Defined in: src/ui/map.ts:2203 Returns the source with the specified ID in the map's style. This method is often used to update a source using the instance members for the relevant source type as defined in classes that derive from [Source](../interfaces/Source.md). For example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates` of an image source. #### Type Parameters | Type Parameter | | ------ | | `TSource` *extends* [`Source`](../interfaces/Source.md) | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the source to get. | #### Returns `TSource` The style source with the specified ID or `undefined` if the ID corresponds to no existing sources. The shape of the object varies by source type. A list of options for each source type is available on the mapmetrics Style Specification's [Sources](https://mapmetrics.org/mapmetrics-style-spec/sources/) page. #### Example ```ts let sourceObject = map.getSource('points'); ``` #### See - [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) - [Animate a point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/animate-point-along-line/) - [Add live realtime data](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/live-geojson/) *** ### getSprite() > **getSprite**(): `object`[] Defined in: src/ui/map.ts:2852 Returns the as-is value of the style's sprite. #### Returns `object`[] style's sprite list of id-url pairs *** ### getStyle() > **getStyle**(): `StyleSpecification` Defined in: src/ui/map.ts:1974 Returns the map's mapmetrics style object, a JSON object which can be used to recreate the map's style. #### Returns `StyleSpecification` The map's style JSON object. #### Example ```ts let styleJson = map.getStyle(); ``` *** ### getTerrain() > **getTerrain**(): `TerrainSpecification` Defined in: src/ui/map.ts:2140 Get the terrain-options if terrain is loaded #### Returns `TerrainSpecification` the TerrainSpecification passed to setTerrain #### Example ```ts map.getTerrain(); // { source: 'terrain' }; ``` *** ### getVerticalFieldOfView() > **getVerticalFieldOfView**(): `number` Defined in: src/ui/camera.ts:562 Returns the map's current vertical field of view, in degrees. #### Returns `number` The map's current vertical field of view. #### Default Value ```ts 36.87 ``` #### Example ```ts const verticalFieldOfView = map.getVerticalFieldOfView(); ``` #### Inherited from `Camera.getVerticalFieldOfView` *** ### getZoom() > **getZoom**(): `number` Defined in: src/ui/camera.ts:471 Returns the map's current zoom level. #### Returns `number` The map's current zoom level. #### Example ```ts map.getZoom(); ``` #### Inherited from `Camera.getZoom` *** ### hasControl() > **hasControl**(`control`: [`IControl`](../interfaces/IControl.md)): `boolean` Defined in: src/ui/map.ts:883 Checks if a control exists on the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `control` | [`IControl`](../interfaces/IControl.md) | The [IControl](../interfaces/IControl.md) to check. | #### Returns `boolean` true if map contains control. #### Example ```ts // Define a new navigation control. let navigation = new NavigationControl(); // Add zoom and rotation controls to the map. map.addControl(navigation); // Check that the navigation control exists on the map. map.hasControl(navigation); ``` *** ### hasImage() > **hasImage**(`id`: `string`): `boolean` Defined in: src/ui/map.ts:2437 Check whether or not an image with a specific ID exists in the style. This checks both images in the style's original sprite and any images that have been added at runtime using [Map#addImage](#addimage). An [ErrorEvent](ErrorEvent.md) will be fired if the image parameter is invalid. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the image. | #### Returns `boolean` A Boolean indicating whether the image exists. #### Example Check if an image with the ID 'cat' exists in the style's sprite. ```ts let catIconExists = map.hasImage('cat'); ``` *** ### isMoving() > **isMoving**(): `boolean` Defined in: src/ui/map.ts:1282 Returns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture. #### Returns `boolean` true if the map is moving. #### Example ```ts let isMoving = map.isMoving(); ``` *** ### isRotating() > **isRotating**(): `boolean` Defined in: src/ui/map.ts:1306 Returns true if the map is rotating due to a camera animation or user gesture. #### Returns `boolean` true if the map is rotating. #### Example ```ts map.isRotating(); ``` *** ### isSourceLoaded() > **isSourceLoaded**(`id`: `string`): `boolean` Defined in: src/ui/map.ts:2051 Returns a Boolean indicating whether the source is loaded. Returns `true` if the source with the given ID in the map's style has no outstanding network requests, otherwise `false`. A [ErrorEvent](ErrorEvent.md) event will be fired if there is no source wit the specified ID. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the source to be checked. | #### Returns `boolean` A Boolean indicating whether the source is loaded. #### Example ```ts let sourceLoaded = map.isSourceLoaded('bathymetry-data'); ``` *** ### isStyleLoaded() > **isStyleLoaded**(): `boolean` \| `void` Defined in: src/ui/map.ts:1990 Returns a Boolean indicating whether the map's style is fully loaded. #### Returns `boolean` \| `void` A Boolean indicating whether the style is fully loaded. #### Example ```ts let styleLoadStatus = map.isStyleLoaded(); ``` *** ### isZooming() > **isZooming**(): `boolean` Defined in: src/ui/map.ts:1294 Returns true if the map is zooming due to a camera animation or user gesture. #### Returns `boolean` true if the map is zooming. #### Example ```ts let isZooming = map.isZooming(); ``` *** ### jumpTo() > **jumpTo**(`options`: [`JumpToOptions`](../type-aliases/JumpToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:921 Changes any combination of center, zoom, bearing, pitch, and roll, without an animated transition. The map will retain its current values for any details not specified in `options`. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll`, `rollend` and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`JumpToOptions`](../type-aliases/JumpToOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example ```ts // jump to coordinates at current zoom map.jumpTo({center: [0, 0]}); // jump with zoom, pitch, and bearing options map.jumpTo({ center: [0, 0], zoom: 8, pitch: 45, bearing: 90 }); ``` #### See - [Jump to a series of locations](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/jump-to/) - [Update a feature in realtime](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/live-update-feature/) #### Inherited from `Camera.jumpTo` *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from `Camera.listens` *** ### listImages() > **listImages**(): `string`[] Defined in: src/ui/map.ts:2496 Returns an Array of strings containing the IDs of all images currently available in the map. This includes both images from the style's original sprite and any images that have been added at runtime using [Map#addImage](#addimage). #### Returns `string`[] An Array of strings containing the names of all sprites/images currently available in the map. #### Example ```ts let allImages = map.listImages(); ``` *** ### loaded() > **loaded**(): `boolean` Defined in: src/ui/map.ts:3227 Returns a Boolean indicating whether the map is fully loaded. Returns `false` if the style is not yet fully loaded, or if there has been a change to the sources or style that has not yet fully loaded. #### Returns `boolean` A Boolean indicating whether the map is fully loaded. *** ### loadImage() > **loadImage**(`url`: `string`): `Promise`\<[`GetResourceResponse`](../type-aliases/GetResourceResponse.md)\<`ImageBitmap` \| `HTMLImageElement`\>\> Defined in: src/ui/map.ts:2480 Load an image from an external URL to be used with [Map#addImage](#addimage). External domains must support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `url` | `string` | The URL of the image file. Image file must be in png, webp, or jpg format. | #### Returns `Promise`\<[`GetResourceResponse`](../type-aliases/GetResourceResponse.md)\<`ImageBitmap` \| `HTMLImageElement`\>\> a promise that is resolved when the image is loaded #### Example Load an image from an external URL. ```ts const response = await map.loadImage('https://picsum.photos/50/50'); // Add the loaded image to the style's sprite with the ID 'photo'. map.addImage('photo', response.data); ``` #### See [Add an icon to the map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image/) *** ### moveLayer() > **moveLayer**(`id`: `string`, `beforeId?`: `string`): `this` Defined in: src/ui/map.ts:2597 Moves a layer to a different z-position. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the layer to move. | | `beforeId?` | `string` | The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map. | #### Returns `this` #### Example Move a layer with ID 'polygon' before the layer with ID 'country-label'. The `polygon` layer will appear beneath the `country-label` layer on the map. ```ts map.moveLayer('polygon', 'country-label'); ``` *** ### panBy() > **panBy**(`offset`: [`PointLike`](../type-aliases/PointLike.md), `options?`: [`EaseToOptions`](../type-aliases/EaseToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:435 Pans the map by the specified offset. Triggers the following events: `movestart` and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `offset` | [`PointLike`](../type-aliases/PointLike.md) | `x` and `y` coordinates by which to pan the map. | | `options?` | [`EaseToOptions`](../type-aliases/EaseToOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### See [Navigate the map with game-like controls](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/game-controls/) #### Inherited from `Camera.panBy` *** ### panTo() > **panTo**(`lnglat`: [`LngLatLike`](../type-aliases/LngLatLike.md), `options?`: [`EaseToOptions`](../type-aliases/EaseToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:456 Pans the map to the specified location with an animated transition. Triggers the following events: `movestart` and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lnglat` | [`LngLatLike`](../type-aliases/LngLatLike.md) | The location to pan the map to. | | `options?` | [`EaseToOptions`](../type-aliases/EaseToOptions.md) | Options describing the destination and animation of the transition. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example ```ts map.panTo([-74, 38]); // Specify that the panTo animation should last 5000 milliseconds. map.panTo([-74, 38], {duration: 5000}); ``` #### See [Update a feature in realtime](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/live-update-feature/) #### Inherited from `Camera.panTo` *** ### project() > **project**(`lnglat`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `Point` Defined in: src/ui/map.ts:1252 Returns a [Point](https://github.com/mapbox/point-geometry) representing pixel coordinates, relative to the map's `container`, that correspond to the specified geographical location. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lnglat` | [`LngLatLike`](../type-aliases/LngLatLike.md) | The geographical location to project. | #### Returns `Point` The [Point](https://github.com/mapbox/point-geometry) corresponding to `lnglat`, relative to the map's `container`. #### Example ```ts let coordinate = [-122.420679, 37.772537]; let point = map.project(coordinate); ``` *** ### queryRenderedFeatures() > **queryRenderedFeatures**(`geometryOrOptions?`: [`PointLike`](../type-aliases/PointLike.md) \| [`QueryRenderedFeaturesOptions`](../type-aliases/QueryRenderedFeaturesOptions.md) \| \[[`PointLike`](../type-aliases/PointLike.md), [`PointLike`](../type-aliases/PointLike.md)\], `options?`: [`QueryRenderedFeaturesOptions`](../type-aliases/QueryRenderedFeaturesOptions.md)): [`MapGeoJSONFeature`](../type-aliases/MapGeoJSONFeature.md)[] Defined in: src/ui/map.ts:1750 Returns an array of MapGeoJSONFeature objects representing visible features that satisfy the query parameters. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `geometryOrOptions?` | [`PointLike`](../type-aliases/PointLike.md) \| [`QueryRenderedFeaturesOptions`](../type-aliases/QueryRenderedFeaturesOptions.md) \| \[[`PointLike`](../type-aliases/PointLike.md), [`PointLike`](../type-aliases/PointLike.md)\] | (optional) The geometry of the query region: either a single point or southwest and northeast points describing a bounding box. Omitting this parameter (i.e. calling [Map#queryRenderedFeatures](#queryrenderedfeatures) with zero arguments, or with only a `options` argument) is equivalent to passing a bounding box encompassing the entire map viewport. The geometryOrOptions can receive a [QueryRenderedFeaturesOptions](../type-aliases/QueryRenderedFeaturesOptions.md) only to support a situation where the function receives only one parameter which is the options parameter. | | `options?` | [`QueryRenderedFeaturesOptions`](../type-aliases/QueryRenderedFeaturesOptions.md) | (optional) Options object. | #### Returns [`MapGeoJSONFeature`](../type-aliases/MapGeoJSONFeature.md)[] An array of MapGeoJSONFeature objects. The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only string and numeric property values are supported (i.e. `null`, `Array`, and `Object` values are not supported). Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object representing the style layer to which the feature belongs. Layout and paint properties in this object contain values which are fully evaluated for the given zoom level and feature. Only features that are currently rendered are included. Some features will **not** be included, like: - Features from layers whose `visibility` property is `"none"`. - Features from layers whose zoom range excludes the current zoom level. - Symbol features that have been hidden due to text or icon collision. Features from all other layers are included, including features that may have no visible contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to 0. The topmost rendered feature appears first in the returned array, and subsequent features are sorted by descending z-order. Features that are rendered multiple times (due to wrapping across the antemeridian at low zoom levels) are returned only once (though subject to the following caveat). Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. The results of the query will be those parts of the highway that lie within the map tiles covering the bounding rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple tiles due to tile buffering. #### Examples Find all features at a point ```ts let features = map.queryRenderedFeatures( [20, 35], { layers: ['my-layer-name'] } ); ``` Find all features within a static bounding box ```ts let features = map.queryRenderedFeatures( [[10, 20], [30, 50]], { layers: ['my-layer-name'] } ); ``` Find all features within a bounding box around a point ```ts let width = 10; let height = 20; let features = map.queryRenderedFeatures([ [point.x - width / 2, point.y - height / 2], [point.x + width / 2, point.y + height / 2] ], { layers: ['my-layer-name'] }); ``` Query all rendered features from a single layer ```ts let features = map.queryRenderedFeatures({ layers: ['my-layer-name'] }); ``` #### See [Get features under the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/queryrenderedfeatures/) *** ### querySourceFeatures() > **querySourceFeatures**(`sourceId`: `string`, `parameters?`: [`QuerySourceFeatureOptions`](../type-aliases/QuerySourceFeatureOptions.md)): [`GeoJSONFeature`](GeoJSONFeature.md)[] Defined in: src/ui/map.ts:1800 Returns an array of MapGeoJSONFeature objects representing features within the specified vector tile or GeoJSON source that satisfy the query parameters. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sourceId` | `string` | The ID of the vector tile or GeoJSON source to query. | | `parameters?` | [`QuerySourceFeatureOptions`](../type-aliases/QuerySourceFeatureOptions.md) | The options object. | #### Returns [`GeoJSONFeature`](GeoJSONFeature.md)[] An array of MapGeoJSONFeature objects. In contrast to [Map#queryRenderedFeatures](#queryrenderedfeatures), this function returns all features matching the query parameters, whether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently visible viewport. Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. The results of the query will be those parts of the highway that lie within the map tiles covering the bounding rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple tiles due to tile buffering. #### Example Find all features in one source layer in a vector source ```ts let features = map.querySourceFeatures('your-source-id', { sourceLayer: 'your-source-layer' }); ``` *** ### queryTerrainElevation() > **queryTerrainElevation**(`lngLatLike`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `number` Defined in: src/ui/camera.ts:1629 Gets the elevation at a given location, in meters above sea level. Returns null if terrain is not enabled. If terrain is enabled with some exaggeration value, the value returned here will be reflective of (multiplied by) that exaggeration value. This method should be used for proper positioning of custom 3d objects, as explained [here](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-3d-model-with-terrain/) #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lngLatLike` | [`LngLatLike`](../type-aliases/LngLatLike.md) | [x,y] or LngLat coordinates of the location | #### Returns `number` elevation in meters #### Inherited from `Camera.queryTerrainElevation` *** ### redraw() > **redraw**(): `this` Defined in: src/ui/map.ts:3402 Force a synchronous redraw of the map. #### Returns `this` #### Example ```ts map.redraw(); ``` *** ### refreshTiles() > **refreshTiles**(`sourceId`: `string`, `tileIds?`: `object`[]): `void` Defined in: src/ui/map.ts:2253 Triggers a reload of the selected tiles #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sourceId` | `string` | The ID of the source | | `tileIds?` | `object`[] | An array of tile IDs to be reloaded. If not defined, all tiles will be reloaded. | #### Returns `void` #### Example ```ts map.refreshTiles('satellite', [{x:1024, y: 1023, z: 11}, {x:1023, y: 1023, z: 11}]); ``` *** ### remove() > **remove**(): `void` Defined in: src/ui/map.ts:3423 Clean up and release all internal resources associated with this map. This includes DOM elements, event bindings, web workers, and WebGL resources. Use this method when you are done using the map and wish to ensure that it no longer consumes browser resources. Afterwards, you must not call any other methods on the map. #### Returns `void` *** ### removeControl() > **removeControl**(`control`: [`IControl`](../interfaces/IControl.md)): `Map` Defined in: src/ui/map.ts:857 Removes the control from the map. An [ErrorEvent](ErrorEvent.md) will be fired if the image parameter is invalid. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `control` | [`IControl`](../interfaces/IControl.md) | The [IControl](../interfaces/IControl.md) to remove. | #### Returns `Map` #### Example ```ts // Define a new navigation control. let navigation = new NavigationControl(); // Add zoom and rotation controls to the map. map.addControl(navigation); // Remove zoom and rotation controls from the map. map.removeControl(navigation); ``` *** ### removeFeatureState() > **removeFeatureState**(`target`: [`FeatureIdentifier`](../type-aliases/FeatureIdentifier.md), `key?`: `string`): `this` Defined in: src/ui/map.ts:3019 Removes the `state` of a feature, setting it back to the default behavior. If only a `target.source` is specified, it will remove the state for all features from that source. If `target.id` is also specified, it will remove all keys for that feature's state. If `key` is also specified, it removes only that key from that feature's state. Features are identified by their `feature.id` attribute, which can be any number or string. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `target` | [`FeatureIdentifier`](../type-aliases/FeatureIdentifier.md) | Identifier of where to remove state. It can be a source, a feature, or a specific key of feature. Feature objects returned from [Map#queryRenderedFeatures](#queryrenderedfeatures) or event handlers can be used as feature identifiers. | | `key?` | `string` | (optional) The key in the feature state to reset. | #### Returns `this` #### Examples Reset the entire state object for all features in the `my-source` source ```ts map.removeFeatureState({ source: 'my-source' }); ``` When the mouse leaves the `my-layer` layer, reset the entire state object for the feature under the mouse ```ts map.on('mouseleave', 'my-layer', (e) => { map.removeFeatureState({ source: 'my-source', sourceLayer: 'my-source-layer', id: e.features[0].id }); }); ``` When the mouse leaves the `my-layer` layer, reset only the `hover` key-value pair in the state for the feature under the mouse ```ts map.on('mouseleave', 'my-layer', (e) => { map.removeFeatureState({ source: 'my-source', sourceLayer: 'my-source-layer', id: e.features[0].id }, 'hover'); }); ``` *** ### removeImage() > **removeImage**(`id`: `string`): `void` Defined in: src/ui/map.ts:2460 Remove an image from a style. This can be an image from the style's original sprite or any images that have been added at runtime using [Map#addImage](#addimage). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the image. | #### Returns `void` #### Example ```ts // If an image with the ID 'cat' exists in // the style's sprite, remove it. if (map.hasImage('cat')) map.removeImage('cat'); ``` *** ### removeLayer() > **removeLayer**(`id`: `string`): `this` Defined in: src/ui/map.ts:2615 Removes the layer with the given ID from the map's style. An [ErrorEvent](ErrorEvent.md) will be fired if the image parameter is invalid. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the layer to remove | #### Returns `this` #### Example If a layer with ID 'state-data' exists, remove it. ```ts if (map.getLayer('state-data')) map.removeLayer('state-data'); ``` *** ### removeSource() > **removeSource**(`id`: `string`): `Map` Defined in: src/ui/map.ts:2176 Removes a source from the map's style. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the source to remove. | #### Returns `Map` #### Example ```ts map.removeSource('bathymetry-data'); ``` *** ### removeSprite() > **removeSprite**(`id`: `string`): `Map` Defined in: src/ui/map.ts:2841 Removes the sprite from the map's style. Fires the `style` event. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the sprite to remove. If the sprite is declared as a single URL, the ID must be "default". | #### Returns `Map` #### Example ```ts map.removeSprite('sprite-two'); map.removeSprite('default'); ``` *** ### resetNorth() > **resetNorth**(`options?`: [`AnimationOptions`](../type-aliases/AnimationOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:669 Rotates the map so that north is up (0° bearing), with an animated transition. Triggers the following events: `movestart`, `moveend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`AnimationOptions`](../type-aliases/AnimationOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.resetNorth` *** ### resetNorthPitch() > **resetNorthPitch**(`options?`: [`AnimationOptions`](../type-aliases/AnimationOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:682 Rotates and pitches the map so that north is up (0° bearing) and pitch and roll are 0°, with an animated transition. Triggers the following events: `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`AnimationOptions`](../type-aliases/AnimationOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.resetNorthPitch` *** ### resize() > **resize**(`eventData?`: `any`, `constrainTransform?`: `boolean`): `Map` Defined in: src/ui/map.ts:914 Resizes the map according to the dimensions of its `container` element. Checks if the map container size changed and updates the map if it has changed. This method must be called after the map's `container` is resized programmatically or when the map is shown after being initially hidden with CSS. Triggers the following events: `movestart`, `move`, `moveend`, and `resize`. #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `eventData?` | `any` | `undefined` | Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend` events that get triggered as a result of resize. This can be useful for differentiating the source of an event (for example, user-initiated or programmatically-triggered events). | | `constrainTransform?` | `boolean` | `true` | - | #### Returns `Map` #### Example Resize the map when the map container is shown after being initially hidden with CSS. ```ts let mapDiv = document.getElementById('map'); if (mapDiv.style.visibility === true) map.resize(); ``` *** ### rotateTo() > **rotateTo**(`bearing`: `number`, `options?`: [`EaseToOptions`](../type-aliases/EaseToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:655 Rotates the map to the specified bearing, with an animated transition. The bearing is the compass direction that is "up"; for example, a bearing of 90° orients the map so that east is up. Triggers the following events: `movestart`, `moveend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `bearing` | `number` | The desired bearing. | | `options?` | [`EaseToOptions`](../type-aliases/EaseToOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.rotateTo` *** ### setBearing() > **setBearing**(`bearing`: `number`, `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:613 Sets the map's bearing (rotation). The bearing is the compass direction that is "up"; for example, a bearing of 90° orients the map so that east is up. Equivalent to `jumpTo({bearing: bearing})`. Triggers the following events: `movestart`, `moveend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `bearing` | `number` | The desired bearing. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example Rotate the map to 90 degrees ```ts map.setBearing(90); ``` #### Inherited from `Camera.setBearing` *** ### setCenter() > **setCenter**(`center`: [`LngLatLike`](../type-aliases/LngLatLike.md), `eventData?`: `any`): `Map` Defined in: src/ui/camera.ts:379 Sets the map's geographical centerpoint. Equivalent to `jumpTo({center: center})`. Triggers the following events: `movestart` and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `center` | [`LngLatLike`](../type-aliases/LngLatLike.md) | The centerpoint to set. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `Map` #### Example ```ts map.setCenter([-74, 38]); ``` #### Inherited from `Camera.setCenter` *** ### setCenterClampedToGround() > **setCenterClampedToGround**(`centerClampedToGround`: `boolean`): `void` Defined in: src/ui/camera.ts:421 Sets the value of `centerClampedToGround`. If true, the elevation of the center point will automatically be set to the terrain elevation (or zero if terrain is not enabled). If false, the elevation of the center point will default to sea level and will not automatically update. Defaults to true. Needs to be set to false to keep the camera above ground when pitch \> 90 degrees. #### Parameters | Parameter | Type | | ------ | ------ | | `centerClampedToGround` | `boolean` | #### Returns `void` #### Inherited from `Camera.setCenterClampedToGround` *** ### setCenterElevation() > **setCenterElevation**(`elevation`: `number`, `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:398 Sets the elevation of the map's center point, in meters above sea level. Equivalent to `jumpTo({elevation: elevation})`. Triggers the following events: `movestart` and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `elevation` | `number` | The elevation to set, in meters above sea level. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.setCenterElevation` *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `Map` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `Map` #### Inherited from `Camera.setEventedParent` *** ### setFeatureState() > **setFeatureState**(`feature`: [`FeatureIdentifier`](../type-aliases/FeatureIdentifier.md), `state`: `any`): `this` Defined in: src/ui/map.ts:2968 Sets the `state` of a feature. A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime. When using this method, the `state` object is merged with any existing key-value pairs in the feature's state. Features are identified by their `feature.id` attribute, which can be any number or string. This method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways: - For vector or GeoJSON sources, including an `id` attribute in the original data file. - For vector or GeoJSON sources, using the [`promoteId`](https://mapmetrics.org/mapmetrics-style-spec/sources/#promoteid) option at the time the source is defined. - For GeoJSON sources, using the [`generateId`](https://mapmetrics.org/mapmetrics-style-spec/sources/#generateid) option to auto-assign an `id` based on the feature's index in the source data. If you change feature data using `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values. _Note: You can use the [`feature-state` expression](https://mapmetrics.org/mapmetrics-style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling._ #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `feature` | [`FeatureIdentifier`](../type-aliases/FeatureIdentifier.md) | Feature identifier. Feature objects returned from [Map#queryRenderedFeatures](#queryrenderedfeatures) or event handlers can be used as feature identifiers. | | `state` | `any` | A set of key-value pairs. The values should be valid JSON types. | #### Returns `this` #### Example ```ts // When the mouse moves over the `my-layer` layer, update // the feature state for the feature under the mouse map.on('mousemove', 'my-layer', (e) => { if (e.features.length > 0) { map.setFeatureState({ source: 'my-source', sourceLayer: 'my-source-layer', id: e.features[0].id, }, { hover: true }); } }); ``` #### See [Create a hover effect](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) *** ### setFilter() > **setFilter**(`layerId`: `string`, `filter?`: `FilterSpecification`, `options?`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `Map` Defined in: src/ui/map.ts:2710 Sets the filter for the specified style layer. Filters control which features a style layer renders from its source. Any feature for which the filter expression evaluates to `true` will be rendered on the map. Those that are false will be hidden. Use `setFilter` to show a subset of your source data. To clear the filter, pass `null` or `undefined` as the second parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the layer to which the filter will be applied. | | `filter?` | `FilterSpecification` | The filter, conforming to the mapmetrics Style Specification's [filter definition](https://mapmetrics.org/mapmetrics-style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer. | | `options?` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `Map` #### Examples Display only features with the 'name' property 'USA' ```ts map.setFilter('my-layer', ['==', ['get', 'name'], 'USA']); ``` Display only features with five or more 'available-spots' ```ts map.setFilter('bike-docks', ['>=', ['get', 'available-spots'], 5]); ``` Remove the filter for the 'bike-docks' style layer ```ts map.setFilter('bike-docks', null); ``` #### See [Create a timeline animation](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/timeline-animation/) *** ### setGlobalStateProperty() > **setGlobalStateProperty**(`propertyName`: `string`, `value`: `any`): `Map` Defined in: src/ui/map.ts:788 Sets a global state property that can be retrieved with the [`global-state` expression](https://mapmetrics.org/mapmetrics-style-spec/expressions/#global-state). If the value is null, it resets the property to its default value defined in the [`state` style property](https://mapmetrics.org/mapmetrics-style-spec/root/#state). Note that changing `global-state` values defined in layout properties is not supported, and will be ignored. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `propertyName` | `string` | The name of the state property to set. | | `value` | `any` | The value of the state property to set. | #### Returns `Map` *** ### setGlyphs() > **setGlyphs**(`glyphsUrl`: `string`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `this` Defined in: src/ui/map.ts:2795 Sets the value of the style's glyphs property. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `glyphsUrl` | `string` | Glyph URL to set. Must conform to the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/glyphs/). | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `this` #### Example ```ts map.setGlyphs('https://demotiles.mapmetrics.org/font/{fontstack}/{range}.pbf'); ``` *** ### setLayerZoomRange() > **setLayerZoomRange**(`layerId`: `string`, `minzoom`: `number`, `maxzoom`: `number`): `this` Defined in: src/ui/map.ts:2672 Sets the zoom extent for the specified style layer. The zoom extent includes the [minimum zoom level](https://mapmetrics.org/mapmetrics-style-spec/layers/#minzoom) and [maximum zoom level](https://mapmetrics.org/mapmetrics-style-spec/layers/#maxzoom)) at which the layer will be rendered. Note: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style layer will not be rendered at all zoom levels in the zoom range. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the layer to which the zoom extent will be applied. | | `minzoom` | `number` | The minimum zoom to set (0-24). | | `maxzoom` | `number` | The maximum zoom to set (0-24). | #### Returns `this` #### Example ```ts map.setLayerZoomRange('my-layer', 2, 5); ``` *** ### setLayoutProperty() > **setLayoutProperty**(`layerId`: `string`, `name`: `string`, `value`: `any`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `this` Defined in: src/ui/map.ts:2769 Sets the value of a layout property in the specified style layer. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the layer to set the layout property in. | | `name` | `string` | The name of the layout property to set. | | `value` | `any` | The value of the layout property. Must be of a type appropriate for the property, as defined in the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/). | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | The options object. | #### Returns `this` #### Example ```ts map.setLayoutProperty('my-layer', 'visibility', 'none'); ``` *** ### setLight() > **setLight**(`light`: `LightSpecification`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `Map` Defined in: src/ui/map.ts:2887 Sets the any combination of light values. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `light` | `LightSpecification` | Light properties to set. Must conform to the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/light). | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `Map` #### Example ```ts let layerVisibility = map.getLayoutProperty('my-layer', 'visibility'); ``` *** ### setMaxBounds() > **setMaxBounds**(`bounds?`: [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md)): `Map` Defined in: src/ui/map.ts:1040 Sets or clears the map's geographical bounds. Pan and zoom operations are constrained within these bounds. If a pan or zoom is performed that would display regions outside these bounds, the map will instead display a position and zoom level as close as possible to the operation's request while still remaining within the bounds. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `bounds?` | [`LngLatBoundsLike`](../type-aliases/LngLatBoundsLike.md) | The maximum bounds to set. If `null` or `undefined` is provided, the function removes the map's maximum bounds. | #### Returns `Map` #### Example Define bounds that conform to the `LngLatBoundsLike` object as set the max bounds. ```ts let bounds = [ [-74.04728, 40.68392], // [west, south] [-73.91058, 40.87764] // [east, north] ]; map.setMaxBounds(bounds); ``` *** ### setMaxPitch() > **setMaxPitch**(`maxPitch?`: `number`): `Map` Defined in: src/ui/map.ts:1176 Sets or clears the map's maximum pitch. If the map's current pitch is higher than the new maximum, the map will pitch to the new maximum. A [ErrorEvent](ErrorEvent.md) event will be fired if maxPitch is out of bounds. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `maxPitch?` | `number` | The maximum pitch to set (0-180). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the mapmetrics project. If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60). | #### Returns `Map` *** ### setMaxZoom() > **setMaxZoom**(`maxZoom?`: `number`): `Map` Defined in: src/ui/map.ts:1104 Sets or clears the map's maximum zoom level. If the map's current zoom level is higher than the new maximum, the map will zoom to the new maximum. A [ErrorEvent](ErrorEvent.md) event will be fired if minZoom is out of bounds. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `maxZoom?` | `number` | The maximum zoom level to set. If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22). | #### Returns `Map` #### Example ```ts map.setMaxZoom(18.75); ``` *** ### setMinPitch() > **setMinPitch**(`minPitch?`: `number`): `Map` Defined in: src/ui/map.ts:1140 Sets or clears the map's minimum pitch. If the map's current pitch is lower than the new minimum, the map will pitch to the new minimum. A [ErrorEvent](ErrorEvent.md) event will be fired if minPitch is out of bounds. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `minPitch?` | `number` | The minimum pitch to set (0-180). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the mapmetrics project. If `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0). | #### Returns `Map` *** ### setMinZoom() > **setMinZoom**(`minZoom?`: `number`): `Map` Defined in: src/ui/map.ts:1064 Sets or clears the map's minimum zoom level. If the map's current zoom level is lower than the new minimum, the map will zoom to the new minimum. It is not always possible to zoom out and reach the set `minZoom`. Other factors such as map height may restrict zooming. For example, if the map is 512px tall it will not be possible to zoom below zoom 0 no matter what the `minZoom` is set to. A [ErrorEvent](ErrorEvent.md) event will be fired if minZoom is out of bounds. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `minZoom?` | `number` | The minimum zoom level to set (-2 - 24). If `null` or `undefined` is provided, the function removes the current minimum zoom (i.e. sets it to -2). | #### Returns `Map` #### Example ```ts map.setMinZoom(12.25); ``` *** ### setPadding() > **setPadding**(`padding`: [`PaddingOptions`](../type-aliases/PaddingOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:640 Sets the padding in pixels around the viewport. Equivalent to `jumpTo({padding: padding})`. Triggers the following events: `movestart` and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `padding` | [`PaddingOptions`](../type-aliases/PaddingOptions.md) | The desired padding. | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example Sets a left padding of 300px, and a top padding of 50px ```ts map.setPadding({ left: 300, top: 50 }); ``` #### Inherited from `Camera.setPadding` *** ### setPaintProperty() > **setPaintProperty**(`layerId`: `string`, `name`: `string`, `value`: `any`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `this` Defined in: src/ui/map.ts:2741 Sets the value of a paint property in the specified style layer. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | The ID of the layer to set the paint property in. | | `name` | `string` | The name of the paint property to set. | | `value` | `any` | The value of the paint property to set. Must be of a type appropriate for the property, as defined in the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/). Pass `null` to unset the existing value. | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `this` #### Example ```ts map.setPaintProperty('my-layer', 'fill-color', '#faafee'); ``` #### See - [Change a layer's color with buttons](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/color-switcher/) - [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### setPitch() > **setPitch**(`pitch`: `number`, `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:723 Sets the map's pitch (tilt). Equivalent to `jumpTo({pitch: pitch})`. Triggers the following events: `movestart`, `moveend`, `pitchstart`, and `pitchend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `pitch` | `number` | The pitch to set, measured in degrees away from the plane of the screen (0-60). | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.setPitch` *** ### setPixelRatio() > **setPixelRatio**(`pixelRatio`: `number`): `void` Defined in: src/ui/map.ts:989 Sets the map's pixel ratio. This allows to override `devicePixelRatio`. After this call, the canvas' `width` attribute will be `container.clientWidth * pixelRatio` and its height attribute will be `container.clientHeight * pixelRatio`. Set this to null to disable `devicePixelRatio` override. Note that the pixel ratio actually applied may be lower to respect maxCanvasSize. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `pixelRatio` | `number` | The pixel ratio. | #### Returns `void` *** ### setProjection() > **setProjection**(`projection`: [`ProjectionSpecification`](https://mapmetrics.org/mapmetrics-style-spec/projection/)): `Map` Defined in: src/ui/map.ts:3609 Sets the [ProjectionSpecification](https://mapmetrics.org/mapmetrics-style-spec/projection/). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `projection` | [`ProjectionSpecification`](https://mapmetrics.org/mapmetrics-style-spec/projection/) | the projection specification to set | #### Returns `Map` *** ### setRenderWorldCopies() > **setRenderWorldCopies**(`renderWorldCopies?`: `boolean`): `Map` Defined in: src/ui/map.ts:1235 Sets the state of `renderWorldCopies`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `renderWorldCopies?` | `boolean` | If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire container, there will be blank space beyond 180 and -180 degrees longitude. - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the map and the other on the left edge of the map) at every zoom level. `undefined` is treated as `true`, `null` is treated as `false`. | #### Returns `Map` #### Example ```ts map.setRenderWorldCopies(true); ``` #### See [Render world copies](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/render-world-copies/) *** ### setRoll() > **setRoll**(`roll`: `number`, `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:743 Sets the map's roll angle. Equivalent to `jumpTo({roll: roll})`. Triggers the following events: `movestart`, `moveend`, `rollstart`, and `rollend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `roll` | `number` | The roll to set, measured in degrees about the camera boresight | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.setRoll` *** ### setSky() > **setSky**(`sky`: `SkySpecification`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `Map` Defined in: src/ui/map.ts:2913 Sets the value of style's sky properties. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sky` | `SkySpecification` | Sky properties to set. Must conform to the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/sky/). | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `Map` #### Example ```ts map.setSky({'atmosphere-blend': 1.0}); ``` *** ### setSourceTileLodParams() > **setSourceTileLodParams**(`maxZoomLevelsOnScreen`: `number`, `tileCountMaxMinRatio`: `number`, `sourceId?`: `string`): `this` Defined in: src/ui/map.ts:2227 Change the tile Level of Detail behavior of the specified source. These parameters have no effect when pitch == 0, and the largest effect when the horizon is visible on screen. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `maxZoomLevelsOnScreen` | `number` | The maximum number of distinct zoom levels allowed on screen at a time. There will generally be fewer zoom levels on the screen, the maximum can only be reached when the horizon is at the top of the screen. Increasing the maximum number of zoom levels causes the zoom level to decay faster toward the horizon. | | `tileCountMaxMinRatio` | `number` | The ratio of the maximum number of tiles loaded (at high pitch) to the minimum number of tiles loaded. Increasing this ratio allows more tiles to be loaded at high pitch angles. If the ratio would otherwise be exceeded, the zoom level is reduced uniformly to keep the number of tiles within the limit. | | `sourceId?` | `string` | The ID of the source to set tile LOD parameters for. All sources will be updated if unspecified. If `sourceId` is specified but a corresponding source does not exist, an error is thrown. | #### Returns `this` #### Example ```ts map.setSourceTileLodParams(4.0, 3.0, 'terrain'); ``` #### See [Modify Level of Detail behavior](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/lod-control/) *** ### setSprite() > **setSprite**(`spriteUrl`: `string`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `Map` Defined in: src/ui/map.ts:2866 Sets the value of the style's sprite property. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `spriteUrl` | `string` | Sprite URL to set. | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Options object. | #### Returns `Map` #### Example ```ts map.setSprite('YOUR_SPRITE_URL'); ``` *** ### setStyle() > **setStyle**(`style`: `string` \| `StyleSpecification`, `options?`: [`StyleSwapOptions`](../type-aliases/StyleSwapOptions.md) & [`StyleOptions`](../type-aliases/StyleOptions.md)): `this` Defined in: src/ui/map.ts:1851 Updates the map's mapmetrics style object with a new value. If a style is already set when this is used and options.diff is set to true, the map renderer will attempt to compare the given style against the map's current state and perform only the changes necessary to make the map style match the desired state. Changes in sprites (images used for icons and patterns) and glyphs (fonts for label text) **cannot** be diffed. If the sprites or fonts used in the current style and the given style are different in any way, the map renderer will force a full update, removing the current style and building the given one from scratch. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `style` | `string` \| `StyleSpecification` | A JSON object conforming to the schema described in the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/), or a URL to such JSON. | | `options?` | [`StyleSwapOptions`](../type-aliases/StyleSwapOptions.md) & [`StyleOptions`](../type-aliases/StyleOptions.md) | The options object. | #### Returns `this` #### Example ```ts map.setStyle("https://demotiles.mapmetrics.org/style.json"); map.setStyle('https://demotiles.mapmetrics.org/style.json', { transformStyle: (previousStyle, nextStyle) => ({ ...nextStyle, sources: { ...nextStyle.sources, // copy a source from previous style 'osm': previousStyle.sources.osm }, layers: [ // background layer nextStyle.layers[0], // copy a layer from previous style previousStyle.layers[0], // other layers from the next style ...nextStyle.layers.slice(1).map(layer => { // hide the layers we don't need from demotiles style if (layer.id.startsWith('geolines')) { layer.layout = {...layer.layout || {}, visibility: 'none'}; // filter out US polygons } else if (layer.id.startsWith('coastline') || layer.id.startsWith('countries')) { layer.filter = ['!=', ['get', 'ADM0_A3'], 'USA']; } return layer; }) ] }) }); ``` *** ### setTerrain() > **setTerrain**(`options`: `TerrainSpecification`): `this` Defined in: src/ui/map.ts:2071 Loads a 3D terrain mesh, based on a "raster-dem" source. Triggers the `terrain` event. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | `TerrainSpecification` | Options object. | #### Returns `this` #### Example ```ts map.setTerrain({ source: 'terrain' }); ``` *** ### setTransformRequest() > **setTransformRequest**(`transformRequest`: [`RequestTransformFunction`](../type-aliases/RequestTransformFunction.md)): `this` Defined in: src/ui/map.ts:1878 Updates the requestManager's transform request with a new function #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `transformRequest` | [`RequestTransformFunction`](../type-aliases/RequestTransformFunction.md) | A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests. Expected to return an object with a `url` property and optionally `headers` and `credentials` properties | #### Returns `this` #### Example ```ts map.setTransformRequest((url: string, resourceType: string) => {}); ``` *** ### setVerticalFieldOfView() > **setVerticalFieldOfView**(`fov`: `number`, `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:578 Sets the map's vertical field of view, in degrees. Triggers the following events: `movestart`, `move`, and `moveend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `fov` | `number` | The vertical field of view to set, in degrees (0-180). | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Default Value ```ts 36.87 ``` #### Example Change vertical field of view to 30 degrees ```ts map.setVerticalFieldOfView(30); ``` #### Inherited from `Camera.setVerticalFieldOfView` *** ### setZoom() > **setZoom**(`zoom`: `number`, `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:486 Sets the map's zoom level. Equivalent to `jumpTo({zoom: zoom})`. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `zoom` | `number` | The zoom level to set (0-20). | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example Zoom to the zoom level 5 without an animated transition ```ts map.setZoom(5); ``` #### Inherited from `Camera.setZoom` *** ### snapToNorth() > **snapToNorth**(`options?`: [`AnimationOptions`](../type-aliases/AnimationOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:701 Snaps the map so that north is up (0° bearing), if the current bearing is close enough to it (i.e. within the `bearingSnap` threshold). Triggers the following events: `movestart`, `moveend`, and `rotate`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`AnimationOptions`](../type-aliases/AnimationOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Inherited from `Camera.snapToNorth` *** ### stop() > **stop**(): `this` Defined in: src/ui/camera.ts:1555 Stops any animated transition underway. #### Returns `this` #### Inherited from `Camera.stop` *** ### triggerRepaint() > **triggerRepaint**(): `void` Defined in: src/ui/map.ts:3471 Trigger the rendering of a single frame. Use this method with custom layers to repaint the map when the layer changes. Calling this multiple times before the next frame is rendered will still result in only a single frame being rendered. #### Returns `void` #### Example ```ts map.triggerRepaint(); ``` #### See - [Add a 3D model](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-3d-model/) - [Add an animated icon to the map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image-animated/) *** ### unproject() > **unproject**(`point`: [`PointLike`](../type-aliases/PointLike.md)): [`LngLat`](LngLat.md) Defined in: src/ui/map.ts:1270 Returns a [LngLat](LngLat.md) representing geographical coordinates that correspond to the specified pixel coordinates. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `point` | [`PointLike`](../type-aliases/PointLike.md) | The pixel coordinates to unproject. | #### Returns [`LngLat`](LngLat.md) The [LngLat](LngLat.md) corresponding to `point`. #### Example ```ts map.on('click', (e) => { // When the map is clicked, get the geographic coordinate. let coordinate = map.unproject(e.point); }); ``` *** ### updateImage() > **updateImage**(`id`: `string`, `image`: `ImageBitmap` \| `HTMLImageElement` \| `ImageData` \| [`StyleImageInterface`](../interfaces/StyleImageInterface.md) \| \{ `data`: `Uint8Array`\<`ArrayBufferLike`\> \| `Uint8ClampedArray`\<`ArrayBufferLike`\>; `height`: `number`; `width`: `number`; \}): `this` Defined in: src/ui/map.ts:2369 Update an existing image in a style. This image can be displayed on the map like any other icon in the style's sprite using the image's ID with [`icon-image`](https://mapmetrics.org/mapmetrics-style-spec/layers/#layout-symbol-icon-image), [`background-pattern`](https://mapmetrics.org/mapmetrics-style-spec/layers/#paint-background-background-pattern), [`fill-pattern`](https://mapmetrics.org/mapmetrics-style-spec/layers/#paint-fill-fill-pattern), or [`line-pattern`](https://mapmetrics.org/mapmetrics-style-spec/layers/#paint-line-line-pattern). An [ErrorEvent](ErrorEvent.md) will be fired if the image parameter is invalid. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The ID of the image. | | `image` | `ImageBitmap` \| `HTMLImageElement` \| `ImageData` \| [`StyleImageInterface`](../interfaces/StyleImageInterface.md) \| \{ `data`: `Uint8Array`\<`ArrayBufferLike`\> \| `Uint8ClampedArray`\<`ArrayBufferLike`\>; `height`: `number`; `width`: `number`; \} | The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data` properties with the same format as `ImageData`. | #### Returns `this` #### Example ```ts // If an image with the ID 'cat' already exists in the style's sprite, // replace that image with a new image, 'other-cat-icon.png'. if (map.hasImage('cat')) map.updateImage('cat', './other-cat-icon.png'); ``` *** ### zoomIn() > **zoomIn**(`options?`: [`AnimationOptions`](../type-aliases/AnimationOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:529 Increases the map's zoom level by 1. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`AnimationOptions`](../type-aliases/AnimationOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example Zoom the map in one level with a custom animation duration ```ts map.zoomIn({duration: 1000}); ``` #### Inherited from `Camera.zoomIn` *** ### zoomOut() > **zoomOut**(`options?`: [`AnimationOptions`](../type-aliases/AnimationOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:547 Decreases the map's zoom level by 1. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`AnimationOptions`](../type-aliases/AnimationOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example Zoom the map out one level with a custom animation offset ```ts map.zoomOut({offset: [80, 60]}); ``` #### Inherited from `Camera.zoomOut` *** ### zoomTo() > **zoomTo**(`zoom`: `number`, `options?`: [`EaseToOptions`](../type-aliases/EaseToOptions.md), `eventData?`: `any`): `this` Defined in: src/ui/camera.ts:510 Zooms the map to the specified zoom level, with an animated transition. Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `zoom` | `number` | The zoom level to transition to. | | `options?` | [`EaseToOptions`](../type-aliases/EaseToOptions.md) | Options object | | `eventData?` | `any` | Additional properties to be added to event objects of events triggered by this method. | #### Returns `this` #### Example ```ts // Zoom to the zoom level 5 without an animated transition map.zoomTo(5); // Zoom to the zoom level 8 with an animated transition map.zoomTo(8, { duration: 2000, offset: [100, 50] }); ``` #### Inherited from `Camera.zoomTo` ## Properties ### boxZoom > **boxZoom**: [`BoxZoomHandler`](BoxZoomHandler.md) Defined in: src/ui/map.ts:543 The map's [BoxZoomHandler](BoxZoomHandler.md), which implements zooming using a drag gesture with the Shift key pressed. Find more details and examples using `boxZoom` in the [BoxZoomHandler](BoxZoomHandler.md) section. *** ### cancelPendingTileRequestsWhileZooming > **cancelPendingTileRequestsWhileZooming**: `boolean` Defined in: src/ui/map.ts:594 The map's property which determines whether to cancel, or retain, tiles from the current viewport which are still loading but which belong to a farther (smaller) zoom level than the current one. * If `true`, when zooming in, tiles which didn't manage to load for previous zoom levels will become canceled. This might save some computing resources for slower devices, but the map details might appear more abruptly at the end of the zoom. * If `false`, when zooming in, the previous zoom level(s) tiles will progressively appear, giving a smoother map details experience. However, more tiles will be rendered in a short period of time. #### Default Value ```ts true ``` *** ### cooperativeGestures > **cooperativeGestures**: [`CooperativeGesturesHandler`](CooperativeGesturesHandler.md) Defined in: src/ui/map.ts:586 The map's [CooperativeGesturesHandler](CooperativeGesturesHandler.md), which allows the user to see cooperative gesture info when user tries to zoom in/out. Find more details and examples using `cooperativeGestures` in the [CooperativeGesturesHandler](CooperativeGesturesHandler.md) section. *** ### doubleClickZoom > **doubleClickZoom**: [`DoubleClickZoomHandler`](DoubleClickZoomHandler.md) Defined in: src/ui/map.ts:568 The map's [DoubleClickZoomHandler](DoubleClickZoomHandler.md), which allows the user to zoom by double clicking. Find more details and examples using `doubleClickZoom` in the [DoubleClickZoomHandler](DoubleClickZoomHandler.md) section. *** ### dragPan > **dragPan**: [`DragPanHandler`](DragPanHandler.md) Defined in: src/ui/map.ts:556 The map's [DragPanHandler](DragPanHandler.md), which implements dragging the map with a mouse or touch gesture. Find more details and examples using `dragPan` in the [DragPanHandler](DragPanHandler.md) section. *** ### dragRotate > **dragRotate**: [`DragRotateHandler`](DragRotateHandler.md) Defined in: src/ui/map.ts:550 The map's [DragRotateHandler](DragRotateHandler.md), which implements rotating the map while dragging with the right mouse button or with the Control key pressed. Find more details and examples using `dragRotate` in the [DragRotateHandler](DragRotateHandler.md) section. *** ### keyboard > **keyboard**: [`KeyboardHandler`](KeyboardHandler.md) Defined in: src/ui/map.ts:562 The map's [KeyboardHandler](KeyboardHandler.md), which allows the user to zoom, rotate, and pan the map using keyboard shortcuts. Find more details and examples using `keyboard` in the [KeyboardHandler](KeyboardHandler.md) section. *** ### scrollZoom > **scrollZoom**: [`ScrollZoomHandler`](ScrollZoomHandler.md) Defined in: src/ui/map.ts:537 The map's [ScrollZoomHandler](ScrollZoomHandler.md), which implements zooming in and out with a scroll wheel or trackpad. Find more details and examples using `scrollZoom` in the [ScrollZoomHandler](ScrollZoomHandler.md) section. *** ### touchPitch > **touchPitch**: [`TwoFingersTouchPitchHandler`](TwoFingersTouchPitchHandler.md) Defined in: src/ui/map.ts:580 The map's [TwoFingersTouchPitchHandler](TwoFingersTouchPitchHandler.md), which allows the user to pitch the map with touch gestures. Find more details and examples using `touchPitch` in the [TwoFingersTouchPitchHandler](TwoFingersTouchPitchHandler.md) section. *** ### touchZoomRotate > **touchZoomRotate**: [`TwoFingersTouchZoomRotateHandler`](TwoFingersTouchZoomRotateHandler.md) Defined in: src/ui/map.ts:574 The map's [TwoFingersTouchZoomRotateHandler](TwoFingersTouchZoomRotateHandler.md), which allows the user to zoom or rotate the map with touch gestures. Find more details and examples using `touchZoomRotate` in the [TwoFingersTouchZoomRotateHandler](TwoFingersTouchZoomRotateHandler.md) section. *** ### transformCameraUpdate > **transformCameraUpdate**: [`CameraUpdateTransformFunction`](../type-aliases/CameraUpdateTransformFunction.md) Defined in: src/ui/camera.ts:312 A callback used to defer camera updates or apply arbitrary constraints. If specified, this Camera instance can be used as a stateless component in React etc. #### Inherited from `Camera.transformCameraUpdate` --- # MapMouseEvent https://docs.mapatlas.xyz/overview/API/classes/MapMouseEvent # MapMouseEvent Defined in: src/ui/events.ts:488 `MapMouseEvent` is the event type for mouse-related map events. ## Example ```ts // The `click` event is an example of a `MapMouseEvent`. // Set up an event listener on the map. map.on('click', (e) => { // The event object (e) contains information like the // coordinates of the point on the map that was clicked. console.log('A click event has occurred at ' + e.lngLat); }); ``` ## Extends - [`Event`](Event.md) ## Implements - [`MapLibreEvent`](../type-aliases/MapLibreEvent.md)\<`MouseEvent`\> ## Accessors ### defaultPrevented #### Get Signature > **get** **defaultPrevented**(): `boolean` Defined in: src/ui/events.ts:532 `true` if `preventDefault` has been called. ##### Returns `boolean` ## Methods ### preventDefault() > **preventDefault**(): `void` Defined in: src/ui/events.ts:525 Prevents subsequent default processing of the event by the map. Calling this method will prevent the following default map behaviors: * On `mousedown` events, the behavior of [DragPanHandler](DragPanHandler.md) * On `mousedown` events, the behavior of [DragRotateHandler](DragRotateHandler.md) * On `mousedown` events, the behavior of [BoxZoomHandler](BoxZoomHandler.md) * On `dblclick` events, the behavior of [DoubleClickZoomHandler](DoubleClickZoomHandler.md) #### Returns `void` ## Properties ### lngLat > **lngLat**: [`LngLat`](LngLat.md) Defined in: src/ui/events.ts:512 The geographic location on the map of the mouse cursor. *** ### originalEvent > **originalEvent**: `MouseEvent` Defined in: src/ui/events.ts:502 The DOM event which caused the map event. #### Implementation of `mapmetricsEvent.originalEvent` *** ### point > **point**: `Point` Defined in: src/ui/events.ts:507 The pixel coordinates of the mouse cursor, relative to the map and measured from the top left corner. *** ### target > **target**: [`Map`](Map.md) Defined in: src/ui/events.ts:497 The `Map` object that fired the event. #### Implementation of `mapmetricsEvent.target` *** ### type > **type**: `"click"` \| `"contextmenu"` \| `"dblclick"` \| `"mousedown"` \| `"mouseenter"` \| `"mouseleave"` \| `"mousemove"` \| `"mouseout"` \| `"mouseover"` \| `"mouseup"` Defined in: src/ui/events.ts:492 The event type #### Implementation of `mapmetricsEvent.type` #### Overrides `Event.type` --- # MapTouchEvent https://docs.mapatlas.xyz/overview/API/classes/MapTouchEvent # MapTouchEvent Defined in: src/ui/events.ts:553 `MapTouchEvent` is the event type for touch-related map events. ## Extends - [`Event`](Event.md) ## Implements - [`MapLibreEvent`](../type-aliases/MapLibreEvent.md)\<`TouchEvent`\> ## Accessors ### defaultPrevented #### Get Signature > **get** **defaultPrevented**(): `boolean` Defined in: src/ui/events.ts:608 `true` if `preventDefault` has been called. ##### Returns `boolean` ## Methods ### preventDefault() > **preventDefault**(): `void` Defined in: src/ui/events.ts:601 Prevents subsequent default processing of the event by the map. Calling this method will prevent the following default map behaviors: * On `touchstart` events, the behavior of [DragPanHandler](DragPanHandler.md) * On `touchstart` events, the behavior of [TwoFingersTouchZoomRotateHandler](TwoFingersTouchZoomRotateHandler.md) #### Returns `void` ## Properties ### lngLat > **lngLat**: [`LngLat`](LngLat.md) Defined in: src/ui/events.ts:572 The geographic location on the map of the center of the touch event points. *** ### lngLats > **lngLats**: [`LngLat`](LngLat.md)[] Defined in: src/ui/events.ts:590 The geographical locations on the map corresponding to a [touch event's `touches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches) property. *** ### originalEvent > **originalEvent**: `TouchEvent` Defined in: src/ui/events.ts:567 The DOM event which caused the map event. #### Implementation of `mapmetricsEvent.originalEvent` *** ### point > **point**: `Point` Defined in: src/ui/events.ts:578 The pixel coordinates of the center of the touch event points, relative to the map and measured from the top left corner. *** ### points > **points**: `Point`[] Defined in: src/ui/events.ts:584 The array of pixel coordinates corresponding to a [touch event's `touches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches) property. *** ### target > **target**: [`Map`](Map.md) Defined in: src/ui/events.ts:562 The `Map` object that fired the event. #### Implementation of `mapmetricsEvent.target` *** ### type > **type**: `"touchcancel"` \| `"touchend"` \| `"touchmove"` \| `"touchstart"` Defined in: src/ui/events.ts:557 The event type. #### Implementation of `mapmetricsEvent.type` #### Overrides `Event.type` --- # MapWheelEvent https://docs.mapatlas.xyz/overview/API/classes/MapWheelEvent # MapWheelEvent Defined in: src/ui/events.ts:632 `MapWheelEvent` is the event type for the `wheel` map event. ## Extends - [`Event`](Event.md) ## Accessors ### defaultPrevented #### Get Signature > **get** **defaultPrevented**(): `boolean` Defined in: src/ui/events.ts:660 `true` if `preventDefault` has been called. ##### Returns `boolean` ## Constructors ### Constructor > **new MapWheelEvent**(`type`: `string`, `map`: [`Map`](Map.md), `originalEvent`: `WheelEvent`): `MapWheelEvent` Defined in: src/ui/events.ts:667 #### Parameters | Parameter | Type | | ------ | ------ | | `type` | `string` | | `map` | [`Map`](Map.md) | | `originalEvent` | `WheelEvent` | #### Returns `MapWheelEvent` #### Overrides `Event.constructor` ## Methods ### preventDefault() > **preventDefault**(): `void` Defined in: src/ui/events.ts:653 Prevents subsequent default processing of the event by the map. Calling this method will prevent the behavior of [ScrollZoomHandler](ScrollZoomHandler.md). #### Returns `void` ## Properties ### originalEvent > **originalEvent**: `WheelEvent` Defined in: src/ui/events.ts:646 The DOM event which caused the map event. *** ### target > **target**: [`Map`](Map.md) Defined in: src/ui/events.ts:641 The `Map` object that fired the event. *** ### type > **type**: `"wheel"` Defined in: src/ui/events.ts:636 The event type. #### Overrides `Event.type` --- # Marker https://docs.mapatlas.xyz/overview/API/classes/Marker # Marker Defined in: src/ui/marker.ts:127 Creates a marker component ## Examples ```ts let marker = new Marker() .setLngLat([30.5, 50.5]) .addTo(map); ``` Set options ```ts let marker = new Marker({ color: "#FFFFFF", draggable: true }).setLngLat([30.5, 50.5]) .addTo(map); ``` ## See - [Add custom icons with Markers](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/custom-marker-icons/) - [Create a draggable Marker](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-marker/) ## Events **Event** `dragstart` of type [Event](Event.md) will be fired when dragging starts. **Event** `drag` of type [Event](Event.md) will be fired while dragging. **Event** `dragend` of type [Event](Event.md) will be fired when the marker is finished being dragged. ## Extends - [`Evented`](Evented.md) ## Constructors ### Constructor > **new Marker**(`options?`: [`MarkerOptions`](../type-aliases/MarkerOptions.md)): `Marker` Defined in: src/ui/marker.ts:157 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`MarkerOptions`](../type-aliases/MarkerOptions.md) | the options | #### Returns `Marker` #### Overrides `Evented.constructor` ## Methods ### addClassName() > **addClassName**(`className`: `string`): `void` Defined in: src/ui/marker.ts:668 Adds a CSS class to the marker element. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `className` | `string` | on-empty string with CSS class name to add to marker element | #### Returns `void` #### Example ``` let marker = new Marker() marker.addClassName('some-class') ``` *** ### addTo() > **addTo**(`map`: [`Map`](Map.md)): `this` Defined in: src/ui/marker.ts:315 Attaches the `Marker` to a `Map` object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The mapmetrics GL JS map to add the marker to. | #### Returns `this` #### Example ```ts let marker = new Marker() .setLngLat([30.5, 50.5]) .addTo(map); // add the marker to the map ``` *** ### getElement() > **getElement**(): `HTMLElement` Defined in: src/ui/marker.ts:418 Returns the `Marker`'s HTML element. #### Returns `HTMLElement` element *** ### getLngLat() > **getLngLat**(): [`LngLat`](LngLat.md) Defined in: src/ui/marker.ts:389 Get the marker's geographical location. The longitude of the result may differ by a multiple of 360 degrees from the longitude previously set by `setLngLat` because `Marker` wraps the anchor longitude across copies of the world to keep the marker on screen. #### Returns [`LngLat`](LngLat.md) A [LngLat](LngLat.md) describing the marker's location. #### Example ```ts // Store the marker's longitude and latitude coordinates in a variable let lngLat = marker.getLngLat(); // Print the marker's longitude and latitude values in the console console.log('Longitude: ' + lngLat.lng + ', Latitude: ' + lngLat.lat ) ``` #### See [Create a draggable Marker](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-marker/) *** ### getOffset() > **getOffset**(): `Point` Defined in: src/ui/marker.ts:643 Get the marker's offset. #### Returns `Point` The marker's screen coordinates in pixels. *** ### getPitchAlignment() > **getPitchAlignment**(): [`Alignment`](../type-aliases/Alignment.md) Defined in: src/ui/marker.ts:846 Returns the current `pitchAlignment` property of the marker. #### Returns [`Alignment`](../type-aliases/Alignment.md) The current pitch alignment of the marker in degrees. *** ### getPopup() > **getPopup**(): [`Popup`](Popup.md) Defined in: src/ui/marker.ts:524 Returns the [Popup](Popup.md) instance that is bound to the Marker. #### Returns [`Popup`](Popup.md) popup #### Example ```ts let marker = new Marker() .setLngLat([0, 0]) .setPopup(new Popup().setHTML("

Hello World!

")) .addTo(map); console.log(marker.getPopup()); // return the popup instance ``` *** ### getRotation() > **getRotation**(): `number` Defined in: src/ui/marker.ts:810 Returns the current rotation angle of the marker (in degrees). #### Returns `number` The current rotation angle of the marker. *** ### getRotationAlignment() > **getRotationAlignment**(): [`Alignment`](../type-aliases/Alignment.md) Defined in: src/ui/marker.ts:828 Returns the current `rotationAlignment` property of the marker. #### Returns [`Alignment`](../type-aliases/Alignment.md) The current rotational alignment of the marker. *** ### isDraggable() > **isDraggable**(): `boolean` Defined in: src/ui/marker.ts:792 Returns true if the marker can be dragged #### Returns `boolean` True if the marker is draggable. *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `Marker` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `Marker` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `Marker` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `Marker` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### remove() > **remove**(): `this` Defined in: src/ui/marker.ts:348 Removes the marker from a map #### Returns `this` #### Example ```ts let marker = new Marker().addTo(map); marker.remove(); ``` *** ### removeClassName() > **removeClassName**(`className`: `string`): `void` Defined in: src/ui/marker.ts:683 Removes a CSS class from the marker element. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `className` | `string` | Non-empty string with CSS class name to remove from marker element | #### Returns `void` #### Example ```ts let marker = new Marker() marker.removeClassName('some-class') ``` *** ### setDraggable() > **setDraggable**(`shouldBeDraggable?`: `boolean`): `this` Defined in: src/ui/marker.ts:770 Sets the `draggable` property and functionality of the marker #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `shouldBeDraggable?` | `boolean` | Turns drag functionality on/off | #### Returns `this` *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `Marker` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `Marker` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### setLngLat() > **setLngLat**(`lnglat`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `this` Defined in: src/ui/marker.ts:406 Set the marker's geographical position and move it. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lnglat` | [`LngLatLike`](../type-aliases/LngLatLike.md) | A [LngLat](LngLat.md) describing where the marker should be located. | #### Returns `this` #### Example Create a new marker, set the longitude and latitude, and add it to the map ```ts new Marker() .setLngLat([-65.017, -16.457]) .addTo(map); ``` #### See - [Add custom icons with Markers](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/custom-marker-icons/) - [Create a draggable Marker](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-marker/) *** ### setOffset() > **setOffset**(`offset`: [`PointLike`](../type-aliases/PointLike.md)): `this` Defined in: src/ui/marker.ts:651 Sets the offset of the marker #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `offset` | [`PointLike`](../type-aliases/PointLike.md) | The offset in pixels as a [PointLike](../type-aliases/PointLike.md) object to apply relative to the element's center. Negatives indicate left and up. | #### Returns `this` *** ### setOpacity() > **setOpacity**(`opacity?`: `string`, `opacityWhenCovered?`: `string`): `this` Defined in: src/ui/marker.ts:856 Sets the `opacity` and `opacityWhenCovered` properties of the marker. When called without arguments, resets opacity and opacityWhenCovered to defaults #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `opacity?` | `string` | Sets the `opacity` property of the marker. | | `opacityWhenCovered?` | `string` | Sets the `opacityWhenCovered` property of the marker. | #### Returns `this` *** ### setPitchAlignment() > **setPitchAlignment**(`alignment?`: [`Alignment`](../type-aliases/Alignment.md)): `this` Defined in: src/ui/marker.ts:836 Sets the `pitchAlignment` property of the marker. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `alignment?` | [`Alignment`](../type-aliases/Alignment.md) | Sets the `pitchAlignment` property of the marker. If alignment is 'auto', it will automatically match `rotationAlignment`. | #### Returns `this` *** ### setPopup() > **setPopup**(`popup?`: [`Popup`](Popup.md)): `this` Defined in: src/ui/marker.ts:435 Binds a [Popup](Popup.md) to the Marker. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `popup?` | [`Popup`](Popup.md) | An instance of the [Popup](Popup.md) class. If undefined or null, any popup set on this Marker instance is unset. | #### Returns `this` #### Example ```ts let marker = new Marker() .setLngLat([0, 0]) .setPopup(new Popup().setHTML("

Hello World!

")) // add popup .addTo(map); ``` #### See [Attach a popup to a marker instance](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/set-popup/) *** ### setRotation() > **setRotation**(`rotation?`: `number`): `this` Defined in: src/ui/marker.ts:800 Sets the `rotation` property of the marker. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `rotation?` | `number` | The rotation angle of the marker (clockwise, in degrees), relative to its respective [Marker#setRotationAlignment](#setrotationalignment) setting. | #### Returns `this` *** ### setRotationAlignment() > **setRotationAlignment**(`alignment?`: [`Alignment`](../type-aliases/Alignment.md)): `this` Defined in: src/ui/marker.ts:818 Sets the `rotationAlignment` property of the marker. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `alignment?` | [`Alignment`](../type-aliases/Alignment.md) | Sets the `rotationAlignment` property of the marker. defaults to 'auto' | #### Returns `this` *** ### setSubpixelPositioning() > **setSubpixelPositioning**(`value`: `boolean`): `Marker` Defined in: src/ui/marker.ts:485 Set the option to allow subpixel positioning of the marker by passing a boolean #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `value` | `boolean` | when set to `true`, subpixel positioning is enabled for the marker. | #### Returns `Marker` #### Example ```ts let marker = new Marker() marker.setSubpixelPositioning(true); ``` *** ### toggleClassName() > **toggleClassName**(`className`: `string`): `boolean` Defined in: src/ui/marker.ts:700 Add or remove the given CSS class on the marker element, depending on whether the element currently has that class. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `className` | `string` | Non-empty string with CSS class name to add/remove | #### Returns `boolean` if the class was removed return false, if class was added, then return true #### Example ```ts let marker = new Marker() marker.toggleClassName('toggleClass') ``` *** ### togglePopup() > **togglePopup**(): `this` Defined in: src/ui/marker.ts:540 Opens or closes the [Popup](Popup.md) instance that is bound to the Marker, depending on the current state of the [Popup](Popup.md). #### Returns `this` #### Example ```ts let marker = new Marker() .setLngLat([0, 0]) .setPopup(new Popup().setHTML("

Hello World!

")) .addTo(map); marker.togglePopup(); // toggle popup open or closed ``` --- # MercatorCoordinate https://docs.mapatlas.xyz/overview/API/classes/MercatorCoordinate # MercatorCoordinate Defined in: src/geo/mercator\_coordinate.ts:78 A `MercatorCoordinate` object represents a projected three dimensional position. `MercatorCoordinate` uses the web mercator projection ([EPSG:3857](https://epsg.io/3857)) with slightly different units: - the size of 1 unit is the width of the projected world instead of the "mercator meter" - the origin of the coordinate space is at the north-west corner instead of the middle For example, `MercatorCoordinate(0, 0, 0)` is the north-west corner of the mercator world and `MercatorCoordinate(1, 1, 0)` is the south-east corner. If you are familiar with [vector tiles](https://github.com/mapbox/vector-tile-spec) it may be helpful to think of the coordinate space as the `0/0/0` tile with an extent of `1`. The `z` dimension of `MercatorCoordinate` is conformal. A cube in the mercator coordinate space would be rendered as a cube. ## Example ```ts let nullIsland = new MercatorCoordinate(0.5, 0.5, 0); ``` ## See [Add a custom style layer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/custom-style-layer/) ## Implements - `IMercatorCoordinate` ## Constructors ### Constructor > **new MercatorCoordinate**(`x`: `number`, `y`: `number`, `z`: `number`): `MercatorCoordinate` Defined in: src/geo/mercator\_coordinate.ts:88 #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `x` | `number` | `undefined` | The x component of the position. | | `y` | `number` | `undefined` | The y component of the position. | | `z` | `number` | `0` | The z component of the position. | #### Returns `MercatorCoordinate` ## Methods ### meterInMercatorCoordinateUnits() > **meterInMercatorCoordinateUnits**(): `number` Defined in: src/geo/mercator\_coordinate.ts:153 Returns the distance of 1 meter in `MercatorCoordinate` units at this latitude. For coordinates in real world units using meters, this naturally provides the scale to transform into `MercatorCoordinate`s. #### Returns `number` Distance of 1 meter in `MercatorCoordinate` units. #### Implementation of `IMercatorCoordinate.meterInMercatorCoordinateUnits` *** ### toAltitude() > **toAltitude**(): `number` Defined in: src/geo/mercator\_coordinate.ts:141 Returns the altitude in meters of the coordinate. #### Returns `number` The altitude in meters. #### Example ```ts let coord = new MercatorCoordinate(0, 0, 0.02); coord.toAltitude(); // 6914.281956295339 ``` #### Implementation of `IMercatorCoordinate.toAltitude` *** ### toLngLat() > **toLngLat**(): [`LngLat`](LngLat.md) Defined in: src/geo/mercator\_coordinate.ts:125 Returns the `LngLat` for the coordinate. #### Returns [`LngLat`](LngLat.md) The `LngLat` object. #### Example ```ts let coord = new MercatorCoordinate(0.5, 0.5, 0); let lngLat = coord.toLngLat(); // LngLat(0, 0) ``` #### Implementation of `IMercatorCoordinate.toLngLat` *** ### fromLngLat() > `static` **fromLngLat**(`lngLatLike`: [`LngLatLike`](../type-aliases/LngLatLike.md), `altitude`: `number`): `MercatorCoordinate` Defined in: src/geo/mercator\_coordinate.ts:106 Project a `LngLat` to a `MercatorCoordinate`. #### Parameters | Parameter | Type | Default value | Description | | ------ | ------ | ------ | ------ | | `lngLatLike` | [`LngLatLike`](../type-aliases/LngLatLike.md) | `undefined` | The location to project. | | `altitude` | `number` | `0` | The altitude in meters of the position. | #### Returns `MercatorCoordinate` The projected mercator coordinate. #### Example ```ts let coord = MercatorCoordinate.fromLngLat({ lng: 0, lat: 0}, 0); coord; // MercatorCoordinate(0.5, 0.5, 0) ``` --- # NavigationControl https://docs.mapatlas.xyz/overview/API/classes/NavigationControl # NavigationControl Defined in: src/ui/control/navigation\_control.ts:52 A `NavigationControl` control contains zoom buttons and a compass. ## Example ```ts let nav = new NavigationControl(); map.addControl(nav, 'top-left'); ``` ## See [Display map navigation controls](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/navigation/) ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new NavigationControl**(`options?`: [`NavigationControlOptions`](../type-aliases/NavigationControlOptions.md)): `NavigationControl` Defined in: src/ui/control/navigation\_control.ts:65 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`NavigationControlOptions`](../type-aliases/NavigationControlOptions.md) | the control's options | #### Returns `NavigationControl` ## Methods ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/navigation\_control.ts:117 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/navigation\_control.ts:141 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) --- # OverscaledTileID https://docs.mapatlas.xyz/overview/API/classes/OverscaledTileID # OverscaledTileID Defined in: src/source/tile\_id.ts:87 An overscaled tile identifier ## Properties ### terrainRttPosMatrix32f > **terrainRttPosMatrix32f**: `mat4` = `null` Defined in: src/source/tile\_id.ts:98 This matrix is used during terrain's render-to-texture stage only. If the render-to-texture stage is active, this matrix will be present and should be used, otherwise this matrix will be null. The matrix should be float32 in order to avoid slow WebGL calls in Chrome. --- # Popup https://docs.mapatlas.xyz/overview/API/classes/Popup # Popup Defined in: src/ui/popup.ts:168 A popup component. ## Examples Create a popup ```ts let popup = new Popup(); // Set an event listener that will fire // any time the popup is opened popup.on('open', () => { console.log('popup was opened'); }); ``` Create a popup ```ts let popup = new Popup(); // Set an event listener that will fire // any time the popup is closed popup.on('close', () => { console.log('popup was closed'); }); ``` ```ts let markerHeight = 50, markerRadius = 10, linearOffset = 25; let popupOffsets = { 'top': [0, 0], 'top-left': [0,0], 'top-right': [0,0], 'bottom': [0, -markerHeight], 'bottom-left': [linearOffset, (markerHeight - markerRadius + linearOffset) * -1], 'bottom-right': [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1], 'left': [markerRadius, (markerHeight - markerRadius) * -1], 'right': [-markerRadius, (markerHeight - markerRadius) * -1] }; let popup = new Popup({offset: popupOffsets, className: 'my-class'}) .setLngLat(e.lngLat) .setHTML("

Hello World!

") .setMaxWidth("300px") .addTo(map); ``` ## See - [Display a popup](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup/) - [Display a popup on hover](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) - [Display a popup on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-click/) - [Attach a popup to a marker instance](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/set-popup/) ## Events **Event** `open` of type [Event](Event.md) will be fired when the popup is opened manually or programmatically. **Event** `close` of type [Event](Event.md) will be fired when the popup is closed manually or programmatically. ## Extends - [`Evented`](Evented.md) ## Constructors ### Constructor > **new Popup**(`options?`: [`PopupOptions`](../type-aliases/PopupOptions.md)): `Popup` Defined in: src/ui/popup.ts:183 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`PopupOptions`](../type-aliases/PopupOptions.md) | the options | #### Returns `Popup` #### Overrides `Evented.constructor` ## Methods ### \_updateOpacity() > **\_updateOpacity**(): `void` Defined in: src/ui/popup.ts:239 Add opacity to popup if in globe projection and location is behind view #### Returns `void` *** ### addClassName() > **addClassName**(`className`: `string`): `Popup` Defined in: src/ui/popup.ts:500 Adds a CSS class to the popup container element. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `className` | `string` | Non-empty string with CSS class name to add to popup container | #### Returns `Popup` #### Example ```ts let popup = new Popup() popup.addClassName('some-class') ``` *** ### addTo() > **addTo**(`map`: [`Map`](Map.md)): `this` Defined in: src/ui/popup.ts:204 Adds the popup to a map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The mapmetrics GL JS map to add the popup to. | #### Returns `this` #### Example ```ts new Popup() .setLngLat([0, 0]) .setHTML("

Null Island

") .addTo(map); ``` #### See - [Display a popup](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup/) - [Display a popup on hover](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) - [Display a popup on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-click/) - [Show polygon information on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/polygon-popup-on-click/) *** ### getElement() > **getElement**(): `HTMLElement` Defined in: src/ui/popup.ts:375 Returns the `Popup`'s HTML element. #### Returns `HTMLElement` element #### Example Change the `Popup` element's font size ```ts let popup = new Popup() .setLngLat([-96, 37.8]) .setHTML("

Hello World!

") .addTo(map); let popupElem = popup.getElement(); popupElem.style.fontSize = "25px"; ``` *** ### getLngLat() > **getLngLat**(): [`LngLat`](LngLat.md) Defined in: src/ui/popup.ts:301 Returns the geographical location of the popup's anchor. The longitude of the result may differ by a multiple of 360 degrees from the longitude previously set by `setLngLat` because `Popup` wraps the anchor longitude across copies of the world to keep the popup on screen. #### Returns [`LngLat`](LngLat.md) The geographical location of the popup's anchor. *** ### getMaxWidth() > **getMaxWidth**(): `string` Defined in: src/ui/popup.ts:438 Returns the popup's maximum width. #### Returns `string` The maximum width of the popup. *** ### isOpen() > **isOpen**(): `boolean` Defined in: src/ui/popup.ts:253 #### Returns `boolean` `true` if the popup is open, `false` if it is closed. *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `Popup` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `Popup` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `Popup` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `Popup` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### remove() > **remove**(): `this` Defined in: src/ui/popup.ts:266 Removes the popup from the map it has been added to. #### Returns `this` #### Example ```ts let popup = new Popup().addTo(map); popup.remove(); ``` *** ### removeClassName() > **removeClassName**(`className`: `string`): `Popup` Defined in: src/ui/popup.ts:518 Removes a CSS class from the popup container element. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `className` | `string` | Non-empty string with CSS class name to remove from popup container | #### Returns `Popup` #### Example ```ts let popup = new Popup() popup.removeClassName('some-class') ``` *** ### setDOMContent() > **setDOMContent**(`htmlNode`: `Node`): `this` Defined in: src/ui/popup.ts:469 Sets the popup's content to the element provided as a DOM node. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `htmlNode` | `Node` | A DOM node to be used as content for the popup. | #### Returns `this` #### Example Create an element with the popup content ```ts let div = document.createElement('div'); div.innerHTML = 'Hello, world!'; let popup = new Popup() .setLngLat(e.lngLat) .setDOMContent(div) .addTo(map); ``` *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `Popup` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `Popup` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### setHTML() > **setHTML**(`html`: `string`): `this` Defined in: src/ui/popup.ts:419 Sets the popup's content to the HTML provided as a string. This method does not perform HTML filtering or sanitization, and must be used only with trusted content. Consider [Popup#setText](#settext) if the content is an untrusted text string. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `html` | `string` | A string representing HTML content for the popup. | #### Returns `this` #### Example ```ts let popup = new Popup() .setLngLat(e.lngLat) .setHTML("

Hello World!

") .addTo(map); ``` #### See - [Display a popup](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup/) - [Display a popup on hover](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) - [Display a popup on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-click/) - [Attach a popup to a marker instance](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/set-popup/) *** ### setLngLat() > **setLngLat**(`lnglat`: [`LngLatLike`](../type-aliases/LngLatLike.md)): `this` Defined in: src/ui/popup.ts:310 Sets the geographical location of the popup's anchor, and moves the popup to it. Replaces trackPointer() behavior. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `lnglat` | [`LngLatLike`](../type-aliases/LngLatLike.md) | The geographical location to set as the popup's anchor. | #### Returns `this` *** ### setMaxWidth() > **setMaxWidth**(`maxWidth`: `string`): `this` Defined in: src/ui/popup.ts:448 Sets the popup's maximum width. This is setting the CSS property `max-width`. Available values can be found here: https://developer.mozilla.org/en-US/docs/Web/CSS/max-width #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `maxWidth` | `string` | A string representing the value for the maximum width. | #### Returns `this` *** ### setOffset() > **setOffset**(`offset?`: [`Offset`](../type-aliases/Offset.md)): `this` Defined in: src/ui/popup.ts:530 Sets the popup's offset. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `offset?` | [`Offset`](../type-aliases/Offset.md) | Sets the popup's offset. | #### Returns `this` *** ### setSubpixelPositioning() > **setSubpixelPositioning**(`value`: `boolean`): `void` Defined in: src/ui/popup.ts:566 Set the option to allow subpixel positioning of the popup by passing a boolean #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `value` | `boolean` | When boolean is true, subpixel positioning is enabled for the popup. | #### Returns `void` #### Example ```ts let popup = new Popup() popup.setSubpixelPositioning(true); ``` *** ### setText() > **setText**(`text`: `string`): `this` Defined in: src/ui/popup.ts:395 Sets the popup's content to a string of text. This function creates a [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) node in the DOM, so it cannot insert raw HTML. Use this method for security against XSS if the popup content is user-provided. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `text` | `string` | Textual content for the popup. | #### Returns `this` #### Example ```ts let popup = new Popup() .setLngLat(e.lngLat) .setText('Hello, world!') .addTo(map); ``` *** ### toggleClassName() > **toggleClassName**(`className`: `string`): `boolean` Defined in: src/ui/popup.ts:549 Add or remove the given CSS class on the popup container, depending on whether the container currently has that class. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `className` | `string` | Non-empty string with CSS class name to add/remove | #### Returns `boolean` if the class was removed return false, if class was added, then return true, undefined if there is no container #### Example ```ts let popup = new Popup() popup.toggleClassName('toggleClass') ``` *** ### trackPointer() > **trackPointer**(): `this` Defined in: src/ui/popup.ts:342 Tracks the popup anchor to the cursor position on screens with a pointer device (it will be hidden on touchscreens). Replaces the `setLngLat` behavior. For most use cases, set `closeOnClick` and `closeButton` to `false`. #### Returns `this` #### Example ```ts let popup = new Popup({ closeOnClick: false, closeButton: false }) .setHTML("

Hello World!

") .trackPointer() .addTo(map); ``` --- # RGBAImage https://docs.mapatlas.xyz/overview/API/classes/RGBAImage # RGBAImage Defined in: src/util/image.ts:114 An object to store image data not premultiplied, because ImageData is not premultiplied. UNPACK_PREMULTIPLY_ALPHA_WEBGL must be used when uploading to a texture. ## Properties ### data > **data**: `Uint8Array` Defined in: src/util/image.ts:121 data must be a Uint8Array instead of Uint8ClampedArray because texImage2D does not support Uint8ClampedArray in all browsers. --- # RasterDEMTileSource https://docs.mapatlas.xyz/overview/API/classes/RasterDEMTileSource # RasterDEMTileSource Defined in: src/source/raster\_dem\_tile\_source.ts:37 A source containing raster DEM tiles (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/) for detailed documentation of options.) This source can be used to show hillshading and 3D terrain ## Example ```ts map.addSource('raster-dem-source', { type: 'raster-dem', url: 'https://demotiles.mapmetrics.org/terrain-tiles/tiles.json', tileSize: 256 }); ``` ## See [3D Terrain](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/3d-terrain/) ## Extends - [`RasterTileSource`](RasterTileSource.md) ## Implements - [`Source`](../interfaces/Source.md) ## Methods ### abortTile() > **abortTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/raster\_tile\_source.ts:213 Allows to abort a tile loading. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to abort | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`abortTile`](../interfaces/Source.md#aborttile) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`abortTile`](RasterTileSource.md#aborttile) *** ### hasTile() > **hasTile**(`tileID`: [`OverscaledTileID`](OverscaledTileID.md)): `boolean` Defined in: src/source/raster\_tile\_source.ts:172 True is the tile is part of the source, false otherwise. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tileID` | [`OverscaledTileID`](OverscaledTileID.md) | The tile ID | #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTile`](../interfaces/Source.md#hastile) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`hasTile`](RasterTileSource.md#hastile) *** ### hasTransition() > **hasTransition**(): `boolean` Defined in: src/source/raster\_tile\_source.ts:226 True if the source has transition, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTransition`](../interfaces/Source.md#hastransition) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`hasTransition`](RasterTileSource.md#hastransition) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`listens`](RasterTileSource.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/raster\_tile\_source.ts:114 True if the source is loaded, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`loaded`](../interfaces/Source.md#loaded) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`loaded`](RasterTileSource.md#loaded) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `RasterDEMTileSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `RasterDEMTileSource` #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`off`](RasterTileSource.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`on`](RasterTileSource.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `void` Defined in: src/source/raster\_tile\_source.ts:118 This method is called when the source is added to the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The map instance | #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onAdd`](../interfaces/Source.md#onadd) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`onAdd`](RasterTileSource.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `RasterDEMTileSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `RasterDEMTileSource` `this` or a promise if a listener is not provided #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`once`](RasterTileSource.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/source/raster\_tile\_source.ts:123 This method is called when the source is removed from the map. #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onRemove`](../interfaces/Source.md#onremove) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`onRemove`](RasterTileSource.md#onremove) *** ### serialize() > **serialize**(): `RasterSourceSpecification` \| `RasterDEMSourceSpecification` Defined in: src/source/raster\_tile\_source.ts:168 #### Returns `RasterSourceSpecification` \| `RasterDEMSourceSpecification` A plain (stringifiable) JS object representing the current state of the source. Creating a source using the returned object as the `options` should result in a Source that is equivalent to this one. #### Implementation of [`Source`](../interfaces/Source.md).[`serialize`](../interfaces/Source.md#serialize) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`serialize`](RasterTileSource.md#serialize) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `RasterDEMTileSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `RasterDEMTileSource` #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`setEventedParent`](RasterTileSource.md#seteventedparent) *** ### setTiles() > **setTiles**(`tiles`: `string`[]): `this` Defined in: src/source/raster\_tile\_source.ts:146 Sets the source `tiles` property and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tiles` | `string`[] | An array of one or more tile source URLs, as in the raster tiles spec (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/) | #### Returns `this` #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`setTiles`](RasterTileSource.md#settiles) *** ### setUrl() > **setUrl**(`url`: `string`): `this` Defined in: src/source/raster\_tile\_source.ts:159 Sets the source `url` property and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `url` | `string` | A URL to a TileJSON resource. Supported protocols are `http:` and `https:`. | #### Returns `this` #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`setUrl`](RasterTileSource.md#seturl) ## Properties ### id > **id**: `string` Defined in: src/source/raster\_tile\_source.ts:54 The id for the source. Must not be used by any existing source. #### Implementation of [`Source`](../interfaces/Source.md).[`id`](../interfaces/Source.md#id) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`id`](RasterTileSource.md#id) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/raster\_tile\_source.ts:56 The maximum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`maxzoom`](../interfaces/Source.md#maxzoom) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`maxzoom`](RasterTileSource.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/raster\_tile\_source.ts:55 The minimum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`minzoom`](../interfaces/Source.md#minzoom) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`minzoom`](RasterTileSource.md#minzoom) *** ### roundZoom > **roundZoom**: `boolean` Defined in: src/source/raster\_tile\_source.ts:63 `true` if zoom levels are rounded to the nearest integer in the source data, `false` if they are floor-ed to the nearest integer. #### Implementation of [`Source`](../interfaces/Source.md).[`roundZoom`](../interfaces/Source.md#roundzoom) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`roundZoom`](RasterTileSource.md#roundzoom) *** ### tileSize > **tileSize**: `number` Defined in: src/source/raster\_tile\_source.ts:59 The tile size for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`tileSize`](../interfaces/Source.md#tilesize) #### Inherited from [`RasterTileSource`](RasterTileSource.md).[`tileSize`](RasterTileSource.md#tilesize) --- # RasterTileSource https://docs.mapatlas.xyz/overview/API/classes/RasterTileSource # RasterTileSource Defined in: src/source/raster\_tile\_source.ts:52 A source containing raster tiles (See the [raster source documentation](https://mapmetrics.org/mapmetrics-style-spec/sources/#raster) for detailed documentation of options.) ## Examples ```ts map.addSource('raster-source', { 'type': 'raster', 'tiles': ['https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg'], 'tileSize': 256, // Set this to match tile server output to avoid blurry rendering }); ``` ```ts map.addSource('wms-test-source', { 'type': 'raster', // use the tiles option to specify a WMS tile source URL 'tiles': [ 'https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015' ], 'tileSize': 256 // Important for WMS if tiles are 256px }); ``` ## See - [Add a raster tile source](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/map-tiles/) - [Add a WMS source](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/wms/) - [Display a satellite map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/satellite-map/) ## Extends - [`Evented`](Evented.md) ## Extended by - [`RasterDEMTileSource`](RasterDEMTileSource.md) ## Implements - [`Source`](../interfaces/Source.md) ## Methods ### abortTile() > **abortTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/raster\_tile\_source.ts:213 Allows to abort a tile loading. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to abort | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`abortTile`](../interfaces/Source.md#aborttile) *** ### hasTile() > **hasTile**(`tileID`: [`OverscaledTileID`](OverscaledTileID.md)): `boolean` Defined in: src/source/raster\_tile\_source.ts:172 True is the tile is part of the source, false otherwise. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tileID` | [`OverscaledTileID`](OverscaledTileID.md) | The tile ID | #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTile`](../interfaces/Source.md#hastile) *** ### hasTransition() > **hasTransition**(): `boolean` Defined in: src/source/raster\_tile\_source.ts:226 True if the source has transition, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTransition`](../interfaces/Source.md#hastransition) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/raster\_tile\_source.ts:114 True if the source is loaded, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`loaded`](../interfaces/Source.md#loaded) *** ### loadTile() > **loadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/raster\_tile\_source.ts:176 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`loadTile`](../interfaces/Source.md#loadtile) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `RasterTileSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `RasterTileSource` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `void` Defined in: src/source/raster\_tile\_source.ts:118 This method is called when the source is added to the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The map instance | #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onAdd`](../interfaces/Source.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `RasterTileSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `RasterTileSource` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/source/raster\_tile\_source.ts:123 This method is called when the source is removed from the map. #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onRemove`](../interfaces/Source.md#onremove) *** ### serialize() > **serialize**(): `RasterSourceSpecification` \| `RasterDEMSourceSpecification` Defined in: src/source/raster\_tile\_source.ts:168 #### Returns `RasterSourceSpecification` \| `RasterDEMSourceSpecification` A plain (stringifiable) JS object representing the current state of the source. Creating a source using the returned object as the `options` should result in a Source that is equivalent to this one. #### Implementation of [`Source`](../interfaces/Source.md).[`serialize`](../interfaces/Source.md#serialize) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `RasterTileSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `RasterTileSource` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### setTiles() > **setTiles**(`tiles`: `string`[]): `this` Defined in: src/source/raster\_tile\_source.ts:146 Sets the source `tiles` property and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tiles` | `string`[] | An array of one or more tile source URLs, as in the raster tiles spec (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/) | #### Returns `this` *** ### setUrl() > **setUrl**(`url`: `string`): `this` Defined in: src/source/raster\_tile\_source.ts:159 Sets the source `url` property and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `url` | `string` | A URL to a TileJSON resource. Supported protocols are `http:` and `https:`. | #### Returns `this` *** ### unloadTile() > **unloadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/raster\_tile\_source.ts:220 Allows to unload a tile. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to unload | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`unloadTile`](../interfaces/Source.md#unloadtile) ## Properties ### id > **id**: `string` Defined in: src/source/raster\_tile\_source.ts:54 The id for the source. Must not be used by any existing source. #### Implementation of [`Source`](../interfaces/Source.md).[`id`](../interfaces/Source.md#id) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/raster\_tile\_source.ts:56 The maximum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`maxzoom`](../interfaces/Source.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/raster\_tile\_source.ts:55 The minimum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`minzoom`](../interfaces/Source.md#minzoom) *** ### roundZoom > **roundZoom**: `boolean` Defined in: src/source/raster\_tile\_source.ts:63 `true` if zoom levels are rounded to the nearest integer in the source data, `false` if they are floor-ed to the nearest integer. #### Implementation of [`Source`](../interfaces/Source.md).[`roundZoom`](../interfaces/Source.md#roundzoom) *** ### tileSize > **tileSize**: `number` Defined in: src/source/raster\_tile\_source.ts:59 The tile size for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`tileSize`](../interfaces/Source.md#tilesize) --- # ScaleControl https://docs.mapatlas.xyz/overview/API/classes/ScaleControl # ScaleControl Defined in: src/ui/control/scale\_control.ts:48 A `ScaleControl` control displays the ratio of a distance on the map to the corresponding distance on the ground. ## Example ```ts let scale = new ScaleControl({ maxWidth: 80, unit: 'imperial' }); map.addControl(scale); scale.setUnit('metric'); ``` ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new ScaleControl**(`options?`: [`ScaleControlOptions`](../type-aliases/ScaleControlOptions.md)): `ScaleControl` Defined in: src/ui/control/scale\_control.ts:56 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | [`ScaleControlOptions`](../type-aliases/ScaleControlOptions.md) | the control's options | #### Returns `ScaleControl` ## Methods ### getDefaultPosition() > **getDefaultPosition**(): [`ControlPosition`](../type-aliases/ControlPosition.md) Defined in: src/ui/control/scale\_control.ts:60 Optionally provide a default position for this control. If this method is implemented and [Map#addControl](Map.md#addcontrol) is called without the `position` parameter, the value returned by getDefaultPosition will be used as the control's position. #### Returns [`ControlPosition`](../type-aliases/ControlPosition.md) a control position, one of the values valid in addControl. #### Implementation of [`IControl`](../interfaces/IControl.md).[`getDefaultPosition`](../interfaces/IControl.md#getdefaultposition) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/scale\_control.ts:69 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/scale\_control.ts:80 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) *** ### setUnit() > **setUnit**(`unit`: [`Unit`](../type-aliases/Unit.md)): `void` Defined in: src/ui/control/scale\_control.ts:91 Set the scale's unit of the distance #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `unit` | [`Unit`](../type-aliases/Unit.md) | Unit of the distance (`'imperial'`, `'metric'` or `'nautical'`). | #### Returns `void` --- # ScrollZoomHandler https://docs.mapatlas.xyz/overview/API/classes/ScrollZoomHandler # ScrollZoomHandler Defined in: src/ui/handler/scroll\_zoom.ts:36 The `ScrollZoomHandler` allows the user to zoom the map by scrolling. ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### \_shouldBePrevented() > **\_shouldBePrevented**(`e`: `WheelEvent`): `boolean` Defined in: src/ui/handler/scroll\_zoom.ts:160 Determines whether or not the gesture is blocked due to cooperativeGestures. #### Parameters | Parameter | Type | | ------ | ------ | | `e` | `WheelEvent` | #### Returns `boolean` *** ### disable() > **disable**(): `void` Defined in: src/ui/handler/scroll\_zoom.ts:152 Disables the "scroll to zoom" interaction. #### Returns `void` #### Example ```ts map.scrollZoom.disable(); ``` #### Implementation of `Handler.disable` *** ### enable() > **enable**(`options?`: `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md)): `void` Defined in: src/ui/handler/scroll\_zoom.ts:138 Enables the "scroll to zoom" interaction. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md) | Options object. | #### Returns `void` #### Example ```ts map.scrollZoom.enable(); map.scrollZoom.enable({ around: 'center' }) ``` #### Implementation of `Handler.enable` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/scroll\_zoom.ts:120 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/scroll\_zoom.ts:111 Returns a Boolean indicating whether the "scroll to zoom" interaction is enabled. #### Returns `boolean` `true` if the "scroll to zoom" interaction is enabled. #### Implementation of `Handler.isEnabled` *** ### renderFrame() > **renderFrame**(): `object` Defined in: src/ui/handler/scroll\_zoom.ts:269 `renderFrame` is the only non-dom event. It is called during render frames and can be used to smooth camera changes (see scroll handler). #### Returns `object` ##### around > **around**: `Point` ##### needsRenderFrame > **needsRenderFrame**: `boolean` = `!finished` ##### noInertia > **noInertia**: `boolean` = `true` ##### originalEvent > **originalEvent**: `any` ##### zoomDelta > **zoomDelta**: `number` #### Implementation of [`Handler`](../interfaces/Handler.md).[`renderFrame`](../interfaces/Handler.md#renderframe) *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/scroll\_zoom.ts:387 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) *** ### setWheelZoomRate() > **setWheelZoomRate**(`wheelZoomRate`: `number`): `void` Defined in: src/ui/handler/scroll\_zoom.ts:103 Set the zoom rate of a mouse wheel #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `wheelZoomRate` | `number` | 1/450 The rate used to scale mouse wheel movement to a zoom value. | #### Returns `void` #### Example Slow down zoom of mouse wheel ```ts map.scrollZoom.setWheelZoomRate(1/600); ``` *** ### setZoomRate() > **setZoomRate**(`zoomRate`: `number`): `void` Defined in: src/ui/handler/scroll\_zoom.ts:90 Set the zoom rate of a trackpad #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `zoomRate` | `number` | 1/100 The rate used to scale trackpad movement to a zoom value. | #### Returns `void` #### Example Speed up trackpad zoom ```ts map.scrollZoom.setZoomRate(1/25); ``` --- # Style https://docs.mapatlas.xyz/overview/API/classes/Style # Style Defined in: src/style/style.ts:201 The Style base class ## Extends - [`Evented`](Evented.md) ## Methods ### \_findGlobalStateAffectedSources() > **\_findGlobalStateAffectedSources**(`globalStateRefs`: `string`[]): `Set`\<`string`\> Defined in: src/style/style.ts:362 Find all sources that are affected by the global state changes. For example, if a layer filter uses global-state expression, this function will return the source id of that layer. #### Parameters | Parameter | Type | | ------ | ------ | | `globalStateRefs` | `string`[] | #### Returns `Set`\<`string`\> *** ### addLayer() > **addLayer**(`layerObject`: [`AddLayerObject`](../type-aliases/AddLayerObject.md), `before?`: `string`, `options?`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `this` Defined in: src/style/style.ts:1035 Add a layer to the map style. The layer will be inserted before the layer with ID `before`, or appended if `before` is omitted. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerObject` | [`AddLayerObject`](../type-aliases/AddLayerObject.md) | The style layer to add. | | `before?` | `string` | ID of an existing layer to insert before | | `options?` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | Style setter options. | #### Returns `this` *** ### addSprite() > **addSprite**(`id`: `string`, `url`: `string`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md), `completion?`: (`err`: `Error`) => `void`): `void` Defined in: src/style/style.ts:1881 Add a sprite. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | The id of the desired sprite | | `url` | `string` | The url to load the desired sprite from | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | The style setter options | | `completion?` | (`err`: `Error`) => `void` | The completion handler | #### Returns `void` *** ### getFilter() > **getFilter**(`layer`: `string`): `void` \| `FilterSpecification` Defined in: src/style/style.ts:1254 Get a layer's filter object #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layer` | `string` | the layer to inspect | #### Returns `void` \| `FilterSpecification` the layer's filter, if any *** ### getLayer() > **getLayer**(`id`: `string`): [`StyleLayer`](StyleLayer.md) Defined in: src/style/style.ts:1179 Return the style layer object with the given `id`. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | id of the desired layer | #### Returns [`StyleLayer`](StyleLayer.md) a layer, if one with the given `id` exists *** ### getLayersOrder() > **getLayersOrder**(): `string`[] Defined in: src/style/style.ts:1188 Return the ids of all layers currently in the style, including custom layers, in order. #### Returns `string`[] ids of layers, in order *** ### getLayoutProperty() > **getLayoutProperty**(`layerId`: `string`, `name`: `string`): `any` Defined in: src/style/style.ts:1279 Get a layout property's value from a given layer #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `layerId` | `string` | the layer to inspect | | `name` | `string` | the name of the layout property | #### Returns `any` the property value *** ### getSource() > **getSource**(`id`: `string`): [`Source`](../interfaces/Source.md) Defined in: src/style/style.ts:1024 Get a source by ID. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | ID of the desired source | #### Returns [`Source`](../interfaces/Source.md) source *** ### getSprite() > **getSprite**(): `object`[] Defined in: src/style/style.ts:1934 Get the current sprite value. #### Returns `object`[] empty array when no sprite is set; id-url pairs otherwise *** ### hasLayer() > **hasLayer**(`id`: `string`): `boolean` Defined in: src/style/style.ts:1198 Checks if a specific layer is present within the style. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | the id of the desired layer | #### Returns `boolean` a boolean specifying if the given layer is present *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### moveLayer() > **moveLayer**(`id`: `string`, `before?`: `string`): `void` Defined in: src/style/style.ts:1110 Moves a layer to a different z-position. The layer will be inserted before the layer with ID `before`, or appended if `before` is omitted. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | ID of the layer to move | | `before?` | `string` | ID of an existing layer to insert before | #### Returns `void` *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `Style` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `Style` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `Style` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `Style` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### removeLayer() > **removeLayer**(`id`: `string`): `void` Defined in: src/style/style.ts:1143 Remove the layer with the given id from the style. A [ErrorEvent](ErrorEvent.md) event will be fired if no such layer exists. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | id of the layer to remove | #### Returns `void` *** ### removeSource() > **removeSource**(`id`: `string`): `this` Defined in: src/style/style.ts:982 Remove a source from this stylesheet, given its id. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | id of the source to remove | #### Returns `this` #### Throws if no source is found with the given ID *** ### removeSprite() > **removeSprite**(`id`: `string`): `void` Defined in: src/style/style.ts:1902 Remove a sprite by its id. When the last sprite is removed, the whole `this.stylesheet.sprite` object becomes `undefined`. This falsy `undefined` value later prevents attempts to load the sprite when it's absent. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | the id of the sprite to remove | #### Returns `void` *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `Style` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `Style` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### setGeoJSONSourceData() > **setGeoJSONSourceData**(`id`: `string`, `data`: `string` \| `GeoJSON`\<`Geometry`, \{[`name`: `string`]: `any`; \}\>): `void` Defined in: src/style/style.ts:1008 Set the data of a GeoJSON source, given its id. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `id` | `string` | id of the source | | `data` | `string` \| `GeoJSON`\<`Geometry`, \{[`name`: `string`]: `any`; \}\> | GeoJSON source | #### Returns `void` *** ### setSprite() > **setSprite**(`sprite`: `SpriteSpecification`, `options`: [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md), `completion?`: (`err`: `Error`) => `void`): `void` Defined in: src/style/style.ts:1945 Set a new value for the style's sprite. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sprite` | `SpriteSpecification` | new sprite value | | `options` | [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | style setter options | | `completion?` | (`err`: `Error`) => `void` | the completion handler | #### Returns `void` *** ### setState() > **setState**(`nextState`: `StyleSpecification`, `options`: [`StyleSwapOptions`](../type-aliases/StyleSwapOptions.md) & [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md)): `boolean` Defined in: src/style/style.ts:805 Update this style's state to match the given style JSON, performing only the necessary mutations. May throw an Error ('Unimplemented: METHOD') if the mapbox-gl-style-spec diff algorithm produces an operation that is not supported. #### Parameters | Parameter | Type | | ------ | ------ | | `nextState` | `StyleSpecification` | | `options` | [`StyleSwapOptions`](../type-aliases/StyleSwapOptions.md) & [`StyleSetterOptions`](../type-aliases/StyleSetterOptions.md) | #### Returns `boolean` true if any changes were made; false otherwise --- # `abstract` StyleLayer https://docs.mapatlas.xyz/overview/API/classes/StyleLayer # `abstract` StyleLayer Defined in: src/style/style\_layer.ts:81 A base class for style layers ## Extends - [`Evented`](Evented.md) ## Extended by - [`CircleStyleLayer`](CircleStyleLayer.md) - [`HeatmapStyleLayer`](HeatmapStyleLayer.md) ## Methods ### getLayoutAffectingGlobalStateRefs() > **getLayoutAffectingGlobalStateRefs**(): `Set`\<`string`\> Defined in: src/style/style\_layer.ts:175 Get list of global state references that are used within layout or filter properties. This is used to determine if layer source need to be reloaded when global state property changes. #### Returns `Set`\<`string`\> *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `StyleLayer` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `StyleLayer` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `StyleLayer` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `StyleLayer` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `StyleLayer` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `StyleLayer` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) --- # SubdivisionGranularityExpression https://docs.mapatlas.xyz/overview/API/classes/SubdivisionGranularityExpression # SubdivisionGranularityExpression Defined in: src/render/subdivision\_granularity\_settings.ts:15 Controls how much subdivision happens for a given type of geometry at different zoom levels. --- # SubdivisionGranularitySetting https://docs.mapatlas.xyz/overview/API/classes/SubdivisionGranularitySetting # SubdivisionGranularitySetting Defined in: src/render/subdivision\_granularity\_settings.ts:45 An object describing how much subdivision should be applied to different types of geometry at different zoom levels. ## Properties ### circle > `readonly` **circle**: [`CircleGranularity`](../type-aliases/CircleGranularity.md) Defined in: src/render/subdivision\_granularity\_settings.ts:70 Controls the granularity of `pitch-alignment: map` circles and heatmap kernels. More granular circles will more closely follow the map's surface. *** ### fill > `readonly` **fill**: [`SubdivisionGranularityExpression`](SubdivisionGranularityExpression.md) Defined in: src/render/subdivision\_granularity\_settings.ts:49 Granularity settings used for fill and fill-extrusion layers (for fill, both polygons and their anti-aliasing outlines). *** ### line > `readonly` **line**: [`SubdivisionGranularityExpression`](SubdivisionGranularityExpression.md) Defined in: src/render/subdivision\_granularity\_settings.ts:54 Granularity used for the line layer. *** ### stencil > `readonly` **stencil**: [`SubdivisionGranularityExpression`](SubdivisionGranularityExpression.md) Defined in: src/render/subdivision\_granularity\_settings.ts:64 Granularity used for stencil masks for tiles. *** ### tile > `readonly` **tile**: [`SubdivisionGranularityExpression`](SubdivisionGranularityExpression.md) Defined in: src/render/subdivision\_granularity\_settings.ts:59 Granularity used for geometry covering the entire tile: raster tiles, etc. *** ### noSubdivision > `readonly` `static` **noSubdivision**: `SubdivisionGranularitySetting` Defined in: src/render/subdivision\_granularity\_settings.ts:105 Granularity settings that disable subdivision altogether. --- # TapDragZoomHandler https://docs.mapatlas.xyz/overview/API/classes/TapDragZoomHandler # TapDragZoomHandler Defined in: src/ui/handler/tap\_drag\_zoom.ts:8 A `TapDragZoomHandler` allows the user to zoom the map at a point by double tapping. It also allows the user pan the map by dragging. ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/tap\_drag\_zoom.ts:109 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/tap\_drag\_zoom.ts:28 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # TapZoomHandler https://docs.mapatlas.xyz/overview/API/classes/TapZoomHandler # TapZoomHandler Defined in: src/ui/handler/tap\_zoom.ts:10 A `TapZoomHandler` allows the user to zoom the map at a point by double tapping ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/tap\_zoom.ts:95 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/tap\_zoom.ts:32 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # TerrainControl https://docs.mapatlas.xyz/overview/API/classes/TerrainControl # TerrainControl Defined in: src/ui/control/terrain\_control.ts:20 A `TerrainControl` control contains a button for turning the terrain on and off. ## Example ```ts let map = new Map({TerrainControl: false}) .addControl(new TerrainControl({ source: "terrain" })); ``` ## Implements - [`IControl`](../interfaces/IControl.md) ## Constructors ### Constructor > **new TerrainControl**(`options`: `TerrainSpecification`): `TerrainControl` Defined in: src/ui/control/terrain\_control.ts:29 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | `TerrainSpecification` | the control's options | #### Returns `TerrainControl` ## Methods ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `HTMLElement` Defined in: src/ui/control/terrain\_control.ts:34 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. #### Implementation of [`IControl`](../interfaces/IControl.md).[`onAdd`](../interfaces/IControl.md#onadd) *** ### onRemove() > **onRemove**(): `void` Defined in: src/ui/control/terrain\_control.ts:48 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](Map.md#removecontrol) internally. #### Returns `void` #### Implementation of [`IControl`](../interfaces/IControl.md).[`onRemove`](../interfaces/IControl.md#onremove) --- # ThrottledInvoker https://docs.mapatlas.xyz/overview/API/classes/ThrottledInvoker # ThrottledInvoker Defined in: src/util/throttled\_invoker.ts:5 Invokes the wrapped function in a non-blocking way when trigger() is called. Invocation requests are ignored until the function was actually invoked. --- # Tile https://docs.mapatlas.xyz/overview/API/classes/Tile # Tile Defined in: src/source/tile.ts:53 A tile object is the combination of a Coordinate, which defines its place, as well as a unique ID and data tracking for its content ## Constructors ### Constructor > **new Tile**(`tileID`: [`OverscaledTileID`](OverscaledTileID.md), `size`: `number`): `Tile` Defined in: src/source/tile.ts:103 #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tileID` | [`OverscaledTileID`](OverscaledTileID.md) | the tile ID | | `size` | `number` | The tile size | #### Returns `Tile` ## Methods ### loadVectorData() > **loadVectorData**(`data`: [`WorkerTileResult`](../type-aliases/WorkerTileResult.md), `painter`: `any`, `justReloaded?`: `boolean`): `void` Defined in: src/source/tile.ts:154 Given a data object with a 'buffers' property, load it into this tile's elementGroups and buffers properties and set loaded to true. If the data is null, like in the case of an empty GeoJSON tile, no-op but still set loaded to true. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `data` | [`WorkerTileResult`](../type-aliases/WorkerTileResult.md) | The data from the worker | | `painter` | `any` | the painter | | `justReloaded?` | `boolean` | `true` to just reload | #### Returns `void` *** ### unloadVectorData() > **unloadVectorData**(): `void` Defined in: src/source/tile.ts:227 Release any data or WebGL resources referenced by this tile. #### Returns `void` --- # TouchPanHandler https://docs.mapatlas.xyz/overview/API/classes/TouchPanHandler # TouchPanHandler Defined in: src/ui/handler/touch\_pan.ts:9 A `TouchPanHandler` allows the user to pan the map using touch gestures. ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/touch\_pan.ts:112 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/touch\_pan.ts:26 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # `abstract` TwoFingersTouchHandler https://docs.mapatlas.xyz/overview/API/classes/TwoFingersTouchHandler # `abstract` TwoFingersTouchHandler Defined in: src/ui/handler/two\_fingers\_touch.ts:20 The `TwoFingersTouchHandler`s allows the user to zoom, pitch and rotate the map using two fingers ## Extended by - [`TwoFingersTouchZoomHandler`](TwoFingersTouchZoomHandler.md) - [`TwoFingersTouchRotateHandler`](TwoFingersTouchRotateHandler.md) - [`TwoFingersTouchPitchHandler`](TwoFingersTouchPitchHandler.md) ## Implements - [`Handler`](../interfaces/Handler.md) ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:108 Disables the "drag to pitch" interaction. #### Returns `void` #### Example ```ts map.touchPitch.disable(); ``` #### Implementation of `Handler.disable` *** ### enable() > **enable**(`options?`: `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md)): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:95 Enables the "drag to pitch" interaction. #### Parameters | Parameter | Type | | ------ | ------ | | `options?` | `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md) | #### Returns `void` #### Example ```ts map.touchPitch.enable(); ``` #### Implementation of `Handler.enable` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:127 Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "drag to pitch" interaction is active. #### Implementation of [`Handler`](../interfaces/Handler.md).[`isActive`](../interfaces/Handler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:118 Returns a Boolean indicating whether the "drag to pitch" interaction is enabled. #### Returns `boolean` `true` if the "drag to pitch" interaction is enabled. #### Implementation of `Handler.isEnabled` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:34 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Implementation of [`Handler`](../interfaces/Handler.md).[`reset`](../interfaces/Handler.md#reset) --- # TwoFingersTouchPitchHandler https://docs.mapatlas.xyz/overview/API/classes/TwoFingersTouchPitchHandler # TwoFingersTouchPitchHandler Defined in: src/ui/handler/two\_fingers\_touch.ts:253 The `TwoFingersTouchPitchHandler` allows the user to pitch the map by dragging up and down with two fingers. ## Extends - [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md) ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:108 Disables the "drag to pitch" interaction. #### Returns `void` #### Example ```ts map.touchPitch.disable(); ``` #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`disable`](TwoFingersTouchHandler.md#disable) *** ### enable() > **enable**(`options?`: `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md)): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:95 Enables the "drag to pitch" interaction. #### Parameters | Parameter | Type | | ------ | ------ | | `options?` | `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md) | #### Returns `void` #### Example ```ts map.touchPitch.enable(); ``` #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`enable`](TwoFingersTouchHandler.md#enable) *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:127 Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "drag to pitch" interaction is active. #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`isActive`](TwoFingersTouchHandler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:118 Returns a Boolean indicating whether the "drag to pitch" interaction is enabled. #### Returns `boolean` `true` if the "drag to pitch" interaction is enabled. #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`isEnabled`](TwoFingersTouchHandler.md#isenabled) --- # TwoFingersTouchRotateHandler https://docs.mapatlas.xyz/overview/API/classes/TwoFingersTouchRotateHandler # TwoFingersTouchRotateHandler Defined in: src/ui/handler/two\_fingers\_touch.ts:192 The `TwoFingersTouchHandler`s allows the user to rotate the map two fingers ## Extends - [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md) ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:108 Disables the "drag to pitch" interaction. #### Returns `void` #### Example ```ts map.touchPitch.disable(); ``` #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`disable`](TwoFingersTouchHandler.md#disable) *** ### enable() > **enable**(`options?`: `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md)): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:95 Enables the "drag to pitch" interaction. #### Parameters | Parameter | Type | | ------ | ------ | | `options?` | `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md) | #### Returns `void` #### Example ```ts map.touchPitch.enable(); ``` #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`enable`](TwoFingersTouchHandler.md#enable) *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:127 Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "drag to pitch" interaction is active. #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`isActive`](TwoFingersTouchHandler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:118 Returns a Boolean indicating whether the "drag to pitch" interaction is enabled. #### Returns `boolean` `true` if the "drag to pitch" interaction is enabled. #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`isEnabled`](TwoFingersTouchHandler.md#isenabled) --- # TwoFingersTouchZoomHandler https://docs.mapatlas.xyz/overview/API/classes/TwoFingersTouchZoomHandler # TwoFingersTouchZoomHandler Defined in: src/ui/handler/two\_fingers\_touch.ts:152 The `TwoFingersTouchHandler`s allows the user to zoom the map two fingers ## Extends - [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md) ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:108 Disables the "drag to pitch" interaction. #### Returns `void` #### Example ```ts map.touchPitch.disable(); ``` #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`disable`](TwoFingersTouchHandler.md#disable) *** ### enable() > **enable**(`options?`: `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md)): `void` Defined in: src/ui/handler/two\_fingers\_touch.ts:95 Enables the "drag to pitch" interaction. #### Parameters | Parameter | Type | | ------ | ------ | | `options?` | `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md) | #### Returns `void` #### Example ```ts map.touchPitch.enable(); ``` #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`enable`](TwoFingersTouchHandler.md#enable) *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:127 Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used. #### Returns `boolean` `true` if the "drag to pitch" interaction is active. #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`isActive`](TwoFingersTouchHandler.md#isactive) *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/two\_fingers\_touch.ts:118 Returns a Boolean indicating whether the "drag to pitch" interaction is enabled. #### Returns `boolean` `true` if the "drag to pitch" interaction is enabled. #### Inherited from [`TwoFingersTouchHandler`](TwoFingersTouchHandler.md).[`isEnabled`](TwoFingersTouchHandler.md#isenabled) --- # TwoFingersTouchZoomRotateHandler https://docs.mapatlas.xyz/overview/API/classes/TwoFingersTouchZoomRotateHandler # TwoFingersTouchZoomRotateHandler Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:13 The `TwoFingersTouchZoomRotateHandler` allows the user to zoom and rotate the map by pinching on a touchscreen. They can zoom with one finger by double tapping and dragging. On the second tap, hold the finger down and drag up or down to zoom in or out. ## Methods ### disable() > **disable**(): `void` Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:58 Disables the "pinch to rotate and zoom" interaction. #### Returns `void` #### Example ```ts map.touchZoomRotate.disable(); ``` *** ### disableRotation() > **disableRotation**(): `void` Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:94 Disables the "pinch to rotate" interaction, leaving the "pinch to zoom" interaction enabled. #### Returns `void` #### Example ```ts map.touchZoomRotate.disableRotation(); ``` *** ### enable() > **enable**(`options?`: `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md)): `void` Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:43 Enables the "pinch to rotate and zoom" interaction. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options?` | `boolean` \| [`AroundCenterOptions`](../type-aliases/AroundCenterOptions.md) | Options object. | #### Returns `void` #### Example ```ts map.touchZoomRotate.enable(); map.touchZoomRotate.enable({ around: 'center' }); ``` *** ### enableRotation() > **enableRotation**(): `void` Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:108 Enables the "pinch to rotate" interaction. #### Returns `void` #### Example ```ts map.touchZoomRotate.enable(); map.touchZoomRotate.enableRotation(); ``` *** ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:81 Returns true if the handler is enabled and has detected the start of a zoom/rotate gesture. #### Returns `boolean` `true` if the handler is active, `false` otherwise *** ### isEnabled() > **isEnabled**(): `boolean` Defined in: src/ui/handler/shim/two\_fingers\_touch.ts:70 Returns a Boolean indicating whether the "pinch to rotate and zoom" interaction is enabled. #### Returns `boolean` `true` if the "pinch to rotate and zoom" interaction is enabled. --- # VectorTileSource https://docs.mapatlas.xyz/overview/API/classes/VectorTileSource # VectorTileSource Defined in: src/source/vector\_tile\_source.ts:57 A source containing vector tiles in [Mapbox Vector Tile format](https://docs.mapbox.com/vector-tiles/reference/). (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/) for detailed documentation of options.) ## Examples ```ts map.addSource('some id', { type: 'vector', url: 'https://demotiles.mapmetrics.org/tiles/tiles.json' }); ``` ```ts map.addSource('some id', { type: 'vector', tiles: ['https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt'], minzoom: 6, maxzoom: 14 }); ``` ```ts map.getSource('some id').setUrl("https://demotiles.mapmetrics.org/tiles/tiles.json"); ``` ```ts map.getSource('some id').setTiles(['https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt']); ``` ## See [Add a vector tile source](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/vector-source/) ## Extends - [`Evented`](Evented.md) ## Implements - [`Source`](../interfaces/Source.md) ## Methods ### abortTile() > **abortTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/vector\_tile\_source.ts:256 Allows to abort a tile loading. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to abort | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`abortTile`](../interfaces/Source.md#aborttile) *** ### hasTile() > **hasTile**(`tileID`: [`OverscaledTileID`](OverscaledTileID.md)): `boolean` Defined in: src/source/vector\_tile\_source.ts:134 True is the tile is part of the source, false otherwise. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tileID` | [`OverscaledTileID`](OverscaledTileID.md) | The tile ID | #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTile`](../interfaces/Source.md#hastile) *** ### hasTransition() > **hasTransition**(): `boolean` Defined in: src/source/vector\_tile\_source.ts:282 True if the source has transition, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`hasTransition`](../interfaces/Source.md#hastransition) *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`Evented`](Evented.md).[`listens`](Evented.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/vector\_tile\_source.ts:130 True if the source is loaded, false otherwise. #### Returns `boolean` #### Implementation of [`Source`](../interfaces/Source.md).[`loaded`](../interfaces/Source.md#loaded) *** ### loadTile() > **loadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/vector\_tile\_source.ts:191 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`loadTile`](../interfaces/Source.md#loadtile) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `VectorTileSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `VectorTileSource` #### Inherited from [`Evented`](Evented.md).[`off`](Evented.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`Evented`](Evented.md).[`on`](Evented.md#on) *** ### onAdd() > **onAdd**(`map`: [`Map`](Map.md)): `void` Defined in: src/source/vector\_tile\_source.ts:138 This method is called when the source is added to the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](Map.md) | The map instance | #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onAdd`](../interfaces/Source.md#onadd) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `VectorTileSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `VectorTileSource` `this` or a promise if a listener is not provided #### Inherited from [`Evented`](Evented.md).[`once`](Evented.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/source/vector\_tile\_source.ts:180 This method is called when the source is removed from the map. #### Returns `void` #### Implementation of [`Source`](../interfaces/Source.md).[`onRemove`](../interfaces/Source.md#onremove) *** ### serialize() > **serialize**(): `VectorSourceSpecification` Defined in: src/source/vector\_tile\_source.ts:187 #### Returns `VectorSourceSpecification` A plain (stringifiable) JS object representing the current state of the source. Creating a source using the returned object as the `options` should result in a Source that is equivalent to this one. #### Implementation of [`Source`](../interfaces/Source.md).[`serialize`](../interfaces/Source.md#serialize) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `VectorTileSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `VectorTileSource` #### Inherited from [`Evented`](Evented.md).[`setEventedParent`](Evented.md#seteventedparent) *** ### setTiles() > **setTiles**(`tiles`: `string`[]): `this` Defined in: src/source/vector\_tile\_source.ts:158 Sets the source `tiles` property and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tiles` | `string`[] | An array of one or more tile source URLs, as in the TileJSON spec. | #### Returns `this` *** ### setUrl() > **setUrl**(`url`: `string`): `this` Defined in: src/source/vector\_tile\_source.ts:171 Sets the source `url` property and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `url` | `string` | A URL to a TileJSON resource. Supported protocols are `http:` and `https:`. | #### Returns `this` *** ### unloadTile() > **unloadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/vector\_tile\_source.ts:269 Allows to unload a tile. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to unload | #### Returns `Promise`\<`void`\> #### Implementation of [`Source`](../interfaces/Source.md).[`unloadTile`](../interfaces/Source.md#unloadtile) ## Properties ### id > **id**: `string` Defined in: src/source/vector\_tile\_source.ts:59 The id for the source. Must not be used by any existing source. #### Implementation of [`Source`](../interfaces/Source.md).[`id`](../interfaces/Source.md#id) *** ### isTileClipped > **isTileClipped**: `boolean` Defined in: src/source/vector\_tile\_source.ts:75 `false` if tiles can be drawn outside their boundaries, `true` if they cannot. #### Implementation of [`Source`](../interfaces/Source.md).[`isTileClipped`](../interfaces/Source.md#istileclipped) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/vector\_tile\_source.ts:61 The maximum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`maxzoom`](../interfaces/Source.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/vector\_tile\_source.ts:60 The minimum zoom level for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`minzoom`](../interfaces/Source.md#minzoom) *** ### reparseOverscaled > **reparseOverscaled**: `boolean` Defined in: src/source/vector\_tile\_source.ts:74 `true` if tiles should be sent back to the worker for each overzoomed zoom level, `false` if not. #### Implementation of [`Source`](../interfaces/Source.md).[`reparseOverscaled`](../interfaces/Source.md#reparseoverscaled) *** ### tileSize > **tileSize**: `number` Defined in: src/source/vector\_tile\_source.ts:64 The tile size for the source. #### Implementation of [`Source`](../interfaces/Source.md).[`tileSize`](../interfaces/Source.md#tilesize) --- # VideoSource https://docs.mapatlas.xyz/overview/API/classes/VideoSource # VideoSource Defined in: src/source/video\_source.ts:54 A data source containing video. (See the [Style Specification](https://mapmetrics.org/mapmetrics-style-spec/#sources-video) for detailed documentation of options.) ## Example ```ts // add to map map.addSource('some id', { type: 'video', url: [ 'https://www.mapbox.com/blog/assets/baltimore-smoke.mp4', 'https://www.mapbox.com/blog/assets/baltimore-smoke.webm' ], coordinates: [ [-76.54, 39.18], [-76.52, 39.18], [-76.52, 39.17], [-76.54, 39.17] ] }); // update let mySource = map.getSource('some id'); mySource.setCoordinates([ [-76.54335737228394, 39.18579907229748], [-76.52803659439087, 39.1838364847587], [-76.5295386314392, 39.17683392507606], [-76.54520273208618, 39.17876344106642] ]); map.removeSource('some id'); // remove ``` ## See [Add a video](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/video-on-a-map/) Note that when rendered as a raster layer, the layer's `raster-fade-duration` property will cause the video to fade in. This happens when playback is started, paused and resumed, or when the video's coordinates are updated. To avoid this behavior, set the layer's `raster-fade-duration` property to `0`. ## Extends - [`ImageSource`](ImageSource.md) ## Methods ### getVideo() > **getVideo**(): `HTMLVideoElement` Defined in: src/source/video\_source.ts:135 Returns the HTML `video` element. #### Returns `HTMLVideoElement` The HTML `video` element. *** ### listens() > **listens**(`type`: `string`): `boolean` Defined in: src/util/evented.ts:165 Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type | #### Returns `boolean` `true` if there is at least one registered listener for specified event type, `false` otherwise #### Inherited from [`ImageSource`](ImageSource.md).[`listens`](ImageSource.md#listens) *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/image\_source.ts:164 True if the source is loaded, false otherwise. #### Returns `boolean` #### Inherited from [`ImageSource`](ImageSource.md).[`loaded`](ImageSource.md#loaded) *** ### loadTile() > **loadTile**(`tile`: [`Tile`](Tile.md)): `Promise`\<`void`\> Defined in: src/source/image\_source.ts:276 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> #### Inherited from [`ImageSource`](ImageSource.md).[`loadTile`](ImageSource.md#loadtile) *** ### off() > **off**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): `VideoSource` Defined in: src/util/evented.ts:90 Removes a previously registered event listener. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to remove listeners for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The listener function to remove. | #### Returns `VideoSource` #### Inherited from [`ImageSource`](ImageSource.md).[`off`](ImageSource.md#off) *** ### on() > **on**(`type`: `string`, `listener`: [`Listener`](../type-aliases/Listener.md)): [`Subscription`](../interfaces/Subscription.md) Defined in: src/util/evented.ts:73 Adds a listener to a specified event type. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to add a listen for. | | `listener` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired. The listener function is called with the data object passed to `fire`, extended with `target` and `type` properties. | #### Returns [`Subscription`](../interfaces/Subscription.md) #### Inherited from [`ImageSource`](ImageSource.md).[`on`](ImageSource.md#on) *** ### once() > **once**(`type`: `string`, `listener?`: [`Listener`](../type-aliases/Listener.md)): `Promise`\<`any`\> \| `VideoSource` Defined in: src/util/evented.ts:106 Adds a listener that will be called only once to a specified event type. The listener will be called first time the event fires after the listener is registered. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `type` | `string` | The event type to listen for. | | `listener?` | [`Listener`](../type-aliases/Listener.md) | The function to be called when the event is fired the first time. | #### Returns `Promise`\<`any`\> \| `VideoSource` `this` or a promise if a listener is not provided #### Inherited from [`ImageSource`](ImageSource.md).[`once`](ImageSource.md#once) *** ### onRemove() > **onRemove**(): `void` Defined in: src/source/image\_source.ts:201 This method is called when the source is removed from the map. #### Returns `void` #### Inherited from [`ImageSource`](ImageSource.md).[`onRemove`](ImageSource.md#onremove) *** ### pause() > **pause**(): `void` Defined in: src/source/video\_source.ts:103 Pauses the video. #### Returns `void` *** ### play() > **play**(): `void` Defined in: src/source/video\_source.ts:112 Plays the video. #### Returns `void` *** ### prepare() > **prepare**(): `this` Defined in: src/source/video\_source.ts:152 Sets the video's coordinates and re-renders the map. #### Returns `this` #### Overrides [`ImageSource`](ImageSource.md).[`prepare`](ImageSource.md#prepare) *** ### seek() > **seek**(`seconds`: `number`): `void` Defined in: src/source/video\_source.ts:121 Sets playback to a timestamp, in seconds. #### Parameters | Parameter | Type | | ------ | ------ | | `seconds` | `number` | #### Returns `void` *** ### setCoordinates() > **setCoordinates**(`coordinates`: [`Coordinates`](../type-aliases/Coordinates.md)): `this` Defined in: src/source/image\_source.ts:216 Sets the image's coordinates and re-renders the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `coordinates` | [`Coordinates`](../type-aliases/Coordinates.md) | Four geographical coordinates, represented as arrays of longitude and latitude numbers, which define the corners of the image. The coordinates start at the top left corner of the image and proceed in clockwise order. They do not have to represent a rectangle. | #### Returns `this` #### Inherited from [`ImageSource`](ImageSource.md).[`setCoordinates`](ImageSource.md#setcoordinates) *** ### setEventedParent() > **setEventedParent**(`parent?`: [`Evented`](Evented.md), `data?`: `any`): `VideoSource` Defined in: src/util/evented.ts:176 Bubble all events fired by this instance of Evented to this parent instance of Evented. #### Parameters | Parameter | Type | | ------ | ------ | | `parent?` | [`Evented`](Evented.md) | | `data?` | `any` | #### Returns `VideoSource` #### Inherited from [`ImageSource`](ImageSource.md).[`setEventedParent`](ImageSource.md#seteventedparent) *** ### updateImage() > **updateImage**(`options`: [`UpdateImageOptions`](../type-aliases/UpdateImageOptions.md)): `this` Defined in: src/source/image\_source.ts:174 Updates the image URL and, optionally, the coordinates. To avoid having the image flash after changing, set the `raster-fade-duration` paint property on the raster layer to 0. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`UpdateImageOptions`](../type-aliases/UpdateImageOptions.md) | The options object. | #### Returns `this` #### Inherited from [`ImageSource`](ImageSource.md).[`updateImage`](ImageSource.md#updateimage) ## Properties ### id > **id**: `string` Defined in: src/source/image\_source.ts:95 The id for the source. Must not be used by any existing source. #### Inherited from [`ImageSource`](ImageSource.md).[`id`](ImageSource.md#id) *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/image\_source.ts:97 The maximum zoom level for the source. #### Inherited from [`ImageSource`](ImageSource.md).[`maxzoom`](ImageSource.md#maxzoom) *** ### minzoom > **minzoom**: `number` Defined in: src/source/image\_source.ts:96 The minimum zoom level for the source. #### Inherited from [`ImageSource`](ImageSource.md).[`minzoom`](ImageSource.md#minzoom) *** ### terrainTileRanges > **terrainTileRanges**: `object` Defined in: src/source/image\_source.ts:104 This object is used to store the range of terrain tiles that overlap with this tile. It is relevant for image tiles, as the image exceeds single tile boundaries. #### Index Signature \[`zoom`: `string`\]: `CanonicalTileRange` #### Inherited from [`ImageSource`](ImageSource.md).[`terrainTileRanges`](ImageSource.md#terraintileranges) *** ### tileSize > **tileSize**: `number` Defined in: src/source/image\_source.ts:98 The tile size for the source. #### Inherited from [`ImageSource`](ImageSource.md).[`tileSize`](ImageSource.md#tilesize) --- # WorkerPool https://docs.mapatlas.xyz/overview/API/classes/WorkerPool # WorkerPool Defined in: src/util/worker\_pool.ts:11 Constructs a worker pool. --- # Classes https://docs.mapatlas.xyz/overview/API/classes/ # Classes This section contains all the classes available in the MapMetrics GL API. ## Core Classes - [Actor](./Actor.md) - Handles communication between the main thread and workers - [AJAXError](./AJAXError.md) - Represents AJAX request errors - [AlphaImage](./AlphaImage.md) - Image data with alpha channel - [AttributionControl](./AttributionControl.md) - Control for displaying attribution information - [BoxZoomHandler](./BoxZoomHandler.md) - Handles box zoom interactions - [CanonicalTileID](./CanonicalTileID.md) - Represents a canonical tile identifier - [CanvasSource](./CanvasSource.md) - Source for canvas-based data - [CircleStyleLayer](./CircleStyleLayer.md) - Style layer for circle geometries - [ClickZoomHandler](./ClickZoomHandler.md) - Handles click zoom interactions - [CooperativeGesturesHandler](./CooperativeGesturesHandler.md) - Handles cooperative gesture interactions - [DEMData](./DEMData.md) - Digital Elevation Model data - [Dispatcher](./Dispatcher.md) - Event dispatcher for handling events - [DoubleClickZoomHandler](./DoubleClickZoomHandler.md) - Handles double-click zoom interactions - [DragPanHandler](./DragPanHandler.md) - Handles drag pan interactions - [DragRotateHandler](./DragRotateHandler.md) - Handles drag rotate interactions - [EdgeInsets](./EdgeInsets.md) - Represents edge insets for padding - [ErrorEvent](./ErrorEvent.md) - Represents error events - [Event](./Event.md) - Base event class - [Evented](./Evented.md) - Base class for objects that can emit events - [FeatureIndex](./FeatureIndex.md) - Index for features in a tile - [FullscreenControl](./FullscreenControl.md) - Control for fullscreen functionality - [GeoJSONFeature](./GeoJSONFeature.md) - Represents a GeoJSON feature - [GeoJSONSource](./GeoJSONSource.md) - Source for GeoJSON data - [GeolocateControl](./GeolocateControl.md) - Control for geolocation functionality - [GlobeControl](./GlobeControl.md) - Control for globe view - [Hash](./Hash.md) - Handles URL hash management - [HeatmapStyleLayer](./HeatmapStyleLayer.md) - Style layer for heatmap visualization - [ImageAtlas](./ImageAtlas.md) - Atlas for managing images - [ImageManager](./ImageManager.md) - Manages image loading and caching - [ImageSource](./ImageSource.md) - Source for image data - [KeyboardHandler](./KeyboardHandler.md) - Handles keyboard interactions - [Layout](./Layout.md) - Layout configuration for style layers - [LngLat](./LngLat.md) - Represents longitude and latitude coordinates - [LngLatBounds](./LngLatBounds.md) - Represents longitude/latitude bounds - [LogoControl](./LogoControl.md) - Control for displaying logo - [Map](./Map.md) - Main map class - [MapMouseEvent](./MapMouseEvent.md) - Mouse event on the map - [MapTouchEvent](./MapTouchEvent.md) - Touch event on the map - [MapWheelEvent](./MapWheelEvent.md) - Wheel event on the map - [Marker](./Marker.md) - Represents a marker on the map - [MercatorCoordinate](./MercatorCoordinate.md) - Represents Mercator projection coordinates - [NavigationControl](./NavigationControl.md) - Control for navigation (zoom, rotate, pitch) - [OverscaledTileID](./OverscaledTileID.md) - Represents an overscaled tile identifier - [Popup](./Popup.md) - Represents a popup on the map - [RasterDEMTileSource](./RasterDEMTileSource.md) - Source for raster DEM tiles - [RasterTileSource](./RasterTileSource.md) - Source for raster tiles - [RGBAImage](./RGBAImage.md) - Image data with RGBA channels - [ScaleControl](./ScaleControl.md) - Control for displaying scale - [ScrollZoomHandler](./ScrollZoomHandler.md) - Handles scroll zoom interactions - [Style](./Style.md) - Manages the map style - [StyleLayer](./StyleLayer.md) - Base class for style layers - [SubdivisionGranularityExpression](./SubdivisionGranularityExpression.md) - Expression for subdivision granularity - [SubdivisionGranularitySetting](./SubdivisionGranularitySetting.md) - Setting for subdivision granularity - [TapDragZoomHandler](./TapDragZoomHandler.md) - Handles tap-drag zoom interactions - [TapZoomHandler](./TapZoomHandler.md) - Handles tap zoom interactions - [TerrainControl](./TerrainControl.md) - Control for terrain functionality - [ThrottledInvoker](./ThrottledInvoker.md) - Throttles function invocations - [Tile](./Tile.md) - Represents a map tile - [TouchPanHandler](./TouchPanHandler.md) - Handles touch pan interactions - [TwoFingersTouchHandler](./TwoFingersTouchHandler.md) - Base handler for two-finger touch interactions - [TwoFingersTouchPitchHandler](./TwoFingersTouchPitchHandler.md) - Handles two-finger touch pitch interactions - [TwoFingersTouchRotateHandler](./TwoFingersTouchRotateHandler.md) - Handles two-finger touch rotate interactions - [TwoFingersTouchZoomHandler](./TwoFingersTouchZoomHandler.md) - Handles two-finger touch zoom interactions - [TwoFingersTouchZoomRotateHandler](./TwoFingersTouchZoomRotateHandler.md) - Handles two-finger touch zoom and rotate interactions - [VectorTileSource](./VectorTileSource.md) - Source for vector tiles - [VideoSource](./VideoSource.md) - Source for video data - [WorkerPool](./WorkerPool.md) - Pool of worker threads --- # MessageType https://docs.mapatlas.xyz/overview/API/enumerations/MessageType # MessageType Defined in: src/util/actor\_messages.ts:86 All the possible message types that can be sent to and from the worker ## Enumeration Members ### abortTile > **abortTile**: `"AT"` Defined in: src/util/actor\_messages.ts:106 *** ### getClusterChildren > **getClusterChildren**: `"GCC"` Defined in: src/util/actor\_messages.ts:89 *** ### getClusterExpansionZoom > **getClusterExpansionZoom**: `"GCEZ"` Defined in: src/util/actor\_messages.ts:88 *** ### getClusterLeaves > **getClusterLeaves**: `"GCL"` Defined in: src/util/actor\_messages.ts:90 *** ### getData > **getData**: `"GD"` Defined in: src/util/actor\_messages.ts:92 *** ### getGlyphs > **getGlyphs**: `"GG"` Defined in: src/util/actor\_messages.ts:95 *** ### getImages > **getImages**: `"GI"` Defined in: src/util/actor\_messages.ts:96 *** ### getResource > **getResource**: `"GR"` Defined in: src/util/actor\_messages.ts:108 *** ### importScript > **importScript**: `"IS"` Defined in: src/util/actor\_messages.ts:104 *** ### loadData > **loadData**: `"LD"` Defined in: src/util/actor\_messages.ts:91 *** ### loadDEMTile > **loadDEMTile**: `"LDT"` Defined in: src/util/actor\_messages.ts:87 *** ### loadTile > **loadTile**: `"LT"` Defined in: src/util/actor\_messages.ts:93 *** ### reloadTile > **reloadTile**: `"RT"` Defined in: src/util/actor\_messages.ts:94 *** ### removeDEMTile > **removeDEMTile**: `"RDT"` Defined in: src/util/actor\_messages.ts:107 *** ### removeMap > **removeMap**: `"RM"` Defined in: src/util/actor\_messages.ts:103 *** ### removeSource > **removeSource**: `"RS"` Defined in: src/util/actor\_messages.ts:102 *** ### removeTile > **removeTile**: `"RMT"` Defined in: src/util/actor\_messages.ts:105 *** ### setImages > **setImages**: `"SI"` Defined in: src/util/actor\_messages.ts:97 *** ### setLayers > **setLayers**: `"SL"` Defined in: src/util/actor\_messages.ts:98 *** ### setReferrer > **setReferrer**: `"SR"` Defined in: src/util/actor\_messages.ts:101 *** ### syncRTLPluginState > **syncRTLPluginState**: `"SRPS"` Defined in: src/util/actor\_messages.ts:100 *** ### updateLayers > **updateLayers**: `"UL"` Defined in: src/util/actor\_messages.ts:99 --- # ResourceType https://docs.mapatlas.xyz/overview/API/enumerations/ResourceType # ResourceType Defined in: src/util/request\_manager.ts:6 A type of mapmetrics resource. ## Enumeration Members ### Glyphs > **Glyphs**: `"Glyphs"` Defined in: src/util/request\_manager.ts:7 *** ### Image > **Image**: `"Image"` Defined in: src/util/request\_manager.ts:8 *** ### Source > **Source**: `"Source"` Defined in: src/util/request\_manager.ts:9 *** ### SpriteImage > **SpriteImage**: `"SpriteImage"` Defined in: src/util/request\_manager.ts:10 *** ### SpriteJSON > **SpriteJSON**: `"SpriteJSON"` Defined in: src/util/request\_manager.ts:11 *** ### Style > **Style**: `"Style"` Defined in: src/util/request\_manager.ts:12 *** ### Tile > **Tile**: `"Tile"` Defined in: src/util/request\_manager.ts:13 *** ### Unknown > **Unknown**: `"Unknown"` Defined in: src/util/request\_manager.ts:14 --- # TextFit https://docs.mapatlas.xyz/overview/API/enumerations/TextFit # TextFit Defined in: src/style/style\_image.ts:37 Enumeration of possible values for StyleImageMetadata.textFitWidth and textFitHeight. ## Enumeration Members ### proportional > **proportional**: `"proportional"` Defined in: src/style/style\_image.ts:52 The image will be resized on the specified axis to fit the content rectangle to the target text and will resize the other axis to maintain the aspect ratio of the content rectangle. *** ### stretchOnly > **stretchOnly**: `"stretchOnly"` Defined in: src/style/style\_image.ts:47 The image will be resized on the specified axis to fit the content rectangle to the target text, but will not fall below the aspect ratio of the original content rectangle if the other axis is set to proportional. *** ### stretchOrShrink > **stretchOrShrink**: `"stretchOrShrink"` Defined in: src/style/style\_image.ts:42 The image will be resized on the specified axis to tightly fit the content rectangle to target text. This is the same as not being defined. --- # Enumerations https://docs.mapatlas.xyz/overview/API/enumerations/ # Enumerations This section contains all the enumerations available in the MapMetrics GL API. ## Core Enumerations - [MessageType](./MessageType.md) - Types of messages for communication - [ResourceType](./ResourceType.md) - Types of resources that can be loaded - [TextFit](./TextFit.md) - Text fitting options for labels --- # addProtocol() https://docs.mapatlas.xyz/overview/API/functions/addProtocol # addProtocol() > **addProtocol**(`customProtocol`: `string`, `loadFn`: [`AddProtocolAction`](../type-aliases/AddProtocolAction.md)): `void` Defined in: src/source/protocol\_crud.ts:33 Adds a custom load resource function that will be called when using a URL that starts with a custom url schema. This will happen in the main thread, and workers might call it if they don't know how to handle the protocol. The example below will be triggered for custom:// urls defined in the sources list in the style definitions. The function passed will receive the request parameters and should return with the resulting resource, for example a pbf vector tile, non-compressed, represented as ArrayBuffer. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `customProtocol` | `string` | the protocol to hook, for example 'custom' | | `loadFn` | [`AddProtocolAction`](../type-aliases/AddProtocolAction.md) | the function to use when trying to fetch a tile specified by the customProtocol | ## Returns `void` ## Example ```ts // This will fetch a file using the fetch API (this is obviously a non interesting example...) addProtocol('custom', async (params, abortController) => { const t = await fetch(`https://${params.url.split("://")[1]}`); if (t.status == 200) { const buffer = await t.arrayBuffer(); return {data: buffer} } else { throw new Error(`Tile fetch error: ${t.statusText}`); } }); // the following is an example of a way to return an error when trying to load a tile addProtocol('custom2', async (params, abortController) => { throw new Error('someErrorMessage')); }); ``` --- # addSourceType() https://docs.mapatlas.xyz/overview/API/functions/addSourceType # addSourceType() > **addSourceType**(`name`: `string`, `SourceType`: [`SourceClass`](../type-aliases/SourceClass.md)): `Promise`\<`void`\> Defined in: src/source/source.ts:186 Adds a custom source type, making it available for use with [Map#addSource](../classes/Map.md#addsource). ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `name` | `string` | The name of the source type; source definition objects use this name in the `{type: ...}` field. | | `SourceType` | [`SourceClass`](../type-aliases/SourceClass.md) | A [SourceClass](../type-aliases/SourceClass.md) - which is a constructor for the `Source` interface. | ## Returns `Promise`\<`void`\> a promise that is resolved when the source type is ready or rejected with an error. --- # clearPrewarmedResources() https://docs.mapatlas.xyz/overview/API/functions/clearPrewarmedResources # clearPrewarmedResources() > **clearPrewarmedResources**(): `void` Defined in: src/util/global\_worker\_pool.ts:54 Clears up resources that have previously been created by `prewarm()`. Note that this is typically not necessary. You should only call this function if you expect the user of your app to not return to a Map view at any point in your application. ## Returns `void` ## Example ```ts clearPrewarmedResources() ``` --- # createTileMesh() https://docs.mapatlas.xyz/overview/API/functions/createTileMesh # createTileMesh() > **createTileMesh**(`options`: [`CreateTileMeshOptions`](../type-aliases/CreateTileMeshOptions.md), `forceIndicesSize?`: [`IndicesType`](../type-aliases/IndicesType.md)): [`TileMesh`](../type-aliases/TileMesh.md) Defined in: src/util/create\_tile\_mesh.ts:117 Creates a mesh of a quad that covers the entire tile (covering positions in range 0..EXTENT), is optionally subdivided into finer quads, optionally includes a border and optionally extends to the north and/or special pole vertices. Additionally the resulting mesh indices type can be specified using `forceIndicesSize`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `options` | [`CreateTileMeshOptions`](../type-aliases/CreateTileMeshOptions.md) | Specify options for tile mesh creation such as granularity or border. | | `forceIndicesSize?` | [`IndicesType`](../type-aliases/IndicesType.md) | Specifies what indices type to use. The values '32bit' and '16bit' force their respective indices size. If undefined, the mesh may use either size, and will pick 16 bit indices if possible. If '16bit' is specified and the mesh exceeds 65536 vertices, an exception is thrown. | ## Returns [`TileMesh`](../type-aliases/TileMesh.md) Typed arrays of the mesh vertices and indices. ## Example ``` // Creating a mesh for a tile that can be used for raster layers, hillshade, etc. const meshBuffers = createTileMesh({ granularity: map.style.projection.subdivisionGranularity.tile.getGranularityForZoomLevel(tileID.z), generateBorders: true, extendToNorthPole: tileID.y === 0, extendToSouthPole: tileID.y === (1 << tileID.z) - 1, }, '16bit'); ``` --- # getMaxParallelImageRequests() https://docs.mapatlas.xyz/overview/API/functions/getMaxParallelImageRequests # getMaxParallelImageRequests() > **getMaxParallelImageRequests**(): `number` Defined in: src/index.ts:123 Gets and sets the maximum number of images (raster tiles, sprites, icons) to load in parallel, which affects performance in raster-heavy maps. 16 by default. ## Returns `number` Number of parallel requests currently configured. ## Example ```ts getMaxParallelImageRequests(); ``` --- # getRTLTextPluginStatus() https://docs.mapatlas.xyz/overview/API/functions/getRTLTextPluginStatus # getRTLTextPluginStatus() > **getRTLTextPluginStatus**(): `string` Defined in: src/index.ts:82 Gets the map's [RTL text plugin](https://www.mapbox.com/mapbox-gl-js/plugins/#mapbox-gl-rtl-text) status. The status can be `unavailable` (i.e. not requested or removed), `loading`, `loaded` or `error`. If the status is `loaded` and the plugin is requested again, an error will be thrown. ## Returns `string` ## Example ```ts const pluginStatus = getRTLTextPluginStatus(); ``` --- # getVersion() https://docs.mapatlas.xyz/overview/API/functions/getVersion # getVersion() > **getVersion**(): `string` Defined in: src/index.ts:89 Returns the package version of the library ## Returns `string` Package version of the library --- # getWorkerCount() https://docs.mapatlas.xyz/overview/API/functions/getWorkerCount # getWorkerCount() > **getWorkerCount**(): `number` Defined in: src/index.ts:101 Gets the number of web workers instantiated on a page with GL JS maps. By default, workerCount is 1 except for Safari browser where it is set to half the number of CPU cores (capped at 3). Make sure to set this property before creating any map instances for it to have effect. ## Returns `number` Number of workers currently configured. ## Example ```ts const workerCount = getWorkerCount() ``` --- # getWorkerUrl() https://docs.mapatlas.xyz/overview/API/functions/getWorkerUrl # getWorkerUrl() > **getWorkerUrl**(): `string` Defined in: src/index.ts:138 Gets the worker url ## Returns `string` The worker url --- # importScriptInWorkers() https://docs.mapatlas.xyz/overview/API/functions/importScriptInWorkers # importScriptInWorkers() > **importScriptInWorkers**(`workerUrl`: `string`): `Promise`\<`void`[]\> Defined in: src/index.ts:174 Allows loading javascript code in the worker thread. *Note* that since this is using some very internal classes and flows it is considered experimental and can break at any point. It can be useful for the following examples: 1. Using `self.addProtocol` in the worker thread - note that you might need to also register the protocol on the main thread. 2. Using `self.registerWorkerSource(workerSource: WorkerSource)` to register a worker source, which should come with `addSourceType` usually. 3. using `self.actor.registerMessageHandler` to override some internal worker operations ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `workerUrl` | `string` | the worker url e.g. a url of a javascript file to load in the worker | ## Returns `Promise`\<`void`[]\> ## Example ```ts // below is an example of sending a js file to the worker to load the method there // Note that you'll need to call the global function `addProtocol` in the worker to register the protocol there. // add-protocol-worker.js async function loadFn(params, abortController) { const t = await fetch(`https://${params.url.split("://")[1]}`); if (t.status == 200) { const buffer = await t.arrayBuffer(); return {data: buffer} } else { throw new Error(`Tile fetch error: ${t.statusText}`); } } self.addProtocol('custom', loadFn); // main.js importScriptInWorkers('add-protocol-worker.js'); ``` --- # Functions https://docs.mapatlas.xyz/overview/API/functions/ # Functions This section contains all the functions available in the MapMetrics GL API. ## Core Functions - [addProtocol](./addProtocol.md) - Add a custom protocol for loading resources - [addSourceType](./addSourceType.md) - Add a custom source type - [clearPrewarmedResources](./clearPrewarmedResources.md) - Clear prewarmed resources - [createTileMesh](./createTileMesh.md) - Create a tile mesh for rendering - [getMaxParallelImageRequests](./getMaxParallelImageRequests.md) - Get the maximum number of parallel image requests - [getRTLTextPluginStatus](./getRTLTextPluginStatus.md) - Get the status of the RTL text plugin - [getVersion](./getVersion.md) - Get the current version of MapMetrics GL - [getWorkerCount](./getWorkerCount.md) - Get the number of worker threads - [getWorkerUrl](./getWorkerUrl.md) - Get the URL for worker scripts - [importScriptInWorkers](./importScriptInWorkers.md) - Import a script in worker threads - [prewarm](./prewarm.md) - Prewarm resources for better performance - [removeProtocol](./removeProtocol.md) - Remove a custom protocol - [setMaxParallelImageRequests](./setMaxParallelImageRequests.md) - Set the maximum number of parallel image requests - [setRTLTextPlugin](./setRTLTextPlugin.md) - Set the RTL text plugin - [setWorkerCount](./setWorkerCount.md) - Set the number of worker threads - [setWorkerUrl](./setWorkerUrl.md) - Set the URL for worker scripts --- # prewarm() https://docs.mapatlas.xyz/overview/API/functions/prewarm # prewarm() > **prewarm**(): `void` Defined in: src/util/global\_worker\_pool.ts:38 Initializes resources like WebWorkers that can be shared across maps to lower load times in some situations. `setWorkerUrl()` and `setWorkerCount()`, if being used, must be set before `prewarm()` is called to have an effect. By default, the lifecycle of these resources is managed automatically, and they are lazily initialized when a Map is first created. By invoking `prewarm()`, these resources will be created ahead of time, and will not be cleared when the last Map is removed from the page. This allows them to be re-used by new Map instances that are created later. They can be manually cleared by calling `clearPrewarmedResources()`. This is only necessary if your web page remains active but stops using maps altogether. This is primarily useful when using GL-JS maps in a single page app, wherein a user would navigate between various views that can cause Map instances to constantly be created and destroyed. ## Returns `void` ## Example ```ts prewarm() ``` --- # removeProtocol() https://docs.mapatlas.xyz/overview/API/functions/removeProtocol # removeProtocol() > **removeProtocol**(`customProtocol`: `string`): `void` Defined in: src/source/protocol\_crud.ts:46 Removes a previously added protocol in the main thread. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `customProtocol` | `string` | the custom protocol to remove registration for | ## Returns `void` ## Example ```ts removeProtocol('custom'); ``` --- # setMaxParallelImageRequests() https://docs.mapatlas.xyz/overview/API/functions/setMaxParallelImageRequests # setMaxParallelImageRequests() > **setMaxParallelImageRequests**(`numRequests`: `number`): `void` Defined in: src/index.ts:133 Sets the maximum number of images (raster tiles, sprites, icons) to load in parallel, which affects performance in raster-heavy maps. 16 by default. ## Parameters | Parameter | Type | | ------ | ------ | | `numRequests` | `number` | ## Returns `void` ## Example ```ts setMaxParallelImageRequests(10); ``` --- # setRTLTextPlugin() https://docs.mapatlas.xyz/overview/API/functions/setRTLTextPlugin # setRTLTextPlugin() > **setRTLTextPlugin**(`pluginURL`: `string`, `lazy`: `boolean`): `Promise`\<`void`\> Defined in: src/index.ts:69 Sets the map's [RTL text plugin](https://www.mapbox.com/mapbox-gl-js/plugins/#mapbox-gl-rtl-text). Necessary for supporting the Arabic and Hebrew languages, which are written right-to-left. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `pluginURL` | `string` | URL pointing to the Mapbox RTL text plugin source. | | `lazy` | `boolean` | If set to `true`, mapmetrics will defer loading the plugin until rtl text is encountered, rtl text will then be rendered only after the plugin finishes loading. | ## Returns `Promise`\<`void`\> ## Example ```ts setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.3.0/dist/mapbox-gl-rtl-text.js', false); ``` ## See [Add support for right-to-left scripts](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/mapbox-gl-rtl-text/) --- # setWorkerCount() https://docs.mapatlas.xyz/overview/API/functions/setWorkerCount # setWorkerCount() > **setWorkerCount**(`count`: `number`): `void` Defined in: src/index.ts:112 Sets the number of web workers instantiated on a page with GL JS maps. By default, workerCount is 1 except for Safari browser where it is set to half the number of CPU cores (capped at 3). Make sure to set this property before creating any map instances for it to have effect. ## Parameters | Parameter | Type | | ------ | ------ | | `count` | `number` | ## Returns `void` ## Example ```ts setWorkerCount(2); ``` --- # setWorkerUrl() https://docs.mapatlas.xyz/overview/API/functions/setWorkerUrl # setWorkerUrl() > **setWorkerUrl**(`value`: `string`): `void` Defined in: src/index.ts:142 Sets the worker url ## Parameters | Parameter | Type | | ------ | ------ | | `value` | `string` | ## Returns `void` --- # ActorTarget https://docs.mapatlas.xyz/overview/API/interfaces/ActorTarget # ActorTarget Defined in: src/util/actor.ts:13 An interface to be sent to the actor in order for it to allow communication between the worker and the main thread --- # AttributeBinder https://docs.mapatlas.xyz/overview/API/interfaces/AttributeBinder # AttributeBinder Defined in: src/data/program\_configuration.ts:70 `Binder` is the interface definition for the strategies for constructing, uploading, and binding paint property data as GLSL attributes. Most style- spec properties have a 1:1 relationship to shader attribute/uniforms, but some require multiple values per feature to be passed to the GPU, and in those cases we bind multiple attributes/uniforms. It has three implementations, one for each of the three strategies we use: * For _constant_ properties -- those whose value is a constant, or the constant result of evaluating a camera expression at a particular camera position -- we don't need a vertex attribute buffer, and instead use a uniform. * For data expressions, we use a vertex buffer with a single attribute value, the evaluated result of the source function for the given feature. * For composite expressions, we use a vertex buffer with two attributes: min and max values covering the range of zooms at which we expect the tile to be displayed. These values are calculated by evaluating the composite expression for the given feature at strategically chosen zoom levels. In addition to this attribute data, we also use a uniform value which the shader uses to interpolate between the min and max value at the final displayed zoom level. The use of a uniform allows us to cheaply update the value on every frame. Note that the shader source varies depending on whether we're using a uniform or attribute. We dynamically compile shaders at runtime to accommodate this. --- # Bucket https://docs.mapatlas.xyz/overview/API/interfaces/Bucket # Bucket Defined in: src/data/bucket.ts:79 The `Bucket` interface is the single point of knowledge about turning vector tiles into WebGL buffers. `Bucket` is an abstract interface. An implementation exists for each style layer type. Create a bucket via the `StyleLayer#createBucket` method. The concrete bucket types, using layout options from the style layer, transform feature geometries into vertex and index data for use by the vertex shader. They also (via `ProgramConfiguration`) use feature properties and the zoom level to populate the attributes needed for data-driven styling. Buckets are designed to be built on a worker thread and then serialized and transferred back to the main thread for rendering. On the worker side, a bucket's vertex, index, and attribute data is stored in `bucket.arrays: ArrayGroup`. When a bucket's data is serialized and sent back to the main thread, is gets deserialized (using `new Bucket(serializedBucketData)`, with the array data now stored in `bucket.buffers: BufferGroup`. BufferGroups hold the same data as ArrayGroups, but are tuned for consumption by WebGL. ## Methods ### destroy() > **destroy**(): `void` Defined in: src/data/bucket.ts:95 Release the WebGL resources associated with the buffers. Note that because buckets are shared between layers having the same layout properties, they must be destroyed in groups (all buckets for a tile, or all symbol buckets). #### Returns `void` --- # CustomLayerInterface https://docs.mapatlas.xyz/overview/API/interfaces/CustomLayerInterface # CustomLayerInterface Defined in: src/style/style\_layer/custom\_style\_layer.ts:188 Interface for custom style layers. This is a specification for implementers to model: it is not an exported method or class. Custom layers allow a user to render directly into the map's GL context using the map's camera. These layers can be added between any regular layers using [Map#addLayer](../classes/Map.md#addlayer). Custom layers must have a unique `id` and must have the `type` of `"custom"`. They must implement `render` and may implement `prerender`, `onAdd` and `onRemove`. They can trigger rendering using [Map#triggerRepaint](../classes/Map.md#triggerrepaint) and they should appropriately handle [MapContextEvent](../type-aliases/MapContextEvent.md) with `webglcontextlost` and `webglcontextrestored`. The `renderingMode` property controls whether the layer is treated as a `"2d"` or `"3d"` map layer. Use: - `"renderingMode": "3d"` to use the depth buffer and share it with other layers - `"renderingMode": "2d"` to add a layer with no depth. If you need to use the depth buffer for a `"2d"` layer you must use an offscreen framebuffer and [CustomLayerInterface#prerender](#prerender) ## Example Custom layer implemented as ES6 class ```ts class NullIslandLayer { constructor() { this.id = 'null-island'; this.type = 'custom'; this.renderingMode = '2d'; } onAdd(map: mapmetricsgl.Map, gl: WebGLRenderingContext | WebGL2RenderingContext) { const vertexSource = ` uniform mat4 u_matrix; void main() { gl_Position = u_matrix * vec4(0.5, 0.5, 0.0, 1.0); gl_PointSize = 20.0; }`; const fragmentSource = ` void main() { fragColor = vec4(1.0, 0.0, 0.0, 1.0); }`; const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSource); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSource); gl.compileShader(fragmentShader); this.program = gl.createProgram(); gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, fragmentShader); gl.linkProgram(this.program); } render({ gl, modelViewProjectionMatrix: matrix }: { gl: WebGLRenderingContext | WebGL2RenderingContext; modelViewProjectionMatrix: Float32Array; }) { gl.useProgram(this.program); gl.uniformMatrix4fv(gl.getUniformLocation(this.program, "u_matrix"), false, matrix); gl.drawArrays(gl.POINTS, 0, 1); } } map.on('load', () => { map.addLayer(new NullIslandLayer()); }); ``` ## Methods ### onAdd()? > `optional` **onAdd**(`map`: [`Map`](../classes/Map.md), `gl`: `WebGLRenderingContext` \| `WebGL2RenderingContext`): `void` Defined in: src/style/style\_layer/custom\_style\_layer.ts:231 Optional method called when the layer has been added to the Map with [Map#addLayer](../classes/Map.md#addlayer). This gives the layer a chance to initialize gl resources and register event listeners. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | The Map this custom layer was just added to. | | `gl` | `WebGLRenderingContext` \| `WebGL2RenderingContext` | The gl context for the map. | #### Returns `void` *** ### onRemove()? > `optional` **onRemove**(`map`: [`Map`](../classes/Map.md), `gl`: `WebGLRenderingContext` \| `WebGL2RenderingContext`): `void` Defined in: src/style/style\_layer/custom\_style\_layer.ts:239 Optional method called when the layer has been removed from the Map with [Map#removeLayer](../classes/Map.md#removelayer). This gives the layer a chance to clean up gl resources and event listeners. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | The Map this custom layer was just added to. | | `gl` | `WebGLRenderingContext` \| `WebGL2RenderingContext` | The gl context for the map. | #### Returns `void` ## Properties ### id > **id**: `string` Defined in: src/style/style\_layer/custom\_style\_layer.ts:192 A unique layer id. *** ### prerender? > `optional` **prerender**: [`CustomRenderMethod`](../type-aliases/CustomRenderMethod.md) Defined in: src/style/style\_layer/custom\_style\_layer.ts:223 Optional method called during a render frame to allow a layer to prepare resources or render into a texture. The layer cannot make any assumptions about the current GL state and must bind a framebuffer before rendering. *** ### render > **render**: [`CustomRenderMethod`](../type-aliases/CustomRenderMethod.md) Defined in: src/style/style\_layer/custom\_style\_layer.ts:217 Called during a render frame allowing the layer to draw into the GL context. The layer can assume blending and depth state is set to allow the layer to properly blend and clip other layers. The layer cannot make any other assumptions about the current GL state. If the layer needs to render to a texture, it should implement the `prerender` method to do this and only use the `render` method for drawing directly into the main framebuffer. The blend function is set to `gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)`. This expects colors to be provided in premultiplied alpha form where the `r`, `g` and `b` values are already multiplied by the `a` value. If you are unable to provide colors in premultiplied form you may want to change the blend function to `gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA)`. *** ### renderingMode? > `optional` **renderingMode**: `"2d"` \| `"3d"` Defined in: src/style/style\_layer/custom\_style\_layer.ts:200 Either `"2d"` or `"3d"`. Defaults to `"2d"`. *** ### type > **type**: `"custom"` Defined in: src/style/style\_layer/custom\_style\_layer.ts:196 The layer's type. Must be `"custom"`. --- # Handler https://docs.mapatlas.xyz/overview/API/interfaces/Handler # Handler Defined in: src/ui/handler\_manager.ts:40 Handlers interpret dom events and return camera changes that should be applied to the map (`HandlerResult`s). The camera changes are all deltas. The handler itself should have no knowledge of the map's current state. This makes it easier to merge multiple results and keeps handlers simpler. For example, if there is a mousedown and mousemove, the mousePan handler would return a `panDelta` on the mousemove. ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler\_manager.ts:49 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler\_manager.ts:53 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` ## Properties ### renderFrame()? > `readonly` `optional` **renderFrame**: () => `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) Defined in: src/ui/handler\_manager.ts:75 `renderFrame` is the only non-dom event. It is called during render frames and can be used to smooth camera changes (see scroll handler). #### Returns `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) --- # IActor https://docs.mapatlas.xyz/overview/API/interfaces/IActor # IActor Defined in: src/util/actor.ts:42 This interface allowing to substitute only the sendAsync method of the Actor class. --- # IControl https://docs.mapatlas.xyz/overview/API/interfaces/IControl # IControl Defined in: src/ui/control/control.ts:37 Interface for interactive controls added to the map. This is a specification for implementers to model: it is not an exported method or class. Controls must implement `onAdd` and `onRemove`, and must own an element, which is often a `div` element. To use mapmetrics GL JS's default control styling, add the `mapmetricsgl-ctrl` class to your control's node. ## Example ```ts class HelloWorldControl: IControl { onAdd(map) { this._map = map; this._container = document.createElement('div'); this._container.className = 'mapmetricsgl-ctrl'; this._container.textContent = 'Hello, world'; return this._container; } onRemove() { this._container.parentNode.removeChild(this._container); this._map = undefined; } } ``` ## Methods ### onAdd() > **onAdd**(`map`: [`Map`](../classes/Map.md)): `HTMLElement` Defined in: src/ui/control/control.ts:49 Register a control on the map and give it a chance to register event listeners and resources. This method is called by [Map#addControl](../classes/Map.md#addcontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | the Map this control will be added to | #### Returns `HTMLElement` The control's container element. This should be created by the control and returned by onAdd without being attached to the DOM: the map will insert the control's element into the DOM as necessary. *** ### onRemove() > **onRemove**(`map`: [`Map`](../classes/Map.md)): `void` Defined in: src/ui/control/control.ts:57 Unregister a control on the map and give it a chance to detach event listeners and resources. This method is called by [Map#removeControl](../classes/Map.md#removecontrol) internally. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | the Map this control will be removed from | #### Returns `void` ## Properties ### getDefaultPosition()? > `readonly` `optional` **getDefaultPosition**: () => [`ControlPosition`](../type-aliases/ControlPosition.md) Defined in: src/ui/control/control.ts:66 Optionally provide a default position for this control. If this method is implemented and [Map#addControl](../classes/Map.md#addcontrol) is called without the `position` parameter, the value returned by getDefaultPosition will be used as the control's position. #### Returns [`ControlPosition`](../type-aliases/ControlPosition.md) a control position, one of the values valid in addControl. --- # MousePanHandler https://docs.mapatlas.xyz/overview/API/interfaces/MousePanHandler # MousePanHandler Defined in: src/ui/handler/mouse.ts:11 `MousePanHandler` allows the user to pan the map by clicking and dragging ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler\_manager.ts:49 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Inherited from `DragMoveHandler.isActive` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler\_manager.ts:53 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Inherited from `DragMoveHandler.reset` ## Properties ### renderFrame()? > `readonly` `optional` **renderFrame**: () => `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) Defined in: src/ui/handler\_manager.ts:75 `renderFrame` is the only non-dom event. It is called during render frames and can be used to smooth camera changes (see scroll handler). #### Returns `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) #### Inherited from `DragMoveHandler.renderFrame` --- # MousePitchHandler https://docs.mapatlas.xyz/overview/API/interfaces/MousePitchHandler # MousePitchHandler Defined in: src/ui/handler/mouse.ts:19 `MousePitchHandler` allows the user to zoom the map by pitching ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler\_manager.ts:49 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Inherited from `DragMoveHandler.isActive` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler\_manager.ts:53 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Inherited from `DragMoveHandler.reset` ## Properties ### renderFrame()? > `readonly` `optional` **renderFrame**: () => `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) Defined in: src/ui/handler\_manager.ts:75 `renderFrame` is the only non-dom event. It is called during render frames and can be used to smooth camera changes (see scroll handler). #### Returns `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) #### Inherited from `DragMoveHandler.renderFrame` --- # MouseRollHandler https://docs.mapatlas.xyz/overview/API/interfaces/MouseRollHandler # MouseRollHandler Defined in: src/ui/handler/mouse.ts:23 `MouseRollHandler` allows the user to roll the camera by holding `Ctrl`, right-clicking and dragging ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler\_manager.ts:49 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Inherited from `DragMoveHandler.isActive` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler\_manager.ts:53 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Inherited from `DragMoveHandler.reset` ## Properties ### renderFrame()? > `readonly` `optional` **renderFrame**: () => `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) Defined in: src/ui/handler\_manager.ts:75 `renderFrame` is the only non-dom event. It is called during render frames and can be used to smooth camera changes (see scroll handler). #### Returns `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) #### Inherited from `DragMoveHandler.renderFrame` --- # MouseRotateHandler https://docs.mapatlas.xyz/overview/API/interfaces/MouseRotateHandler # MouseRotateHandler Defined in: src/ui/handler/mouse.ts:15 `MouseRotateHandler` allows the user to rotate the map by clicking and dragging ## Methods ### isActive() > **isActive**(): `boolean` Defined in: src/ui/handler\_manager.ts:49 This is used to indicate if the handler is currently active or not. In case a handler is active, it will block other handlers from getting the relevant events. There is an allow list of handlers that can be active at the same time, which is configured when adding a handler. #### Returns `boolean` #### Inherited from `DragMoveHandler.isActive` *** ### reset() > **reset**(): `void` Defined in: src/ui/handler\_manager.ts:53 `reset` can be called by the manager at any time and must reset everything to it's original state #### Returns `void` #### Inherited from `DragMoveHandler.reset` ## Properties ### renderFrame()? > `readonly` `optional` **renderFrame**: () => `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) Defined in: src/ui/handler\_manager.ts:75 `renderFrame` is the only non-dom event. It is called during render frames and can be used to smooth camera changes (see scroll handler). #### Returns `void` \| [`HandlerResult`](../type-aliases/HandlerResult.md) #### Inherited from `DragMoveHandler.renderFrame` --- # Projection https://docs.mapatlas.xyz/overview/API/interfaces/Projection # Projection Defined in: src/geo/projection/projection.ts:45 An interface the implementations of which are used internally by mapmetrics to handle different projections. ## Accessors ### shaderDefine #### Get Signature > **get** **shaderDefine**(): `string` Defined in: src/geo/projection/projection.ts:72 A `#define` macro that is injected into every mapmetrics shader that uses this projection. ##### Example ```ts `const define = projection.shaderDefine; // '#define GLOBE'` ``` ##### Returns `string` *** ### shaderVariantName #### Get Signature > **get** **shaderVariantName**(): `string` Defined in: src/geo/projection/projection.ts:65 Name of the shader projection variant that should be used for this projection. Note that this value may change dynamically, for example when globe projection internally transitions to mercator. Then globe projection might start reporting the mercator shader variant name to make mapmetrics use faster mercator shaders. ##### Returns `string` *** ### vertexShaderPreludeCode #### Get Signature > **get** **vertexShaderPreludeCode**(): `string` Defined in: src/geo/projection/projection.ts:83 Vertex shader code that is injected into every mapmetrics vertex shader that uses this projection. ##### Returns `string` --- # Source https://docs.mapatlas.xyz/overview/API/interfaces/Source # Source Defined in: src/source/source.ts:30 The `Source` interface must be implemented by each source type, including "core" types (`vector`, `raster`, `video`, etc.) and all custom, third-party types. **Event** `data` - Fired with `{dataType: 'source', sourceDataType: 'metadata'}` to indicate that any necessary metadata has been loaded so that it's okay to call `loadTile`; and with `{dataType: 'source', sourceDataType: 'content'}` to indicate that the source data has changed, so that any current caches should be flushed. ## Methods ### abortTile()? > `optional` **abortTile**(`tile`: [`Tile`](../classes/Tile.md)): `Promise`\<`void`\> Defined in: src/source/source.ts:104 Allows to abort a tile loading. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](../classes/Tile.md) | The tile to abort | #### Returns `Promise`\<`void`\> *** ### fire() > **fire**(`event`: [`Event`](../classes/Event.md)): `unknown` Defined in: src/source/source.ts:78 An ability to fire an event to all the listeners, see [Evented](../classes/Evented.md) #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `event` | [`Event`](../classes/Event.md) | The event to fire | #### Returns `unknown` *** ### hasTile()? > `optional` **hasTile**(`tileID`: [`OverscaledTileID`](../classes/OverscaledTileID.md)): `boolean` Defined in: src/source/source.ts:99 True is the tile is part of the source, false otherwise. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tileID` | [`OverscaledTileID`](../classes/OverscaledTileID.md) | The tile ID | #### Returns `boolean` *** ### hasTransition() > **hasTransition**(): `boolean` Defined in: src/source/source.ts:69 True if the source has transition, false otherwise. #### Returns `boolean` *** ### loaded() > **loaded**(): `boolean` Defined in: src/source/source.ts:73 True if the source is loaded, false otherwise. #### Returns `boolean` *** ### loadTile() > **loadTile**(`tile`: [`Tile`](../classes/Tile.md)): `Promise`\<`void`\> Defined in: src/source/source.ts:94 This method does the heavy lifting of loading a tile. In most cases it will defer the work to the relevant worker source. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](../classes/Tile.md) | The tile to load | #### Returns `Promise`\<`void`\> *** ### onAdd()? > `optional` **onAdd**(`map`: [`Map`](../classes/Map.md)): `void` Defined in: src/source/source.ts:83 This method is called when the source is added to the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | The map instance | #### Returns `void` *** ### onRemove()? > `optional` **onRemove**(`map`: [`Map`](../classes/Map.md)): `void` Defined in: src/source/source.ts:88 This method is called when the source is removed from the map. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | The map instance | #### Returns `void` *** ### prepare()? > `optional` **prepare**(): `void` Defined in: src/source/source.ts:119 Allows to execute a prepare step before the source is used. #### Returns `void` *** ### serialize() > **serialize**(): `any` Defined in: src/source/source.ts:115 #### Returns `any` A plain (stringifiable) JS object representing the current state of the source. Creating a source using the returned object as the `options` should result in a Source that is equivalent to this one. *** ### unloadTile()? > `optional` **unloadTile**(`tile`: [`Tile`](../classes/Tile.md)): `Promise`\<`void`\> Defined in: src/source/source.ts:109 Allows to unload a tile. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `tile` | [`Tile`](../classes/Tile.md) | The tile to unload | #### Returns `Promise`\<`void`\> ## Properties ### attribution? > `optional` **attribution**: `string` Defined in: src/source/source.ts:51 The attribution for the source. *** ### calculateTileZoom? > `optional` **calculateTileZoom**: [`CalculateTileZoomFunction`](../type-aliases/CalculateTileZoomFunction.md) Defined in: src/source/source.ts:123 Optional function to redefine how tiles are loaded at high pitch angles. *** ### id > **id**: `string` Defined in: src/source/source.ts:35 The id for the source. Must not be used by any existing source. *** ### isTileClipped? > `optional` **isTileClipped**: `boolean` Defined in: src/source/source.ts:59 `false` if tiles can be drawn outside their boundaries, `true` if they cannot. *** ### maxzoom > **maxzoom**: `number` Defined in: src/source/source.ts:43 The maximum zoom level for the source. *** ### minzoom > **minzoom**: `number` Defined in: src/source/source.ts:39 The minimum zoom level for the source. *** ### reparseOverscaled? > `optional` **reparseOverscaled**: `boolean` Defined in: src/source/source.ts:64 `true` if tiles should be sent back to the worker for each overzoomed zoom level, `false` if not. *** ### roundZoom? > `optional` **roundZoom**: `boolean` Defined in: src/source/source.ts:55 `true` if zoom levels are rounded to the nearest integer in the source data, `false` if they are floor-ed to the nearest integer. *** ### tileSize > **tileSize**: `number` Defined in: src/source/source.ts:47 The tile size for the source. --- # StyleImageInterface https://docs.mapatlas.xyz/overview/API/interfaces/StyleImageInterface # StyleImageInterface Defined in: src/style/style\_image.ts:148 Interface for dynamically generated style images. This is a specification for implementers to model: it is not an exported method or class. Images implementing this interface can be redrawn for every frame. They can be used to animate icons and patterns or make them respond to user input. Style images can implement a [StyleImageInterface#render](#render) method. The method is called every frame and can be used to update the image. ## See [Add an animated icon to the map.](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image-animated/) ## Example ```ts let flashingSquare = { width: 64, height: 64, data: new Uint8Array(64 * 64 * 4), onAdd: function(map) { this.map = map; }, render: function() { // keep repainting while the icon is on the map this.map.triggerRepaint(); // alternate between black and white based on the time let value = Math.round(Date.now() / 1000) % 2 === 0 ? 255 : 0; // check if image needs to be changed if (value !== this.previousValue) { this.previousValue = value; let bytesPerPixel = 4; for (let x = 0; x < this.width; x++) { for (let y = 0; y < this.height; y++) { let offset = (y * this.width + x) * bytesPerPixel; this.data[offset + 0] = value; this.data[offset + 1] = value; this.data[offset + 2] = value; this.data[offset + 3] = 255; } } // return true to indicate that the image changed return true; } } } map.addImage('flashing_square', flashingSquare); ``` ## Properties ### onAdd()? > `optional` **onAdd**: (`map`: [`Map`](../classes/Map.md), `id`: `string`) => `void` Defined in: src/style/style\_image.ts:170 Optional method called when the layer has been added to the Map with [Map#addImage](../classes/Map.md#addimage). #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | The Map this custom layer was just added to. | | `id` | `string` | - | #### Returns `void` *** ### onRemove()? > `optional` **onRemove**: () => `void` Defined in: src/style/style\_image.ts:175 Optional method called when the icon is removed from the map with [Map#removeImage](../classes/Map.md#removeimage). This gives the image a chance to clean up resources and event listeners. #### Returns `void` *** ### render()? > `optional` **render**: () => `boolean` Defined in: src/style/style\_image.ts:164 This method is called once before every frame where the icon will be used. The method can optionally update the image's `data` member with a new image. If the method updates the image it must return `true` to commit the change. If the method returns `false` or nothing the image is assumed to not have changed. If updates are infrequent it maybe easier to use [Map#updateImage](../classes/Map.md#updateimage) to update the image instead of implementing this method. #### Returns `boolean` `true` if this method updated the image. `false` if the image was not changed. --- # Subscription https://docs.mapatlas.xyz/overview/API/interfaces/Subscription # Subscription Defined in: src/util/util.ts:914 Allows to unsubscribe from events without the need to store the method reference. ## Methods ### unsubscribe() > **unsubscribe**(): `void` Defined in: src/util/util.ts:918 Unsubscribes from the event. #### Returns `void` --- # Interfaces https://docs.mapatlas.xyz/overview/API/interfaces/ # Interfaces This section contains all the interfaces available in the MapMetrics GL API. ## Core Interfaces - [ActorTarget](./ActorTarget.md) - Target for actor communication - [AttributeBinder](./AttributeBinder.md) - Binds attributes for rendering - [Bucket](./Bucket.md) - Bucket for storing rendering data - [CustomLayerInterface](./CustomLayerInterface.md) - Interface for custom layers - [Handler](./Handler.md) - Base interface for event handlers - [IActor](./IActor.md) - Actor interface - [IControl](./IControl.md) - Interface for map controls - [MousePanHandler](./MousePanHandler.md) - Handler for mouse pan interactions - [MousePitchHandler](./MousePitchHandler.md) - Handler for mouse pitch interactions - [MouseRollHandler](./MouseRollHandler.md) - Handler for mouse roll interactions - [MouseRotateHandler](./MouseRotateHandler.md) - Handler for mouse rotate interactions - [Projection](./Projection.md) - Interface for map projections - [Source](./Source.md) - Interface for data sources - [StyleImageInterface](./StyleImageInterface.md) - Interface for style images - [Subscription](./Subscription.md) - Interface for event subscriptions --- # ActorMessage\ https://docs.mapatlas.xyz/overview/API/type-aliases/ActorMessage # ActorMessage\ > **ActorMessage**\<`T`\> = `object` Defined in: src/util/actor\_messages.ts:143 The message to be sent by the actor ## Type Parameters | Type Parameter | | ------ | | `T` *extends* [`MessageType`](../enumerations/MessageType.md) | --- # AddLayerObject https://docs.mapatlas.xyz/overview/API/type-aliases/AddLayerObject # AddLayerObject > **AddLayerObject** = [`LayerSpecification`](https://mapmetrics.org/mapmetrics-style-spec/layers/) \| `Omit`\<[`LayerSpecification`](https://mapmetrics.org/mapmetrics-style-spec/layers/), `"source"`\> & `object` \| [`CustomLayerInterface`](../interfaces/CustomLayerInterface.md) Defined in: src/style/style.ts:196 Specifies a layer to be added to a [Style](../classes/Style.md). In addition to a standard [LayerSpecification](https://mapmetrics.org/mapmetrics-style-spec/layers/) or a [CustomLayerInterface](../interfaces/CustomLayerInterface.md), a [LayerSpecification](https://mapmetrics.org/mapmetrics-style-spec/layers/) with an embedded [SourceSpecification](https://mapmetrics.org/mapmetrics-style-spec/sources/) can also be provided. --- # AddProtocolAction() https://docs.mapatlas.xyz/overview/API/type-aliases/AddProtocolAction # AddProtocolAction() > **AddProtocolAction** = (`requestParameters`: [`RequestParameters`](RequestParameters.md), `abortController`: `AbortController`) => `Promise`\<[`GetResourceResponse`](GetResourceResponse.md)\<`any`\>\> Defined in: src/util/config.ts:8 This method type is used to register a protocol handler. Use the abort controller for aborting requests. Return a promise with the relevant resource response. ## Parameters | Parameter | Type | | ------ | ------ | | `requestParameters` | [`RequestParameters`](RequestParameters.md) | | `abortController` | `AbortController` | ## Returns `Promise`\<[`GetResourceResponse`](GetResourceResponse.md)\<`any`\>\> --- # Alignment https://docs.mapatlas.xyz/overview/API/type-aliases/Alignment # Alignment > **Alignment** = `"map"` \| `"viewport"` \| `"auto"` Defined in: src/ui/marker.ts:18 Alignment options of rotation and pitch --- # AnimationOptions https://docs.mapatlas.xyz/overview/API/type-aliases/AnimationOptions # AnimationOptions > **AnimationOptions** = `object` Defined in: src/ui/camera.ts:205 Options common to map movement methods that involve animation, such as [Map#panBy](../classes/Map.md#panby) and [Map#easeTo](../classes/Map.md#easeto), controlling the duration and easing function of the animation. All properties are optional. ## Properties ### animate? > `optional` **animate**: `boolean` Defined in: src/ui/camera.ts:222 If `false`, no animation will occur. *** ### duration? > `optional` **duration**: `number` Defined in: src/ui/camera.ts:209 The animation's duration, measured in milliseconds. *** ### easing()? > `optional` **easing**: (`_`: `number`) => `number` Defined in: src/ui/camera.ts:214 A function taking a time in the range 0..1 and returning a number where 0 is the initial state and 1 is the final state. #### Parameters | Parameter | Type | | ------ | ------ | | `_` | `number` | #### Returns `number` *** ### essential? > `optional` **essential**: `boolean` Defined in: src/ui/camera.ts:227 If `true`, then the animation is considered essential and will not be affected by [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion). *** ### freezeElevation? > `optional` **freezeElevation**: `boolean` Defined in: src/ui/camera.ts:233 Default false. Needed in 3D maps to let the camera stay in a constant height based on sea-level. After the animation finished the zoom-level will be recalculated in respect of the distance from the camera to the center-coordinate-altitude. *** ### offset? > `optional` **offset**: [`PointLike`](PointLike.md) Defined in: src/ui/camera.ts:218 of the target center relative to real map container center at the end of animation. --- # AroundCenterOptions https://docs.mapatlas.xyz/overview/API/type-aliases/AroundCenterOptions # AroundCenterOptions > **AroundCenterOptions** = `object` Defined in: src/ui/handler/two\_fingers\_touch.ts:9 An options object sent to the enable function of some of the handlers ## Properties ### around > **around**: `"center"` Defined in: src/ui/handler/two\_fingers\_touch.ts:13 If "center" is passed, map will zoom around the center of map --- # AttributionControlOptions https://docs.mapatlas.xyz/overview/API/type-aliases/AttributionControlOptions # AttributionControlOptions > **AttributionControlOptions** = `object` Defined in: src/ui/control/attribution\_control.ts:10 The [AttributionControl](../classes/AttributionControl.md) options object ## Properties ### compact? > `optional` **compact**: `boolean` Defined in: src/ui/control/attribution\_control.ts:16 If `true`, the attribution control will always collapse when moving the map. If `false`, force the expanded attribution control. The default is a responsive attribution that collapses when the user moves the map on maps less than 640 pixels wide. **Attribution should not be collapsed if it can comfortably fit on the map. `compact` should only be used to modify default attribution when map size makes it impossible to fit default attribution and when the automatic compact resizing for default settings are not sufficient.** *** ### customAttribution? > `optional` **customAttribution**: `string` \| `string`[] Defined in: src/ui/control/attribution\_control.ts:20 Attributions to show in addition to any other attributions. --- # CalculateTileZoomFunction() https://docs.mapatlas.xyz/overview/API/type-aliases/CalculateTileZoomFunction # CalculateTileZoomFunction() > **CalculateTileZoomFunction** = (`requestedCenterZoom`: `number`, `distanceToTile2D`: `number`, `distanceToTileZ`: `number`, `distanceToCenter3D`: `number`, `cameraVerticalFOV`: `number`) => `number` Defined in: src/geo/projection/covering\_tiles.ts:72 Function to define how tiles are loaded at high pitch angles ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `requestedCenterZoom` | `number` | the requested zoom level, valid at the center point. | | `distanceToTile2D` | `number` | 2D distance from the camera to the candidate tile, in mercator units. | | `distanceToTileZ` | `number` | vertical distance from the camera to the candidate tile, in mercator units. | | `distanceToCenter3D` | `number` | distance from camera to center point, in mercator units | | `cameraVerticalFOV` | `number` | camera vertical field of view, in degrees | ## Returns `number` the desired zoom level for this tile. May not be an integer. --- # CameraForBoundsOptions https://docs.mapatlas.xyz/overview/API/type-aliases/CameraForBoundsOptions # CameraForBoundsOptions > **CameraForBoundsOptions** = [`CameraOptions`](CameraOptions.md) & `object` Defined in: src/ui/camera.ts:104 A options object for the [Map#cameraForBounds](../classes/Map.md#cameraforbounds) method ## Type declaration ### maxZoom? > `optional` **maxZoom**: `number` The maximum zoom level to allow when the camera would transition to the specified bounds. ### offset? > `optional` **offset**: [`PointLike`](PointLike.md) The center of the given bounds relative to the map's center, measured in pixels. #### Default Value ```ts [0, 0] ``` ### padding? > `optional` **padding**: `number` \| [`PaddingOptions`](PaddingOptions.md) The amount of padding in pixels to add to the given bounds. --- # CameraOptions https://docs.mapatlas.xyz/overview/API/type-aliases/CameraOptions # CameraOptions > **CameraOptions** = [`CenterZoomBearing`](CenterZoomBearing.md) & `object` Defined in: src/ui/camera.ts:54 Options common to [Map#jumpTo](../classes/Map.md#jumpto), [Map#easeTo](../classes/Map.md#easeto), and [Map#flyTo](../classes/Map.md#flyto), controlling the desired location, zoom, bearing, pitch, and roll of the camera. All properties are optional, and when a property is omitted, the current camera value for that property will remain unchanged. ## Type declaration ### elevation? > `optional` **elevation**: `number` The elevation of the center point in meters above sea level. ### pitch? > `optional` **pitch**: `number` The desired pitch in degrees. The pitch is the angle towards the horizon measured in degrees with a range between 0 and 60 degrees. For example, pitch: 0 provides the appearance of looking straight down at the map, while pitch: 60 tilts the user's perspective towards the horizon. Increasing the pitch value is often used to display 3D objects. ### roll? > `optional` **roll**: `number` The desired roll in degrees. The roll is the angle about the camera boresight. ## Example Set the map's initial perspective with CameraOptions ```ts let map = new Map({ container: 'map', style: 'https://demotiles.mapmetrics.org/style.json', center: [-73.5804, 45.53483], pitch: 60, bearing: -60, zoom: 10 }); ``` ## See - [Set pitch and bearing](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/set-perspective/) - [Jump to a series of locations](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/jump-to/) - [Fly to a location](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/flyto/) - [Display buildings in 3D](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/3d-buildings/) --- # CameraUpdateTransformFunction() https://docs.mapatlas.xyz/overview/API/type-aliases/CameraUpdateTransformFunction # CameraUpdateTransformFunction() > **CameraUpdateTransformFunction** = (`next`: `object`) => `object` Defined in: src/ui/camera.ts:239 A callback hook that allows manipulating the camera and being notified about camera updates before they happen ## Parameters | Parameter | Type | | ------ | ------ | | `next` | \{ `bearing`: `number`; `center`: [`LngLat`](../classes/LngLat.md); `elevation`: `number`; `pitch`: `number`; `roll`: `number`; `zoom`: `number`; \} | | `next.bearing` | `number` | | `next.center` | [`LngLat`](../classes/LngLat.md) | | `next.elevation` | `number` | | `next.pitch` | `number` | | `next.roll` | `number` | | `next.zoom` | `number` | ## Returns `object` ### bearing? > `optional` **bearing**: `number` ### center? > `optional` **center**: [`LngLat`](../classes/LngLat.md) ### elevation? > `optional` **elevation**: `number` ### pitch? > `optional` **pitch**: `number` ### roll? > `optional` **roll**: `number` ### zoom? > `optional` **zoom**: `number` --- # CanvasSourceSpecification https://docs.mapatlas.xyz/overview/API/type-aliases/CanvasSourceSpecification # CanvasSourceSpecification > **CanvasSourceSpecification** = `object` Defined in: src/source/canvas\_source.ts:14 Options to add a canvas source type to the map. ## Properties ### animate? > `optional` **animate**: `boolean` Defined in: src/source/canvas\_source.ts:27 Whether the canvas source is animated. If the canvas is static (i.e. pixels do not need to be re-read on every frame), `animate` should be set to `false` to improve performance. #### Default Value ```ts true ``` *** ### canvas? > `optional` **canvas**: `string` \| `HTMLCanvasElement` Defined in: src/source/canvas\_source.ts:31 Canvas source from which to read pixels. Can be a string representing the ID of the canvas element, or the `HTMLCanvasElement` itself. *** ### coordinates > **coordinates**: \[\[`number`, `number`\], \[`number`, `number`\], \[`number`, `number`\], \[`number`, `number`\]\] Defined in: src/source/canvas\_source.ts:22 Four geographical coordinates denoting where to place the corners of the canvas, specified in `[longitude, latitude]` pairs. *** ### type > **type**: `"canvas"` Defined in: src/source/canvas\_source.ts:18 Source type. Must be `"canvas"`. --- # CenterZoomBearing https://docs.mapatlas.xyz/overview/API/type-aliases/CenterZoomBearing # CenterZoomBearing > **CenterZoomBearing** = `object` Defined in: src/ui/camera.ts:75 Holds center, zoom and bearing properties ## Properties ### bearing? > `optional` **bearing**: `number` Defined in: src/ui/camera.ts:88 The desired bearing in degrees. The bearing is the compass direction that is "up". For example, `bearing: 90` orients the map so that east is up. *** ### center? > `optional` **center**: [`LngLatLike`](LngLatLike.md) Defined in: src/ui/camera.ts:79 The desired center. *** ### zoom? > `optional` **zoom**: `number` Defined in: src/ui/camera.ts:83 The desired mercator zoom level. --- # CircleGranularity https://docs.mapatlas.xyz/overview/API/type-aliases/CircleGranularity # CircleGranularity > **CircleGranularity** = `1` \| `3` \| `5` \| `7` Defined in: src/render/subdivision\_granularity\_settings.ts:10 Defines the granularity of subdivision for circles with `circle-pitch-alignment: 'map'` and for heatmap kernels. More subdivision will cause circles to more closely follow the planet's surface. Possible values: 1, 3, 5, 7. Subdivision of 1 results in a simple quad. --- # ClusterIDAndSource https://docs.mapatlas.xyz/overview/API/type-aliases/ClusterIDAndSource # ClusterIDAndSource > **ClusterIDAndSource** = `object` Defined in: src/util/actor\_messages.ts:14 The parameters needed in order to get information about the cluster --- # Complete\ https://docs.mapatlas.xyz/overview/API/type-aliases/Complete # Complete\ > **Complete**\<`T`\> = \{ \[P in keyof Required\\]: Pick\ extends Required\\> ? T\[P\] : T\[P\] \| undefined \} Defined in: src/util/util.ts:1040 Makes optional keys required and add the the undefined type. ``` interface Test { foo: number; bar?: number; baz: number | undefined; } Complete { foo: number; bar: number | undefined; baz: number | undefined; } ``` See https://medium.com/terria/typescript-transforming-optional-properties-to-required-properties-that-may-be-undefined-7482cb4e1585 ## Type Parameters | Type Parameter | | ------ | | `T` | --- # Config https://docs.mapatlas.xyz/overview/API/type-aliases/Config # Config > **Config** = `object` Defined in: src/util/config.ts:15 This is a global config object used to store the configuration It is available in the workers as well. Only serializable data should be stored in it. --- # ControlPosition https://docs.mapatlas.xyz/overview/API/type-aliases/ControlPosition # ControlPosition > **ControlPosition** = `"top-left"` \| `"top-right"` \| `"bottom-left"` \| `"bottom-right"` Defined in: src/ui/control/control.ts:7 A position defintion for the control to be placed, can be in one of the corners of the map. When two or more controls are places in the same location they are stacked toward the center of the map. --- # Coordinates https://docs.mapatlas.xyz/overview/API/type-aliases/Coordinates # Coordinates > **Coordinates** = \[\[`number`, `number`\], \[`number`, `number`\], \[`number`, `number`\], \[`number`, `number`\]\] Defined in: src/source/image\_source.ts:27 Four geographical coordinates, represented as arrays of longitude and latitude numbers, which define the corners of the image. The coordinates start at the top left corner of the image and proceed in clockwise order. They do not have to represent a rectangle. --- # CreateTileMeshOptions https://docs.mapatlas.xyz/overview/API/type-aliases/CreateTileMeshOptions # CreateTileMeshOptions > **CreateTileMeshOptions** = `object` Defined in: src/util/create\_tile\_mesh.ts:22 Options for generating a tile mesh. Can optionally configure any of the following: - mesh subdivision granularity - border presence - special geometry for the north and/or south pole ## Properties ### extendToNorthPole? > `optional` **extendToNorthPole**: `boolean` Defined in: src/util/create\_tile\_mesh.ts:36 When true, additional geometry is generated along the north edge of the mesh, connecting it to the pole special vertex position. This geometry replaces the mesh border along this edge, if one is present. *** ### extendToSouthPole? > `optional` **extendToSouthPole**: `boolean` Defined in: src/util/create\_tile\_mesh.ts:41 When true, additional geometry is generated along the south edge of the mesh, connecting it to the pole special vertex position. This geometry replaces the mesh border along this edge, if one is present. *** ### generateBorders? > `optional` **generateBorders**: `boolean` Defined in: src/util/create\_tile\_mesh.ts:31 When true, an additional ring of quads is generated along the border, always extending `EXTENT_STENCIL_BORDER` units away from the main mesh. *** ### granularity? > `optional` **granularity**: `number` Defined in: src/util/create\_tile\_mesh.ts:27 Specifies how much should the tile mesh be subdivided. A value of 1 leads to a simple quad, a value of 4 will result in a grid of 4x4 quads. --- # CrossFaded\ https://docs.mapatlas.xyz/overview/API/type-aliases/CrossFaded # CrossFaded\ > **CrossFaded**\<`T`\> = `object` Defined in: src/style/properties.ts:19 A from-to type ## Type Parameters | Type Parameter | | ------ | | `T` | --- # CustomRenderMethod() https://docs.mapatlas.xyz/overview/API/type-aliases/CustomRenderMethod # CustomRenderMethod() > **CustomRenderMethod** = (`gl`: `WebGLRenderingContext` \| `WebGL2RenderingContext`, `options`: [`CustomRenderMethodInput`](CustomRenderMethodInput.md)) => `void` Defined in: src/style/style\_layer/custom\_style\_layer.ts:114 ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `gl` | `WebGLRenderingContext` \| `WebGL2RenderingContext` | The map's gl context. | | `options` | [`CustomRenderMethodInput`](CustomRenderMethodInput.md) | Argument object with render inputs like camera properties. | ## Returns `void` --- # CustomRenderMethodInput https://docs.mapatlas.xyz/overview/API/type-aliases/CustomRenderMethodInput # CustomRenderMethodInput > **CustomRenderMethodInput** = `object` Defined in: src/style/style\_layer/custom\_style\_layer.ts:10 Input arguments exposed by custom render function. ## Properties ### defaultProjectionData > **defaultProjectionData**: [`ProjectionData`](ProjectionData.md) Defined in: src/style/style\_layer/custom\_style\_layer.ts:107 Uniforms that should be passed to the vertex shader, if mapmetrics's projection code is used. For more details of this object's internals, see its doc comments in `src/geo/projection/projection_data.ts`. These uniforms are set so that `projectTile` in shader accepts a vec2 in range 0..1 in web mercator coordinates. Use `map.transform.getProjectionData({overscaledTileID: tileID})` to get uniforms for a given tile and pass vec2 in tile-local range 0..EXTENT instead. For projection 3D features, use `projectTileFor3D` in the shader. If you just need a projection matrix, use `defaultProjectionData.projectionMatrix`. A projection matrix is sufficient for simple custom layers that also only support mercator projection. Under mercator projection, when these uniforms are used, the shader's `projectTile` function projects spherical mercator coordinates to gl clip space coordinates. The spherical mercator coordinate `[0, 0]` represents the top left corner of the mercator world and `[1, 1]` represents the bottom right corner. When the `renderingMode` is `"3d"`, the z coordinate is conformal. A box with identical x, y, and z lengths in mercator units would be rendered as a cube. [MercatorCoordinate.fromLngLat](../classes/MercatorCoordinate.md#fromlnglat) can be used to project a `LngLat` to a mercator coordinate. Under globe projection, when these uniforms are used, the `elevation` parameter passed to `projectTileFor3D` in the shader is elevation in meters above "sea level", or more accurately for globe, elevation above the surface of the perfect sphere used to render the planet. *** ### farZ > **farZ**: `number` Defined in: src/style/style\_layer/custom\_style\_layer.ts:16 This value represents the distance from the camera to the far clipping plane. It is used in the calculation of the projection matrix to determine which objects are visible. farZ should be larger than nearZ. *** ### fov > **fov**: `number` Defined in: src/style/style\_layer/custom\_style\_layer.ts:26 Vertical field of view in radians. *** ### modelViewProjectionMatrix > **modelViewProjectionMatrix**: `mat4` Defined in: src/style/style\_layer/custom\_style\_layer.ts:32 model view projection matrix represents the matrix converting from world space to clip space https://learnopengl.com/Getting-started/Coordinate-Systems * *** ### nearZ > **nearZ**: `number` Defined in: src/style/style\_layer/custom\_style\_layer.ts:22 This value represents the distance from the camera to the near clipping plane. It is used in the calculation of the projection matrix to determine which objects are visible. nearZ should be smaller than farZ. *** ### projectionMatrix > **projectionMatrix**: `mat4` Defined in: src/style/style\_layer/custom\_style\_layer.ts:38 projection matrix represents the matrix converting from view space to clip space https://learnopengl.com/Getting-started/Coordinate-Systems *** ### shaderData > **shaderData**: `object` Defined in: src/style/style\_layer/custom\_style\_layer.ts:42 Data required for picking and compiling a custom shader for the current projection. #### define > **define**: `string` Defines to add to the shader code. Depends on current projection. ##### Example ``` const vertexSource = `#version 300 es ${shaderData.vertexShaderPrelude} ${shaderData.define} in vec2 a_pos; void main() { gl_Position = projectTile(a_pos); #ifdef GLOBE // Do globe-specific things #endif }`; ``` #### variantName > **variantName**: `string` Name of the shader variant that should be used. Depends on current projection. Whenever the other shader properties change, this string changes as well, and can be used as a key with which to cache compiled shaders. #### vertexShaderPrelude > **vertexShaderPrelude**: `string` The prelude code to add to the vertex shader to access mapmetrics's `projectTile` projection function. Depends on current projection. ##### Example ``` const vertexSource = `#version 300 es ${shaderData.vertexShaderPrelude} ${shaderData.define} in vec2 a_pos; void main() { gl_Position = projectTile(a_pos); }`; ``` --- # DEMEncoding https://docs.mapatlas.xyz/overview/API/type-aliases/DEMEncoding # DEMEncoding > **DEMEncoding** = `"mapbox"` \| `"terrarium"` \| `"custom"` Defined in: src/data/dem\_data.ts:9 The possible DEM encoding types --- # DashEntry https://docs.mapatlas.xyz/overview/API/type-aliases/DashEntry # DashEntry > **DashEntry** = `object` Defined in: src/render/line\_atlas.ts:8 A dash entry --- # DistributiveKeys\ https://docs.mapatlas.xyz/overview/API/type-aliases/DistributiveKeys # DistributiveKeys\ > **DistributiveKeys**\<`T`\> = `T` *extends* `T` ? keyof `T` : `never` Defined in: src/util/vectortile\_to\_geojson.ts:7 A helper for type to omit a property from a type ## Type Parameters | Type Parameter | | ------ | | `T` | --- # DistributiveOmit\ https://docs.mapatlas.xyz/overview/API/type-aliases/DistributiveOmit # DistributiveOmit\ > **DistributiveOmit**\<`T`, `K`\> = `T` *extends* `unknown` ? `Omit`\<`T`, `K`\> : `never` Defined in: src/util/vectortile\_to\_geojson.ts:11 A helper for type to omit a property from a type ## Type Parameters | Type Parameter | | ------ | | `T` | | `K` *extends* [`DistributiveKeys`](DistributiveKeys.md)\<`T`\> | --- # DragPanOptions https://docs.mapatlas.xyz/overview/API/type-aliases/DragPanOptions # DragPanOptions > **DragPanOptions** = `object` Defined in: src/ui/handler/shim/drag\_pan.ts:7 A [DragPanHandler](../classes/DragPanHandler.md) options object ## Properties ### deceleration? > `optional` **deceleration**: `number` Defined in: src/ui/handler/shim/drag\_pan.ts:23 the maximum value of the drag velocity. #### Default Value ```ts 1400 ``` *** ### easing()? > `optional` **easing**: (`t`: `number`) => `number` Defined in: src/ui/handler/shim/drag\_pan.ts:18 easing function applied to `map.panTo` when applying the drag. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `t` | `number` | the easing function | #### Returns `number` #### Default Value ```ts bezier(0, 0, 0.3, 1) ``` *** ### linearity? > `optional` **linearity**: `number` Defined in: src/ui/handler/shim/drag\_pan.ts:12 factor used to scale the drag velocity #### Default Value ```ts 0 ``` *** ### maxSpeed? > `optional` **maxSpeed**: `number` Defined in: src/ui/handler/shim/drag\_pan.ts:28 the rate at which the speed reduces after the pan ends. #### Default Value ```ts 2500 ``` --- # DragRotateHandlerOptions https://docs.mapatlas.xyz/overview/API/type-aliases/DragRotateHandlerOptions # DragRotateHandlerOptions > **DragRotateHandlerOptions** = `object` Defined in: src/ui/handler/shim/drag\_rotate.ts:6 Options object for `DragRotateHandler`. ## Properties ### pitchWithRotate > **pitchWithRotate**: `boolean` Defined in: src/ui/handler/shim/drag\_rotate.ts:11 Control the map pitch in addition to the bearing #### Default Value ```ts true ``` *** ### rollEnabled > **rollEnabled**: `boolean` Defined in: src/ui/handler/shim/drag\_rotate.ts:16 Control the map roll in addition to the bearing #### Default Value ```ts false ``` --- # EaseToOptions https://docs.mapatlas.xyz/overview/API/type-aliases/EaseToOptions # EaseToOptions > **EaseToOptions** = [`AnimationOptions`](AnimationOptions.md) & [`CameraOptions`](CameraOptions.md) & `object` Defined in: src/ui/camera.ts:167 The [Map#easeTo](../classes/Map.md#easeto) options object ## Type declaration ### around? > `optional` **around**: [`LngLatLike`](LngLatLike.md) If `zoom` is specified, `around` determines the point around which the zoom is centered. ### delayEndEvents? > `optional` **delayEndEvents**: `number` ### easeId? > `optional` **easeId**: `string` ### noMoveStart? > `optional` **noMoveStart**: `boolean` ### padding? > `optional` **padding**: `number` \| [`PaddingOptions`](PaddingOptions.md) --- # ExpiryData https://docs.mapatlas.xyz/overview/API/type-aliases/ExpiryData # ExpiryData > **ExpiryData** = `object` Defined in: src/util/ajax.ts:14 A type used to store the tile's expiration date and cache control definition --- # FeatureIdentifier https://docs.mapatlas.xyz/overview/API/type-aliases/FeatureIdentifier # FeatureIdentifier > **FeatureIdentifier** = `object` Defined in: src/style/style.ts:76 A feature identifier that is bound to a source ## Properties ### id? > `optional` **id**: `string` \| `number` Defined in: src/style/style.ts:80 Unique id of the feature. *** ### source > **source**: `string` Defined in: src/style/style.ts:84 The id of the vector or GeoJSON source for the feature. *** ### sourceLayer? > `optional` **sourceLayer**: `string` Defined in: src/style/style.ts:88 *For vector tile sources, `sourceLayer` is required.* --- # FitBoundsOptions https://docs.mapatlas.xyz/overview/API/type-aliases/FitBoundsOptions # FitBoundsOptions > **FitBoundsOptions** = [`FlyToOptions`](FlyToOptions.md) & `object` Defined in: src/ui/camera.ts:181 Options for [Map#fitBounds](../classes/Map.md#fitbounds) method ## Type declaration ### linear? > `optional` **linear**: `boolean` If `true`, the map transitions using [Map#easeTo](../classes/Map.md#easeto). If `false`, the map transitions using [Map#flyTo](../classes/Map.md#flyto). See those functions and [AnimationOptions](AnimationOptions.md) for information about options available. #### Default Value ```ts false ``` ### maxZoom? > `optional` **maxZoom**: `number` The maximum zoom level to allow when the map view transitions to the specified bounds. ### offset? > `optional` **offset**: [`PointLike`](PointLike.md) The center of the given bounds relative to the map's center, measured in pixels. #### Default Value ```ts [0, 0] ``` --- # FlyToOptions https://docs.mapatlas.xyz/overview/API/type-aliases/FlyToOptions # FlyToOptions > **FlyToOptions** = [`AnimationOptions`](AnimationOptions.md) & [`CameraOptions`](CameraOptions.md) & `object` Defined in: src/ui/camera.ts:123 The [Map#flyTo](../classes/Map.md#flyto) options object ## Type declaration ### curve? > `optional` **curve**: `number` The zooming "curve" that will occur along the flight path. A high value maximizes zooming for an exaggerated animation, while a low value minimizes zooming for an effect closer to [Map#easeTo](../classes/Map.md#easeto). 1.42 is the average value selected by participants in the user study discussed in [van Wijk (2003)](https://www.win.tue.nl/~vanwijk/zoompan.pdf). A value of `Math.pow(6, 0.25)` would be equivalent to the root mean squared average velocity. A value of 1 would produce a circular motion. #### Default Value ```ts 1.42 ``` ### maxDuration? > `optional` **maxDuration**: `number` The animation's maximum duration, measured in milliseconds. If duration exceeds maximum duration, it resets to 0. ### minZoom? > `optional` **minZoom**: `number` The zero-based zoom level at the peak of the flight path. If `options.curve` is specified, this option is ignored. ### padding? > `optional` **padding**: `number` \| [`PaddingOptions`](PaddingOptions.md) The amount of padding in pixels to add to the given bounds. ### screenSpeed? > `optional` **screenSpeed**: `number` The average speed of the animation measured in screenfulls per second, assuming a linear timing curve. If `options.speed` is specified, this option is ignored. ### speed? > `optional` **speed**: `number` The average speed of the animation defined in relation to `options.curve`. A speed of 1.2 means that the map appears to move along the flight path by 1.2 times `options.curve` screenfulls every second. A _screenfull_ is the map's visible span. It does not correspond to a fixed physical distance, but varies by zoom level. #### Default Value ```ts 1.2 ``` --- # FullscreenControlOptions https://docs.mapatlas.xyz/overview/API/type-aliases/FullscreenControlOptions # FullscreenControlOptions > **FullscreenControlOptions** = `object` Defined in: src/ui/control/fullscreen\_control.ts:12 The [FullscreenControl](../classes/FullscreenControl.md) options object ## Properties ### container? > `optional` **container**: `HTMLElement` Defined in: src/ui/control/fullscreen\_control.ts:16 `container` is the [compatible DOM element](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen#Compatible_elements) which should be made full screen. By default, the map container element will be made full screen. --- # GeoJSONFeatureDiff https://docs.mapatlas.xyz/overview/API/type-aliases/GeoJSONFeatureDiff # GeoJSONFeatureDiff > **GeoJSONFeatureDiff** = `object` Defined in: src/source/geojson\_source\_diff.ts:31 A geojson feature diff object ## Properties ### addOrUpdateProperties? > `optional` **addOrUpdateProperties**: `object`[] Defined in: src/source/geojson\_source\_diff.ts:51 The properties to add or update along side their values #### key > **key**: `string` #### value > **value**: `any` *** ### id > **id**: [`GeoJSONFeatureId`](GeoJSONFeatureId.md) Defined in: src/source/geojson\_source\_diff.ts:35 The feature ID *** ### newGeometry? > `optional` **newGeometry**: `GeoJSON.Geometry` Defined in: src/source/geojson\_source\_diff.ts:39 If it's a new geometry, place it here *** ### removeAllProperties? > `optional` **removeAllProperties**: `boolean` Defined in: src/source/geojson\_source\_diff.ts:43 Setting to `true` will remove all preperties *** ### removeProperties? > `optional` **removeProperties**: `string`[] Defined in: src/source/geojson\_source\_diff.ts:47 The properties keys to remove --- # GeoJSONFeatureId https://docs.mapatlas.xyz/overview/API/type-aliases/GeoJSONFeatureId # GeoJSONFeatureId > **GeoJSONFeatureId** = `number` \| `string` Defined in: src/source/geojson\_source\_diff.ts:4 A way to identify a feature, either by string or by number --- # GeoJSONSourceDiff https://docs.mapatlas.xyz/overview/API/type-aliases/GeoJSONSourceDiff # GeoJSONSourceDiff > **GeoJSONSourceDiff** = `object` Defined in: src/source/geojson\_source\_diff.ts:9 The geojson source diff object ## Properties ### add? > `optional` **add**: `GeoJSON.Feature`[] Defined in: src/source/geojson\_source\_diff.ts:21 An array of features to add *** ### remove? > `optional` **remove**: [`GeoJSONFeatureId`](GeoJSONFeatureId.md)[] Defined in: src/source/geojson\_source\_diff.ts:17 An array of features IDs to remove *** ### removeAll? > `optional` **removeAll**: `boolean` Defined in: src/source/geojson\_source\_diff.ts:13 When set to `true` it will remove all features *** ### update? > `optional` **update**: [`GeoJSONFeatureDiff`](GeoJSONFeatureDiff.md)[] Defined in: src/source/geojson\_source\_diff.ts:25 An array of update objects --- # GeoJSONSourceOptions https://docs.mapatlas.xyz/overview/API/type-aliases/GeoJSONSourceOptions # GeoJSONSourceOptions > **GeoJSONSourceOptions** = `GeoJSONSourceSpecification` & `object` Defined in: src/source/geojson\_source.ts:23 Options object for GeoJSONSource. ## Type declaration ### collectResourceTiming? > `optional` **collectResourceTiming**: `boolean` ### data > **data**: `GeoJSON.GeoJSON` \| `string` ### workerOptions? > `optional` **workerOptions**: [`GeoJSONWorkerOptions`](GeoJSONWorkerOptions.md) --- # GeoJSONWorkerOptions https://docs.mapatlas.xyz/overview/API/type-aliases/GeoJSONWorkerOptions # GeoJSONWorkerOptions > **GeoJSONWorkerOptions** = `object` Defined in: src/source/geojson\_worker\_source.ts:25 The geojson worker options that can be passed to the worker --- # GeoJSONWorkerSourceLoadDataResult https://docs.mapatlas.xyz/overview/API/type-aliases/GeoJSONWorkerSourceLoadDataResult # GeoJSONWorkerSourceLoadDataResult > **GeoJSONWorkerSourceLoadDataResult** = `object` Defined in: src/util/actor\_messages.ts:28 The result of the call to load a geojson source --- # GeolocateControlOptions https://docs.mapatlas.xyz/overview/API/type-aliases/GeolocateControlOptions # GeolocateControlOptions > **GeolocateControlOptions** = `object` Defined in: src/ui/control/geolocate\_control.ts:16 The [GeolocateControl](../classes/GeolocateControl.md) options object ## Properties ### fitBoundsOptions? > `optional` **fitBoundsOptions**: [`FitBoundsOptions`](FitBoundsOptions.md) Defined in: src/ui/control/geolocate\_control.ts:25 A options object to use when the map is panned and zoomed to the user's location. The default is to use a `maxZoom` of 15 to limit how far the map will zoom in for very accurate locations. *** ### positionOptions? > `optional` **positionOptions**: `PositionOptions` Defined in: src/ui/control/geolocate\_control.ts:21 A Geolocation API [PositionOptions](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions) object. #### Default Value `{enableHighAccuracy: false, timeout: 6000}` *** ### showAccuracyCircle? > `optional` **showAccuracyCircle**: `boolean` Defined in: src/ui/control/geolocate\_control.ts:35 By default, if `showUserLocation` is `true`, a transparent circle will be drawn around the user location indicating the accuracy (95% confidence level) of the user's location. Set to `false` to disable. Always disabled when `showUserLocation` is `false`. #### Default Value ```ts true ``` *** ### showUserLocation? > `optional` **showUserLocation**: `boolean` Defined in: src/ui/control/geolocate\_control.ts:40 By default a dot will be shown on the map at the user's location. Set to `false` to disable. #### Default Value ```ts true ``` *** ### trackUserLocation? > `optional` **trackUserLocation**: `boolean` Defined in: src/ui/control/geolocate\_control.ts:30 If `true` the `GeolocateControl` becomes a toggle button and when active the map will receive updates to the user's location as it changes. #### Default Value ```ts false ``` --- # GestureOptions https://docs.mapatlas.xyz/overview/API/type-aliases/GestureOptions # GestureOptions > **GestureOptions** = `boolean` Defined in: src/ui/handler/cooperative\_gestures.ts:10 The [CooperativeGesturesHandler](../classes/CooperativeGesturesHandler.md) options object for the gesture settings --- # GetClusterLeavesParams https://docs.mapatlas.xyz/overview/API/type-aliases/GetClusterLeavesParams # GetClusterLeavesParams > **GetClusterLeavesParams** = [`ClusterIDAndSource`](ClusterIDAndSource.md) & `object` Defined in: src/util/actor\_messages.ts:23 Parameters needed to get the leaves of a cluster ## Type declaration ### limit > **limit**: `number` ### offset > **offset**: `number` --- # GetGlyphsParameters https://docs.mapatlas.xyz/overview/API/type-aliases/GetGlyphsParameters # GetGlyphsParameters > **GetGlyphsParameters** = `object` Defined in: src/util/actor\_messages.ts:62 Parameters needed to get the glyphs --- # GetGlyphsResponse https://docs.mapatlas.xyz/overview/API/type-aliases/GetGlyphsResponse # GetGlyphsResponse > **GetGlyphsResponse** = `object` Defined in: src/util/actor\_messages.ts:72 A response object returned when requesting glyphs ## Index Signature \[`stack`: `string`\]: `object` --- # GetImagesParameters https://docs.mapatlas.xyz/overview/API/type-aliases/GetImagesParameters # GetImagesParameters > **GetImagesParameters** = `object` Defined in: src/util/actor\_messages.ts:52 Parameters needed to get the images --- # GetImagesResponse https://docs.mapatlas.xyz/overview/API/type-aliases/GetImagesResponse # GetImagesResponse > **GetImagesResponse** = `object` Defined in: src/util/actor\_messages.ts:81 A response object returned when requesting images ## Index Signature \[`_`: `string`\]: [`StyleImage`](StyleImage.md) --- # GetResourceResponse\ https://docs.mapatlas.xyz/overview/API/type-aliases/GetResourceResponse # GetResourceResponse\ > **GetResourceResponse**\<`T`\> = [`ExpiryData`](ExpiryData.md) & `object` Defined in: src/util/ajax.ts:70 The response object returned from a successful AJAx request ## Type declaration ### data > **data**: `T` ## Type Parameters | Type Parameter | | ------ | | `T` | --- # GlyphMetrics https://docs.mapatlas.xyz/overview/API/type-aliases/GlyphMetrics # GlyphMetrics > **GlyphMetrics** = `object` Defined in: src/style/style\_glyph.ts:6 Some metices related to a glyph ## Properties ### isDoubleResolution? > `optional` **isDoubleResolution**: `boolean` Defined in: src/style/style\_glyph.ts:15 isDoubleResolution = true for 48px textures --- # GlyphPosition https://docs.mapatlas.xyz/overview/API/type-aliases/GlyphPosition # GlyphPosition > **GlyphPosition** = `object` Defined in: src/render/glyph\_atlas.ts:23 The glyph's position --- # GlyphPositions https://docs.mapatlas.xyz/overview/API/type-aliases/GlyphPositions # GlyphPositions > **GlyphPositions** = `object` Defined in: src/render/glyph\_atlas.ts:31 The glyphs' positions ## Index Signature \[`_`: `string`\]: `object` --- # GridKey https://docs.mapatlas.xyz/overview/API/type-aliases/GridKey # GridKey > **GridKey** = `object` Defined in: src/symbol/grid\_index.ts:32 A key for the grid --- # HandlerResult https://docs.mapatlas.xyz/overview/API/type-aliases/HandlerResult # HandlerResult > **HandlerResult** = `object` Defined in: src/ui/handler\_manager.ts:81 All handler methods that are called with events can optionally return a `HandlerResult`. ## Properties ### around? > `optional` **around**: `Point` \| `null` Defined in: src/ui/handler\_manager.ts:90 the point to not move when changing the camera *** ### cameraAnimation()? > `optional` **cameraAnimation**: (`map`: [`Map`](../classes/Map.md)) => `any` Defined in: src/ui/handler\_manager.ts:98 A method that can fire a one-off easing by directly changing the map's camera. #### Parameters | Parameter | Type | | ------ | ------ | | `map` | [`Map`](../classes/Map.md) | #### Returns `any` *** ### needsRenderFrame? > `optional` **needsRenderFrame**: `boolean` Defined in: src/ui/handler\_manager.ts:107 Makes the manager trigger a frame, allowing the handler to return multiple results over time (see scrollzoom). *** ### noInertia? > `optional` **noInertia**: `boolean` Defined in: src/ui/handler\_manager.ts:111 The camera changes won't get recorded for inertial zooming. *** ### originalEvent? > `optional` **originalEvent**: [`Event`](../classes/Event.md) Defined in: src/ui/handler\_manager.ts:103 The last three properties are needed by only one handler: scrollzoom. The DOM event to be used as the `originalEvent` on any camera change events. *** ### pinchAround? > `optional` **pinchAround**: `Point` \| `null` Defined in: src/ui/handler\_manager.ts:94 same as above, except for pinch actions, which are given higher priority --- # IndicesType https://docs.mapatlas.xyz/overview/API/type-aliases/IndicesType # IndicesType > **IndicesType** = `"32bit"` \| `"16bit"` \| `undefined` Defined in: src/util/create\_tile\_mesh.ts:66 Describes desired type of vertex indices, either 16 bit uint, 32 bit uint, or, if undefined, any of the two options. --- # JumpToOptions https://docs.mapatlas.xyz/overview/API/type-aliases/JumpToOptions # JumpToOptions > **JumpToOptions** = [`CameraOptions`](CameraOptions.md) & `object` Defined in: src/ui/camera.ts:94 The options object related to the [Map#jumpTo](../classes/Map.md#jumpto) method ## Type declaration ### padding? > `optional` **padding**: [`PaddingOptions`](PaddingOptions.md) Dimensions in pixels applied on each side of the viewport for shifting the vanishing point. --- # Listener() https://docs.mapatlas.xyz/overview/API/type-aliases/Listener # Listener() > **Listener** = (`a`: `any`) => `any` Defined in: src/util/evented.ts:6 A listener method used as a callback to events ## Parameters | Parameter | Type | | ------ | ------ | | `a` | `any` | ## Returns `any` --- # LngLatBoundsLike https://docs.mapatlas.xyz/overview/API/type-aliases/LngLatBoundsLike # LngLatBoundsLike > **LngLatBoundsLike** = [`LngLatBounds`](../classes/LngLatBounds.md) \| \[[`LngLatLike`](LngLatLike.md), [`LngLatLike`](LngLatLike.md)\] \| \[`number`, `number`, `number`, `number`\] Defined in: src/geo/lng\_lat\_bounds.ts:20 A [LngLatBounds](../classes/LngLatBounds.md) object, an array of [LngLatLike](LngLatLike.md) objects in [sw, ne] order, or an array of numbers in [west, south, east, north] order. ## Example ```ts let v1 = new LngLatBounds( new LngLat(-73.9876, 40.7661), new LngLat(-73.9397, 40.8002) ); let v2 = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]) let v3 = [[-73.9876, 40.7661], [-73.9397, 40.8002]]; ``` --- # LngLatLike https://docs.mapatlas.xyz/overview/API/type-aliases/LngLatLike # LngLatLike > **LngLatLike** = [`LngLat`](../classes/LngLat.md) \| \{ `lat`: `number`; `lng`: `number`; \} \| \{ `lat`: `number`; `lon`: `number`; \} \| \[`number`, `number`\] Defined in: src/geo/lng\_lat.ts:23 A [LngLat](../classes/LngLat.md) object, an array of two numbers representing longitude and latitude, or an object with `lng` and `lat` or `lon` and `lat` properties. ## Example ```ts let v1 = new LngLat(-122.420679, 37.772537); let v2 = [-122.420679, 37.772537]; let v3 = {lon: -122.420679, lat: 37.772537}; ``` --- # LoadGeoJSONParameters https://docs.mapatlas.xyz/overview/API/type-aliases/LoadGeoJSONParameters # LoadGeoJSONParameters > **LoadGeoJSONParameters** = [`GeoJSONWorkerOptions`](GeoJSONWorkerOptions.md) & `object` Defined in: src/source/geojson\_worker\_source.ts:39 Parameters needed to load a geojson to the worker ## Type declaration ### data? > `optional` **data**: `string` Literal GeoJSON data. Must be provided if `request.url` is not. ### dataDiff? > `optional` **dataDiff**: [`GeoJSONSourceDiff`](GeoJSONSourceDiff.md) ### request? > `optional` **request**: [`RequestParameters`](RequestParameters.md) ### type > **type**: `"geojson"` --- # LogoControlOptions https://docs.mapatlas.xyz/overview/API/type-aliases/LogoControlOptions # LogoControlOptions > **LogoControlOptions** = `object` Defined in: src/ui/control/logo\_control.ts:9 The [LogoControl](../classes/LogoControl.md) options object ## Properties ### compact? > `optional` **compact**: `boolean` Defined in: src/ui/control/logo\_control.ts:14 If `true`, force a compact logo. If `false`, force the full logo. The default is a responsive logo that collapses when the map is less than 640 pixels wide. --- # MapContextEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapContextEvent # MapContextEvent > **MapContextEvent** = `object` Defined in: src/ui/events.ts:768 An event related to the web gl context --- # MapDataEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapDataEvent # MapDataEvent > **MapDataEvent** = `object` Defined in: src/ui/events.ts:721 A `MapDataEvent` object is emitted with the `data` and `dataloading` events. Possible values for `dataType`s are: - `'source'`: The non-tile data associated with any source - `'style'`: The [style](https://mapmetrics.org/mapmetrics-style-spec/) used by the map Possible values for `sourceDataType`s are: - `'metadata'`: indicates that any necessary source metadata has been loaded (such as TileJSON) and it is ok to start loading tiles - `'content'`: indicates the source data has changed (such as when source.setData() has been called on GeoJSONSource) - `'visibility'`: send when the source becomes used when at least one of its layers becomes visible in style sense (inside the layer's zoom range and with layout.visibility set to 'visible') - `'idle'`: indicates that no new source data has been fetched (but the source has done loading) ## Example ```ts // The sourcedata event is an example of MapDataEvent. // Set up an event listener on the map. map.on('sourcedata', (e) => { if (e.isSourceLoaded) { // Do something when the source has finished loading } }); ``` ## Properties ### dataType > **dataType**: `string` Defined in: src/ui/events.ts:729 The type of data that has changed. One of `'source'`, `'style'`. *** ### sourceDataType > **sourceDataType**: [`MapSourceDataType`](MapSourceDataType.md) Defined in: src/ui/events.ts:733 Included if the event has a `dataType` of `source` and the event signals that internal data has been received or changed. Possible values are `metadata`, `content`, `visibility` and `idle`. *** ### type > **type**: `string` Defined in: src/ui/events.ts:725 The event type. --- # MapEventType https://docs.mapatlas.xyz/overview/API/type-aliases/MapEventType # MapEventType > **MapEventType** = `object` Defined in: src/ui/events.ts:149 `MapEventType` - a mapping between the event name and the event value. These events are used with the [Map#on](../classes/Map.md#on) method. When using a `layerId` with [Map#on](../classes/Map.md#on) method, please refer to [MapLayerEventType](MapLayerEventType.md). The following example can be used for all the events. ## Example ```ts // Initialize the map let map = new Map({ // map options }); // Set an event listener map.on('the-event-name', () => { console.log('An event has occurred!'); }); ``` ## Properties ### boxzoomcancel > **boxzoomcancel**: [`MapLibreZoomEvent`](MapLibreZoomEvent.md) Defined in: src/ui/events.ts:251 Fired when the user cancels a "box zoom" interaction, or when the bounding box does not meet the minimum size threshold. See [BoxZoomHandler](../classes/BoxZoomHandler.md). *** ### boxzoomend > **boxzoomend**: [`MapLibreZoomEvent`](MapLibreZoomEvent.md) Defined in: src/ui/events.ts:259 Fired when a "box zoom" interaction ends. See [BoxZoomHandler](../classes/BoxZoomHandler.md). *** ### boxzoomstart > **boxzoomstart**: [`MapLibreZoomEvent`](MapLibreZoomEvent.md) Defined in: src/ui/events.ts:255 Fired when a "box zoom" interaction starts. See [BoxZoomHandler](../classes/BoxZoomHandler.md). *** ### click > **click**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:285 Fired when a pointing device (usually a mouse) is pressed and released at the same point on the map. #### See - [Measure distances](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/measure/) - [Center the map on a clicked symbol](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/center-on-symbol/) *** ### contextmenu > **contextmenu**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:289 Fired when the right button of the mouse is clicked or the context menu key is pressed within the map. *** ### cooperativegestureprevented > **cooperativegestureprevented**: [`MapLibreEvent`](MapLibreEvent.md)\<`WheelEvent` \| `TouchEvent`\> & `object` Defined in: src/ui/events.ts:419 Fired whenever the cooperativeGestures option prevents a gesture from being handled by the map. This is useful for showing your own UI when this happens. #### Type declaration ##### gestureType > **gestureType**: `"wheel_zoom"` \| `"touch_pan"` *** ### data > **data**: [`MapDataEvent`](MapDataEvent.md) Defined in: src/ui/events.ts:210 Fired when any map data loads or changes. See [MapDataEvent](MapDataEvent.md) for more information. #### See [Display HTML clusters with custom properties](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/cluster-html/) *** ### dataabort > **dataabort**: [`MapDataEvent`](MapDataEvent.md) Defined in: src/ui/events.ts:242 Fired when a request for one of the map's sources' tiles or data is aborted. *** ### dataloading > **dataloading**: [`MapDataEvent`](MapDataEvent.md) Defined in: src/ui/events.ts:205 Fired when any map data (style, source, tile, etc) begins loading or changing asynchronously. All `dataloading` events are followed by a `data`, `dataabort` or `error` event. *** ### dblclick > **dblclick**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:295 Fired when a pointing device (usually a mouse) is pressed and released twice at the same point on the map in rapid succession. **Note:** Under normal conditions, this event will be preceded by two `click` events. *** ### drag > **drag**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:385 Fired repeatedly during a "drag to pan" interaction. See [DragPanHandler](../classes/DragPanHandler.md). *** ### dragend > **dragend**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:390 Fired when a "drag to pan" interaction ends. See [DragPanHandler](../classes/DragPanHandler.md). #### See [Create a draggable marker](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-marker/) *** ### dragstart > **dragstart**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:381 Fired when a "drag to pan" interaction starts. See [DragPanHandler](../classes/DragPanHandler.md). *** ### error > **error**: `ErrorEvent` Defined in: src/ui/events.ts:156 Fired when an error occurs. This is GL JS's primary error reporting mechanism. We use an event instead of `throw` to better accommodate asynchronous operations. If no listeners are bound to the `error` event, the error will be printed to the console. *** ### idle > **idle**: [`MapLibreEvent`](MapLibreEvent.md) Defined in: src/ui/events.ts:174 Fired after the last frame rendered before the map enters an "idle" state: - No camera transitions are in progress - All currently requested tiles have loaded - All fade/transition animations have completed *** ### load > **load**: [`MapLibreEvent`](MapLibreEvent.md) Defined in: src/ui/events.ts:165 Fired immediately after all necessary resources have been downloaded and the first visually complete rendering of the map has occurred. #### See - [Draw GeoJSON points](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/geojson-markers/) - [Add live realtime data](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/live-geojson/) - [Animate a point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/animate-point-along-line/) *** ### mousedown > **mousedown**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:316 Fired when a pointing device (usually a mouse) is pressed within the map. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### mousemove > **mousemove**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:304 Fired when a pointing device (usually a mouse) is moved while the cursor is inside the map. As you move the cursor across the map, the event will fire every time the cursor changes position within the map. #### See - [Get coordinates of the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/mouse-position/) - [Highlight features under the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) - [Display a popup on over](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) *** ### mouseout > **mouseout**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:320 Fired when a point device (usually a mouse) leaves the map's canvas. *** ### mouseover > **mouseover**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:330 Fired when a pointing device (usually a mouse) is moved within the map. As you move the cursor across a web page containing a map, the event will fire each time it enters the map or any child elements. #### See - [Get coordinates of the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/mouse-position/) - [Highlight features under the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) - [Display a popup on hover](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) *** ### mouseup > **mouseup**: [`MapMouseEvent`](../classes/MapMouseEvent.md) Defined in: src/ui/events.ts:310 Fired when a pointing device (usually a mouse) is released within the map. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### move > **move**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `WheelEvent` \| `undefined`\> Defined in: src/ui/events.ts:343 Fired repeatedly during an animated transition from one view to another, as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto). #### See [Display HTML clusters with custom properties](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/cluster-html/) *** ### moveend > **moveend**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `WheelEvent` \| `undefined`\> Defined in: src/ui/events.ts:350 Fired just after the map completes a transition from one view to another, as the result of either user interaction or methods such as [Map#jumpTo](../classes/Map.md#jumpto). #### See [Display HTML clusters with custom properties](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/cluster-html/) *** ### movestart > **movestart**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `WheelEvent` \| `undefined`\> Defined in: src/ui/events.ts:336 Fired just before the map begins a transition from one view to another, as the result of either user interaction or methods such as [Map#jumpTo](../classes/Map.md#jumpto). *** ### pitch > **pitch**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:401 Fired repeatedly during the map's pitch (tilt) animation between one state and another as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto). *** ### pitchend > **pitchend**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:406 Fired immediately after the map's pitch (tilt) finishes changing as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto). *** ### pitchstart > **pitchstart**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:395 Fired whenever the map's pitch (tilt) begins a change as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto) . *** ### projectiontransition > **projectiontransition**: [`MapProjectionEvent`](MapProjectionEvent.md) Defined in: src/ui/events.ts:425 Fired when map's projection is modified in other ways than by map being moved. *** ### remove > **remove**: [`MapLibreEvent`](MapLibreEvent.md) Defined in: src/ui/events.ts:178 Fired immediately after the map has been removed with [Map#remove](../classes/Map.md#remove). *** ### render > **render**: [`MapLibreEvent`](MapLibreEvent.md) Defined in: src/ui/events.ts:187 Fired whenever the map is drawn to the screen, as the result of - a change to the map's position, zoom, pitch, or bearing - a change to the map's style - a change to a GeoJSON source - the loading of a vector tile, GeoJSON file, glyph, or sprite *** ### resize > **resize**: [`MapLibreEvent`](MapLibreEvent.md) Defined in: src/ui/events.ts:191 Fired immediately after the map has been resized. *** ### rotate > **rotate**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:373 Fired repeatedly during a "drag to rotate" interaction. See [DragRotateHandler](../classes/DragRotateHandler.md). *** ### rotateend > **rotateend**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:377 Fired when a "drag to rotate" interaction ends. See [DragRotateHandler](../classes/DragRotateHandler.md). *** ### rotatestart > **rotatestart**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `undefined`\> Defined in: src/ui/events.ts:369 Fired when a "drag to rotate" interaction starts. See [DragRotateHandler](../classes/DragRotateHandler.md). *** ### sourcedata > **sourcedata**: [`MapSourceDataEvent`](MapSourceDataEvent.md) Defined in: src/ui/events.ts:227 Fired when one of the map's sources loads or changes, including if a tile belonging to a source loads or changes. *** ### sourcedataabort > **sourcedataabort**: [`MapSourceDataEvent`](MapSourceDataEvent.md) Defined in: src/ui/events.ts:246 Fired when a request for one of the map's sources' data is aborted. *** ### sourcedataloading > **sourcedataloading**: [`MapSourceDataEvent`](MapSourceDataEvent.md) Defined in: src/ui/events.ts:216 Fired when one of the map's sources begins loading or changing asynchronously. All `sourcedataloading` events are followed by a `sourcedata`, `sourcedataabort` or `error` event. *** ### styledata > **styledata**: [`MapStyleDataEvent`](MapStyleDataEvent.md) Defined in: src/ui/events.ts:231 Fired when the map's style loads or changes. *** ### styledataloading > **styledataloading**: [`MapStyleDataEvent`](MapStyleDataEvent.md) Defined in: src/ui/events.ts:222 Fired when the map's style begins loading or changing asynchronously. All `styledataloading` events are followed by a `styledata` or `error` event. *** ### styleimagemissing > **styleimagemissing**: [`MapStyleImageMissingEvent`](MapStyleImageMissingEvent.md) Defined in: src/ui/events.ts:238 Fired when an icon or pattern needed by the style is missing. The missing image can be added with [Map#addImage](../classes/Map.md#addimage) within this event listener callback to prevent the image from being skipped. This event can be used to dynamically generate icons and patterns. #### See [Generate and add a missing icon to the map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image-missing-generated/) *** ### terrain > **terrain**: [`MapTerrainEvent`](MapTerrainEvent.md) Defined in: src/ui/events.ts:414 Fired when terrain is changed *** ### touchcancel > **touchcancel**: [`MapTouchEvent`](../classes/MapTouchEvent.md) Defined in: src/ui/events.ts:263 Fired when a [`touchcancel`](https://developer.mozilla.org/en-US/docs/Web/Events/touchcancel) event occurs within the map. *** ### touchend > **touchend**: [`MapTouchEvent`](../classes/MapTouchEvent.md) Defined in: src/ui/events.ts:273 Fired when a [`touchend`](https://developer.mozilla.org/en-US/docs/Web/Events/touchend) event occurs within the map. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### touchmove > **touchmove**: [`MapTouchEvent`](../classes/MapTouchEvent.md) Defined in: src/ui/events.ts:268 Fired when a [`touchmove`](https://developer.mozilla.org/en-US/docs/Web/Events/touchmove) event occurs within the map. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### touchstart > **touchstart**: [`MapTouchEvent`](../classes/MapTouchEvent.md) Defined in: src/ui/events.ts:278 Fired when a [`touchstart`](https://developer.mozilla.org/en-US/docs/Web/Events/touchstart) event occurs within the map. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### webglcontextlost > **webglcontextlost**: [`MapContextEvent`](MapContextEvent.md) Defined in: src/ui/events.ts:195 Fired when the WebGL context is lost. *** ### webglcontextrestored > **webglcontextrestored**: [`MapContextEvent`](MapContextEvent.md) Defined in: src/ui/events.ts:199 Fired when the WebGL context is restored. *** ### wheel > **wheel**: [`MapWheelEvent`](../classes/MapWheelEvent.md) Defined in: src/ui/events.ts:410 Fired when a [`wheel`](https://developer.mozilla.org/en-US/docs/Web/Events/wheel) event occurs within the map. *** ### zoom > **zoom**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `WheelEvent` \| `undefined`\> Defined in: src/ui/events.ts:360 Fired repeatedly during an animated transition from one zoom level to another, as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto). *** ### zoomend > **zoomend**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `WheelEvent` \| `undefined`\> Defined in: src/ui/events.ts:365 Fired just after the map completes a transition from one zoom level to another, as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto). *** ### zoomstart > **zoomstart**: [`MapLibreEvent`](MapLibreEvent.md)\<`MouseEvent` \| `TouchEvent` \| `WheelEvent` \| `undefined`\> Defined in: src/ui/events.ts:355 Fired just before the map begins a transition from one zoom level to another, as the result of either user interaction or methods such as [Map#flyTo](../classes/Map.md#flyto). --- # MapGeoJSONFeature https://docs.mapatlas.xyz/overview/API/type-aliases/MapGeoJSONFeature # MapGeoJSONFeature > **MapGeoJSONFeature** = [`GeoJSONFeature`](../classes/GeoJSONFeature.md) & `object` Defined in: src/util/vectortile\_to\_geojson.ts:18 An extended geojson feature used by the events to return data to the listener ## Type declaration ### layer > **layer**: [`DistributiveOmit`](DistributiveOmit.md)\<[`LayerSpecification`](https://mapmetrics.org/mapmetrics-style-spec/layers/), `"source"`\> & `object` #### Type declaration ##### source > **source**: `string` ### source > **source**: `string` ### sourceLayer? > `optional` **sourceLayer**: `string` ### state > **state**: `object` #### Index Signature \[`key`: `string`\]: `any` --- # MapLayerEventType https://docs.mapatlas.xyz/overview/API/type-aliases/MapLayerEventType # MapLayerEventType > **MapLayerEventType** = `object` Defined in: src/ui/events.ts:49 `MapLayerEventType` - a mapping between the event name and the event. **Note:** These events are compatible with the optional `layerId` parameter. If `layerId` is included as the second argument in [Map#on](../classes/Map.md#on), the event listener will fire only when the event action contains a visible portion of the specified layer. The following example can be used for all the events. ## Example ```ts // Initialize the map let map = new Map({ // map options }); // Set an event listener for a specific layer map.on('the-event-name', 'poi-label', (e) => { console.log('An event has occurred on a visible portion of the poi-label layer'); }); ``` ## Properties ### click > **click**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:56 Fired when a pointing device (usually a mouse) is pressed and released contains a visible portion of the specified layer. #### See - [Measure distances](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/measure/) - [Center the map on a clicked symbol](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/center-on-symbol/) *** ### contextmenu > **contextmenu**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:114 Fired when the right button of the mouse is clicked or the context menu key is pressed within visible portion of the specified layer. *** ### dblclick > **dblclick**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:62 Fired when a pointing device (usually a mouse) is pressed and released twice contains a visible portion of the specified layer. **Note:** Under normal conditions, this event will be preceded by two `click` events. *** ### mousedown > **mousedown**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:67 Fired when a pointing device (usually a mouse) is pressed while inside a visible portion of the specified layer. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### mouseenter > **mouseenter**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:90 Fired when a pointing device (usually a mouse) enters a visible portion of a specified layer from outside that layer or outside the map canvas. #### See - [Center the map on a clicked symbol](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/center-on-symbol/) - [Display a popup on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-click/) *** ### mouseleave > **mouseleave**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:98 Fired when a pointing device (usually a mouse) leaves a visible portion of a specified layer, or leaves the map canvas. #### See - [Highlight features under the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) - [Display a popup on click](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-click/) *** ### mousemove > **mousemove**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:82 Fired when a pointing device (usually a mouse) is moved while the cursor is inside a visible portion of the specified layer. As you move the cursor across the layer, the event will fire every time the cursor changes position within that layer. #### See - [Get coordinates of the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/mouse-position/) - [Highlight features under the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) - [Display a popup on over](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) - [Animate symbol to follow the mouse](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/animate-symbol-to-follow-mouse/) *** ### mouseout > **mouseout**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:110 Fired when a point device (usually a mouse) leaves the visible portion of the specified layer. *** ### mouseover > **mouseover**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:106 Fired when a pointing device (usually a mouse) is moved inside a visible portion of the specified layer. #### See - [Get coordinates of the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/mouse-position/) - [Highlight features under the mouse pointer](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/hover-styles/) - [Display a popup on hover](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/popup-on-hover/) *** ### mouseup > **mouseup**: [`MapLayerMouseEvent`](MapLayerMouseEvent.md) Defined in: src/ui/events.ts:72 Fired when a pointing device (usually a mouse) is released while inside a visible portion of the specified layer. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### touchcancel > **touchcancel**: [`MapLayerTouchEvent`](MapLayerTouchEvent.md) Defined in: src/ui/events.ts:129 Fired when a [`touchstart`](https://developer.mozilla.org/en-US/docs/Web/Events/touchstart) event occurs within the visible portion of the specified layer. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### touchend > **touchend**: [`MapLayerTouchEvent`](MapLayerTouchEvent.md) Defined in: src/ui/events.ts:124 Fired when a [`touchend`](https://developer.mozilla.org/en-US/docs/Web/Events/touchend) event occurs within the visible portion of the specified layer. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) *** ### touchstart > **touchstart**: [`MapLayerTouchEvent`](MapLayerTouchEvent.md) Defined in: src/ui/events.ts:119 Fired when a [`touchstart`](https://developer.mozilla.org/en-US/docs/Web/Events/touchstart) event occurs within the visible portion of the specified layer. #### See [Create a draggable point](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/drag-a-point/) --- # MapLayerMouseEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapLayerMouseEvent # MapLayerMouseEvent > **MapLayerMouseEvent** = [`MapMouseEvent`](../classes/MapMouseEvent.md) & `object` Defined in: src/ui/events.ts:17 An event from the mouse relevant to a specific layer. ## Type declaration ### features? > `optional` **features**: [`MapGeoJSONFeature`](MapGeoJSONFeature.md)[] --- # MapLayerTouchEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapLayerTouchEvent # MapLayerTouchEvent > **MapLayerTouchEvent** = [`MapTouchEvent`](../classes/MapTouchEvent.md) & `object` Defined in: src/ui/events.ts:24 An event from a touch device relevant to a specific layer. ## Type declaration ### features? > `optional` **features**: [`MapGeoJSONFeature`](MapGeoJSONFeature.md)[] --- # mapmetricsEvent\ https://docs.mapatlas.xyz/overview/API/type-aliases/MapLibreEvent # mapmetricsEvent\ > **mapmetricsEvent**\<`TOrig`\> = `object` Defined in: src/ui/events.ts:433 The base event for mapmetrics ## Type Parameters | Type Parameter | Default type | | ------ | ------ | | `TOrig` | `unknown` | --- # mapmetricsZoomEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapLibreZoomEvent # mapmetricsZoomEvent > **mapmetricsZoomEvent** = `object` Defined in: src/ui/events.ts:678 A `mapmetricsZoomEvent` is the event type for the boxzoom-related map events emitted by the [BoxZoomHandler](../classes/BoxZoomHandler.md). ## Properties ### originalEvent > **originalEvent**: `MouseEvent` Defined in: src/ui/events.ts:690 The DOM event that triggered the boxzoom event. Can be a `MouseEvent` or `KeyboardEvent` *** ### target > **target**: [`Map`](../classes/Map.md) Defined in: src/ui/events.ts:686 The `Map` instance that triggered the event *** ### type > **type**: `"boxzoomstart"` \| `"boxzoomend"` \| `"boxzoomcancel"` Defined in: src/ui/events.ts:682 The type of boxzoom event. One of `boxzoomstart`, `boxzoomend` or `boxzoomcancel` --- # MapOptions https://docs.mapatlas.xyz/overview/API/type-aliases/MapOptions # MapOptions > **MapOptions** = `object` Defined in: src/ui/map.ts:80 The [Map](../classes/Map.md) options object. ## Properties ### attributionControl? > `optional` **attributionControl**: `false` \| [`AttributionControlOptions`](AttributionControlOptions.md) Defined in: src/ui/map.ts:112 If set, an [AttributionControl](../classes/AttributionControl.md) will be added to the map with the provided options. To disable the attribution control, pass `false`. Note: showing the logo of mapmetrics is not required for using mapmetrics. #### Default Value ```ts compact: true, customAttribution: "mapmetrics ...". ``` *** ### bearing? > `optional` **bearing**: `number` Defined in: src/ui/map.ts:227 The initial bearing (rotation) of the map, measured in degrees counter-clockwise from north. If `bearing` is not specified in the constructor options, mapmetrics GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. #### Default Value ```ts 0 ``` *** ### bearingSnap? > `optional` **bearingSnap**: `number` Defined in: src/ui/map.ts:105 The threshold, measured in degrees, that determines when the map's bearing will snap to north. For example, with a `bearingSnap` of 7, if the user rotates the map within 7 degrees of north, the map will automatically snap to exact north. #### Default Value ```ts 7 ``` *** ### bounds? > `optional` **bounds**: [`LngLatBoundsLike`](LngLatBoundsLike.md) Defined in: src/ui/map.ts:298 The initial bounds of the map. If `bounds` is specified, it overrides `center` and `zoom` constructor options. *** ### boxZoom? > `optional` **boxZoom**: `boolean` Defined in: src/ui/map.ts:167 If `true`, the "box zoom" interaction is enabled (see [BoxZoomHandler](../classes/BoxZoomHandler.md)). #### Default Value ```ts true ``` *** ### cancelPendingTileRequestsWhileZooming? > `optional` **cancelPendingTileRequestsWhileZooming**: `boolean` Defined in: src/ui/map.ts:351 Determines whether to cancel, or retain, tiles from the current viewport which are still loading but which belong to a farther (smaller) zoom level than the current one. * If `true`, when zooming in, tiles which didn't manage to load for previous zoom levels will become canceled. This might save some computing resources for slower devices, but the map details might appear more abruptly at the end of the zoom. * If `false`, when zooming in, the previous zoom level(s) tiles will progressively appear, giving a smoother map details experience. However, more tiles will be rendered in a short period of time. #### Default Value ```ts true ``` *** ### canvasContextAttributes? > `optional` **canvasContextAttributes**: `WebGLContextAttributesWithType` Defined in: src/ui/map.ts:128 Set of WebGLContextAttributes that are applied to the WebGL context of the map. See https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext for more details. `contextType` can be set to `webgl2` or `webgl` to force a WebGL version. Not setting it, mapmetrics will do it's best to get a suitable context. #### Default Value ```ts antialias: false, powerPreference: 'high-performance', preserveDrawingBuffer: false, failIfMajorPerformanceCaveat: false, desynchronized: false, contextType: 'webgl2withfallback' ``` *** ### center? > `optional` **center**: [`LngLatLike`](LngLatLike.md) Defined in: src/ui/map.ts:212 The initial geographical centerpoint of the map. If `center` is not specified in the constructor options, mapmetrics GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `[0, 0]` Note: mapmetrics GL JS uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON. #### Default Value ```ts [0, 0] ``` *** ### centerClampedToGround? > `optional` **centerClampedToGround**: `boolean` Defined in: src/ui/map.ts:358 If true, the elevation of the center point will automatically be set to the terrain elevation (or zero if terrain is not enabled). If false, the elevation of the center point will default to sea level and will not automatically update. Defaults to true. Needs to be set to false to keep the camera above ground when pitch \> 90 degrees. *** ### clickTolerance? > `optional` **clickTolerance**: `number` Defined in: src/ui/map.ts:294 The max number of pixels a user can shift the mouse pointer during a click for it to be considered a valid click (as opposed to a mouse drag). #### Default Value ```ts 3 ``` *** ### collectResourceTiming? > `optional` **collectResourceTiming**: `boolean` Defined in: src/ui/map.ts:289 If `true`, Resource Timing API information will be collected for requests made by GeoJSON and Vector Tile web workers (this information is normally inaccessible from the main Javascript thread). Information will be returned in a `resourceTiming` property of relevant `data` events. #### Default Value ```ts false ``` *** ### container > **container**: `HTMLElement` \| `string` Defined in: src/ui/map.ts:98 The HTML element in which mapmetrics GL JS will render the map, or the element's string `id`. The specified element must have no children. *** ### cooperativeGestures? > `optional` **cooperativeGestures**: [`GestureOptions`](GestureOptions.md) Defined in: src/ui/map.ts:202 If `true` or set to an options object, the map is only accessible on desktop while holding Command/Ctrl and only accessible on mobile with two fingers. Interacting with the map using normal gestures will trigger an informational screen. With this option enabled, "drag to pitch" requires a three-finger gesture. Cooperative gestures are disabled when a map enters fullscreen using [FullscreenControl](../classes/FullscreenControl.md). #### Default Value ```ts false ``` *** ### crossSourceCollisions? > `optional` **crossSourceCollisions**: `boolean` Defined in: src/ui/map.ts:284 If `true`, symbols from multiple sources can collide with each other during collision detection. If `false`, collision detection is run separately for the symbols in each source. #### Default Value ```ts true ``` *** ### doubleClickZoom? > `optional` **doubleClickZoom**: `boolean` Defined in: src/ui/map.ts:187 If `true`, the "double click to zoom" interaction is enabled (see [DoubleClickZoomHandler](../classes/DoubleClickZoomHandler.md)). #### Default Value ```ts true ``` *** ### dragPan? > `optional` **dragPan**: `boolean` \| [`DragPanOptions`](DragPanOptions.md) Defined in: src/ui/map.ts:177 If `true`, the "drag to pan" interaction is enabled. An `Object` value is passed as options to [DragPanHandler#enable](../classes/DragPanHandler.md#enable). #### Default Value ```ts true ``` *** ### dragRotate? > `optional` **dragRotate**: `boolean` Defined in: src/ui/map.ts:172 If `true`, the "drag to rotate" interaction is enabled (see [DragRotateHandler](../classes/DragRotateHandler.md)). #### Default Value ```ts true ``` *** ### elevation? > `optional` **elevation**: `number` Defined in: src/ui/map.ts:217 The elevation of the initial geographical centerpoint of the map, in meters above sea level. If `elevation` is not specified in the constructor options, it will default to `0`. #### Default Value ```ts 0 ``` *** ### fadeDuration? > `optional` **fadeDuration**: `number` Defined in: src/ui/map.ts:279 Controls the duration of the fade-in/fade-out animation for label collisions after initial map load, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading. #### Default Value ```ts 300 ``` *** ### fitBoundsOptions? > `optional` **fitBoundsOptions**: [`FitBoundsOptions`](FitBoundsOptions.md) Defined in: src/ui/map.ts:302 A [FitBoundsOptions](FitBoundsOptions.md) options object to use _only_ when fitting the initial `bounds` provided above. *** ### hash? > `optional` **hash**: `boolean` \| `string` Defined in: src/ui/map.ts:89 If `true`, the map's position (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL. For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`. An additional string may optionally be provided to indicate a parameter-styled hash, e.g. http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where foo is a custom parameter and bar is an arbitrary hash distinct from the map hash. #### Default Value ```ts false ``` *** ### interactive? > `optional` **interactive**: `boolean` Defined in: src/ui/map.ts:94 If `false`, no mouse, touch, or keyboard listeners will be attached to the map, so it will not respond to interaction. #### Default Value ```ts true ``` *** ### keyboard? > `optional` **keyboard**: `boolean` Defined in: src/ui/map.ts:182 If `true`, keyboard shortcuts are enabled (see [KeyboardHandler](../classes/KeyboardHandler.md)). #### Default Value ```ts true ``` *** ### locale? > `optional` **locale**: `any` Defined in: src/ui/map.ts:274 A patch to apply to the default localization table for UI strings, e.g. control tooltips. The `locale` object maps namespaced UI string IDs to translated strings in the target language; see `src/ui/default_locale.js` for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table). #### Default Value ```ts null ``` *** ### localIdeographFontFamily? > `optional` **localIdeographFontFamily**: `string` \| `false` Defined in: src/ui/map.ts:311 Defines a CSS font-family for locally overriding generation of Chinese, Japanese, and Korean characters. For these characters, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). Set to `false`, to enable font settings from the map's style for these glyph ranges. The purpose of this option is to avoid bandwidth-intensive glyph server requests. (See [Use locally generated ideographs](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/local-ideographs).) #### Default Value ```ts 'sans-serif' ``` *** ### logoPosition? > `optional` **logoPosition**: [`ControlPosition`](ControlPosition.md) Defined in: src/ui/map.ts:121 A string representing the position of the mapmetrics wordmark on the map. Valid options are `top-left`,`top-right`, `bottom-left`, or `bottom-right`. #### Default Value ```ts 'bottom-left' ``` *** ### mapmetricsLogo? > `optional` **mapmetricsLogo**: `boolean` Defined in: src/ui/map.ts:116 If `true`, the mapmetrics logo will be shown. *** ### maxBounds? > `optional` **maxBounds**: [`LngLatBoundsLike`](LngLatBoundsLike.md) Defined in: src/ui/map.ts:137 If set, the map will be constrained to the given bounds. *** ### maxCanvasSize? > `optional` **maxCanvasSize**: \[`number`, `number`\] Defined in: src/ui/map.ts:344 The canvas' `width` and `height` max size. The values are passed as an array where the first element is max width and the second element is max height. You shouldn't set this above WebGl `MAX_TEXTURE_SIZE`. #### Default Value ```ts [4096, 4096]. ``` *** ### maxPitch? > `optional` **maxPitch**: `number` \| `null` Defined in: src/ui/map.ts:162 The maximum pitch of the map (0-180). #### Default Value ```ts 60 ``` *** ### maxTileCacheSize? > `optional` **maxTileCacheSize**: `number` \| `null` Defined in: src/ui/map.ts:252 The maximum number of tiles stored in the tile cache for a given source. If omitted, the cache will be dynamically sized based on the current viewport which can be set using `maxTileCacheZoomLevels` constructor options. #### Default Value ```ts null ``` *** ### maxTileCacheZoomLevels? > `optional` **maxTileCacheZoomLevels**: `number` Defined in: src/ui/map.ts:257 The maximum number of zoom levels for which to store tiles for a given source. Tile cache dynamic size is calculated by multiplying `maxTileCacheZoomLevels` with the approximate number of tiles in the viewport for a given source. #### Default Value ```ts 5 ``` *** ### maxZoom? > `optional` **maxZoom**: `number` \| `null` Defined in: src/ui/map.ts:152 The maximum zoom level of the map (0-24). #### Default Value ```ts 22 ``` *** ### minPitch? > `optional` **minPitch**: `number` \| `null` Defined in: src/ui/map.ts:157 The minimum pitch of the map (0-180). #### Default Value ```ts 0 ``` *** ### minZoom? > `optional` **minZoom**: `number` \| `null` Defined in: src/ui/map.ts:147 The minimum zoom level of the map (0-24). #### Default Value ```ts 0 ``` *** ### pitch? > `optional` **pitch**: `number` Defined in: src/ui/map.ts:232 The initial pitch (tilt) of the map, measured in degrees away from the plane of the screen (0-85). If `pitch` is not specified in the constructor options, mapmetrics GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the mapmetrics project. #### Default Value ```ts 0 ``` *** ### pitchWithRotate? > `optional` **pitchWithRotate**: `boolean` Defined in: src/ui/map.ts:323 If `false`, the map's pitch (tilt) control with "drag to rotate" interaction will be disabled. #### Default Value ```ts true ``` *** ### pixelRatio? > `optional` **pixelRatio**: `number` Defined in: src/ui/map.ts:333 The pixel ratio. The canvas' `width` attribute will be `container.clientWidth * pixelRatio` and its `height` attribute will be `container.clientHeight * pixelRatio`. Defaults to `devicePixelRatio` if not specified. *** ### refreshExpiredTiles? > `optional` **refreshExpiredTiles**: `boolean` Defined in: src/ui/map.ts:133 If `false`, the map won't attempt to re-request tiles once they expire per their HTTP `cacheControl`/`expires` headers. #### Default Value ```ts true ``` *** ### renderWorldCopies? > `optional` **renderWorldCopies**: `boolean` Defined in: src/ui/map.ts:247 If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire container, there will be blank space beyond 180 and -180 degrees longitude. - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the map and the other on the left edge of the map) at every zoom level. #### Default Value ```ts true ``` *** ### roll? > `optional` **roll**: `number` Defined in: src/ui/map.ts:237 The initial roll angle of the map, measured in degrees counter-clockwise about the camera boresight. If `roll` is not specified in the constructor options, mapmetrics GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. #### Default Value ```ts 0 ``` *** ### rollEnabled? > `optional` **rollEnabled**: `boolean` Defined in: src/ui/map.ts:328 If `false`, the map's roll control with "drag to rotate" interaction will be disabled. #### Default Value ```ts false ``` *** ### scrollZoom? > `optional` **scrollZoom**: `boolean` \| [`AroundCenterOptions`](AroundCenterOptions.md) Defined in: src/ui/map.ts:142 If `true`, the "scroll to zoom" interaction is enabled. [AroundCenterOptions](AroundCenterOptions.md) are passed as options to [ScrollZoomHandler#enable](../classes/ScrollZoomHandler.md#enable). #### Default Value ```ts true ``` *** ### style? > `optional` **style**: `StyleSpecification` \| `string` Defined in: src/ui/map.ts:318 The map's mapmetrics style. This must be a JSON object conforming to the schema described in the [mapmetrics Style Specification](https://mapmetrics.org/mapmetrics-style-spec/), or a URL to such JSON. When the style is not specified, calling [Map#setStyle](../classes/Map.md#setstyle) is required to render the map. *** ### touchPitch? > `optional` **touchPitch**: `boolean` \| [`AroundCenterOptions`](AroundCenterOptions.md) Defined in: src/ui/map.ts:197 If `true`, the "drag to pitch" interaction is enabled. An `Object` value is passed as options to [TwoFingersTouchPitchHandler#enable](../classes/TwoFingersTouchPitchHandler.md#enable). #### Default Value ```ts true ``` *** ### touchZoomRotate? > `optional` **touchZoomRotate**: `boolean` \| [`AroundCenterOptions`](AroundCenterOptions.md) Defined in: src/ui/map.ts:192 If `true`, the "pinch to rotate and zoom" interaction is enabled. An `Object` value is passed as options to [TwoFingersTouchZoomRotateHandler#enable](../classes/TwoFingersTouchZoomRotateHandler.md#enable). #### Default Value ```ts true ``` *** ### trackResize? > `optional` **trackResize**: `boolean` Defined in: src/ui/map.ts:207 If `true`, the map will automatically resize when the browser window resizes. #### Default Value ```ts true ``` *** ### transformCameraUpdate? > `optional` **transformCameraUpdate**: [`CameraUpdateTransformFunction`](CameraUpdateTransformFunction.md) \| `null` Defined in: src/ui/map.ts:269 A callback run before the map's camera is moved due to user input or animation. The callback can be used to modify the new center, zoom, pitch and bearing. Expected to return an object containing center, zoom, pitch or bearing values to overwrite. #### Default Value ```ts null ``` *** ### transformRequest? > `optional` **transformRequest**: [`RequestTransformFunction`](RequestTransformFunction.md) \| `null` Defined in: src/ui/map.ts:263 A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests. Expected to return an object with a `url` property and optionally `headers` and `credentials` properties. #### Default Value ```ts null ``` *** ### validateStyle? > `optional` **validateStyle**: `boolean` Defined in: src/ui/map.ts:338 If false, style validation will be skipped. Useful in production environment. #### Default Value ```ts true ``` *** ### zoom? > `optional` **zoom**: `number` Defined in: src/ui/map.ts:222 The initial zoom level of the map. If `zoom` is not specified in the constructor options, mapmetrics GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. #### Default Value ```ts 0 ``` --- # MapProjectionEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapProjectionEvent # MapProjectionEvent > **MapProjectionEvent** = `object` Defined in: src/ui/events.ts:750 The map projection event ## Properties ### newProjection > **newProjection**: [`ProjectionSpecification`](https://mapmetrics.org/mapmetrics-style-spec/projection/)\[`"type"`\] Defined in: src/ui/events.ts:760 Specifies the name of the new projection. For example: - `globe` to describe globe that has internally switched to mercator - `vertical-perspective` to describe a globe that doesn't change to mercator - `mercator` to describe mercator projection --- # MapSourceDataEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapSourceDataEvent # MapSourceDataEvent > **MapSourceDataEvent** = [`MapLibreEvent`](MapLibreEvent.md) & `object` Defined in: src/ui/events.ts:453 The source data event interface ## Type declaration ### dataType > **dataType**: `"source"` ### isSourceLoaded > **isSourceLoaded**: `boolean` True if the event has a `dataType` of `source` and the source has no outstanding network requests. ### source > **source**: [`SourceSpecification`](https://mapmetrics.org/mapmetrics-style-spec/sources/) The [style spec representation of the source](https://mapmetrics.org/mapmetrics-style-spec/#sources) if the event has a `dataType` of `source`. ### sourceDataChanged? > `optional` **sourceDataChanged**: `boolean` ### sourceDataType > **sourceDataType**: [`MapSourceDataType`](MapSourceDataType.md) ### sourceId > **sourceId**: `string` ### tile > **tile**: `any` The tile being loaded or changed, if the event has a `dataType` of `source` and the event is related to loading of a tile. --- # MapSourceDataType https://docs.mapatlas.xyz/overview/API/type-aliases/MapSourceDataType # MapSourceDataType > **MapSourceDataType** = `"content"` \| `"metadata"` \| `"visibility"` \| `"idle"` Defined in: src/ui/events.ts:29 The source event data type --- # MapStyleDataEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapStyleDataEvent # MapStyleDataEvent > **MapStyleDataEvent** = [`MapLibreEvent`](MapLibreEvent.md) & `object` Defined in: src/ui/events.ts:444 The style data event ## Type declaration ### dataType > **dataType**: `"style"` --- # MapStyleImageMissingEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapStyleImageMissingEvent # MapStyleImageMissingEvent > **MapStyleImageMissingEvent** = [`MapLibreEvent`](MapLibreEvent.md) & `object` Defined in: src/ui/events.ts:780 The style image missing event ## Type declaration ### id > **id**: `string` ### type > **type**: `"styleimagemissing"` ## See [Generate and add a missing icon to the map](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/add-image-missing-generated/) --- # MapTerrainEvent https://docs.mapatlas.xyz/overview/API/type-aliases/MapTerrainEvent # MapTerrainEvent > **MapTerrainEvent** = `object` Defined in: src/ui/events.ts:741 The terrain event --- # MarkerOptions https://docs.mapatlas.xyz/overview/API/type-aliases/MarkerOptions # MarkerOptions > **MarkerOptions** = `object` Defined in: src/ui/marker.ts:23 The [Marker](../classes/Marker.md) options object ## Properties ### anchor? > `optional` **anchor**: [`PositionAnchor`](PositionAnchor.md) Defined in: src/ui/marker.ts:41 A string indicating the part of the Marker that should be positioned closest to the coordinate set via [Marker#setLngLat](../classes/Marker.md#setlnglat). Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. #### Default Value ```ts 'center' ``` *** ### className? > `optional` **className**: `string` Defined in: src/ui/marker.ts:31 Space-separated CSS class names to add to marker element. *** ### clickTolerance? > `optional` **clickTolerance**: `number` Defined in: src/ui/marker.ts:61 The max number of pixels a user can shift the mouse pointer during a click on the marker for it to be considered a valid click (as opposed to a marker drag). The default is to inherit map's clickTolerance. #### Default Value ```ts 0 ``` *** ### color? > `optional` **color**: `string` Defined in: src/ui/marker.ts:46 The color to use for the default marker if options.element is not provided. The default is light blue. #### Default Value ```ts '#3FB1CE' ``` *** ### draggable? > `optional` **draggable**: `boolean` Defined in: src/ui/marker.ts:56 A boolean indicating whether or not a marker is able to be dragged to a new position on the map. #### Default Value ```ts false ``` *** ### element? > `optional` **element**: `HTMLElement` Defined in: src/ui/marker.ts:27 DOM element to use as a marker. The default is a light blue, droplet-shaped SVG marker. *** ### offset? > `optional` **offset**: [`PointLike`](PointLike.md) Defined in: src/ui/marker.ts:35 The offset in pixels as a [PointLike](PointLike.md) object to apply relative to the element's center. Negatives indicate left and up. *** ### opacity? > `optional` **opacity**: `string` Defined in: src/ui/marker.ts:81 Marker's opacity when it's in clear view (not behind 3d terrain) #### Default Value ```ts 1 ``` *** ### opacityWhenCovered? > `optional` **opacityWhenCovered**: `string` Defined in: src/ui/marker.ts:86 Marker's opacity when it's behind 3d terrain #### Default Value ```ts 0.2 ``` *** ### pitchAlignment? > `optional` **pitchAlignment**: [`Alignment`](Alignment.md) Defined in: src/ui/marker.ts:76 `map` aligns the `Marker` to the plane of the map. `viewport` aligns the `Marker` to the plane of the viewport. `auto` automatically matches the value of `rotationAlignment`. #### Default Value ```ts 'auto' ``` *** ### rotation? > `optional` **rotation**: `number` Defined in: src/ui/marker.ts:66 The rotation angle of the marker in degrees, relative to its respective `rotationAlignment` setting. A positive value will rotate the marker clockwise. #### Default Value ```ts 0 ``` *** ### rotationAlignment? > `optional` **rotationAlignment**: [`Alignment`](Alignment.md) Defined in: src/ui/marker.ts:71 `map` aligns the `Marker`'s rotation relative to the map, maintaining a bearing as the map rotates. `viewport` aligns the `Marker`'s rotation relative to the viewport, agnostic to map rotations. `auto` is equivalent to `viewport`. #### Default Value ```ts 'auto' ``` *** ### scale? > `optional` **scale**: `number` Defined in: src/ui/marker.ts:51 The scale to use for the default marker if options.element is not provided. The default scale corresponds to a height of `41px` and a width of `27px`. #### Default Value ```ts 1 ``` *** ### subpixelPositioning? > `optional` **subpixelPositioning**: `boolean` Defined in: src/ui/marker.ts:92 If `true`, rounding is disabled for placement of the marker, allowing for subpixel positioning and smoother movement when the marker is translated. #### Default Value ```ts false ``` --- # MessageData https://docs.mapatlas.xyz/overview/API/type-aliases/MessageData # MessageData > **MessageData** = `object` Defined in: src/util/actor.ts:23 This is used to define the parameters of the message that is sent to the worker and back --- # NavigationControlOptions https://docs.mapatlas.xyz/overview/API/type-aliases/NavigationControlOptions # NavigationControlOptions > **NavigationControlOptions** = `object` Defined in: src/ui/control/navigation\_control.ts:14 The [NavigationControl](../classes/NavigationControl.md) options object ## Properties ### showCompass? > `optional` **showCompass**: `boolean` Defined in: src/ui/control/navigation\_control.ts:18 If `true` the compass button is included. *** ### showZoom? > `optional` **showZoom**: `boolean` Defined in: src/ui/control/navigation\_control.ts:22 If `true` the zoom-in and zoom-out buttons are included. *** ### visualizePitch? > `optional` **visualizePitch**: `boolean` Defined in: src/ui/control/navigation\_control.ts:26 If `true` the pitch is visualized by rotating X-axis of compass. *** ### visualizeRoll? > `optional` **visualizeRoll**: `boolean` Defined in: src/ui/control/navigation\_control.ts:30 If `true` the roll is visualized by rotating the compass. --- # Offset https://docs.mapatlas.xyz/overview/API/type-aliases/Offset # Offset > **Offset** = `number` \| [`PointLike`](PointLike.md) \| `{ [_ in PositionAnchor]: PointLike }` Defined in: src/ui/popup.ts:34 A pixel offset specified as: - A single number specifying a distance from the location - A [PointLike](PointLike.md) specifying a constant offset - An object of [PointLike](PointLike.md)s specifying an offset for each anchor position Negative offsets indicate left and up. --- # OverlapMode https://docs.mapatlas.xyz/overview/API/type-aliases/OverlapMode # OverlapMode > **OverlapMode** = `"never"` \| `"always"` \| `"cooperative"` Defined in: src/style/style\_layer/overlap\_mode.ts:8 The overlap mode for properties like `icon-overlap`and `text-overlap` --- # PaddingOptions https://docs.mapatlas.xyz/overview/API/type-aliases/PaddingOptions # PaddingOptions > **PaddingOptions** = [`RequireAtLeastOne`](RequireAtLeastOne.md)\<\{ `bottom`: `number`; `left`: `number`; `right`: `number`; `top`: `number`; \}\> Defined in: src/geo/edge\_insets.ts:129 Options for setting padding on calls to methods such as [Map#fitBounds](../classes/Map.md#fitbounds), [Map#fitScreenCoordinates](../classes/Map.md#fitscreencoordinates), and [Map#setPadding](../classes/Map.md#setpadding). Adjust these options to set the amount of padding in pixels added to the edges of the canvas. Set a uniform padding on all edges or individual values for each edge. All properties of this object must be non-negative integers. ## Examples ```ts let bbox = [[-79, 43], [-73, 45]]; map.fitBounds(bbox, { padding: {top: 10, bottom:25, left: 15, right: 5} }); ``` ```ts let bbox = [[-79, 43], [-73, 45]]; map.fitBounds(bbox, { padding: 20 }); ``` ## See - [Fit to the bounds of a LineString](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/zoomto-linestring/) - [Fit a map to a bounding box](https://mapmetrics.org/mapmetrics-gl-js/docs/examples/fitbounds/) --- # PluginState https://docs.mapatlas.xyz/overview/API/type-aliases/PluginState # PluginState > **PluginState** = `object` Defined in: src/source/rtl\_text\_plugin\_status.ts:27 The RTL plugin state --- # PointLike https://docs.mapatlas.xyz/overview/API/type-aliases/PointLike # PointLike > **PointLike** = `Point` \| \[`number`, `number`\] Defined in: src/ui/camera.ts:30 A [Point](https://github.com/mapbox/point-geometry) or an array of two numbers representing `x` and `y` screen coordinates in pixels. ## Example ```ts let p1 = new Point(-77, 38); // a PointLike which is a Point let p2 = [-77, 38]; // a PointLike which is an array of two numbers ``` --- # PointProjection https://docs.mapatlas.xyz/overview/API/type-aliases/PointProjection # PointProjection > **PointProjection** = `object` Defined in: src/symbol/projection.ts:23 The result of projecting a point to the screen, with some additional information about the projection. ## Properties ### isOccluded > **isOccluded**: `boolean` Defined in: src/symbol/projection.ts:36 For complex projections (such as globe), true if the point is occluded by the projection, such as by being on the backfacing side of the globe. If the point is simply beyond the edge of the screen, this should NOT be set to false. *** ### point > **point**: `Point` Defined in: src/symbol/projection.ts:27 The projected point. *** ### signedDistanceFromCamera > **signedDistanceFromCamera**: `number` Defined in: src/symbol/projection.ts:31 The original W component of the projection. --- # PopupOptions https://docs.mapatlas.xyz/overview/API/type-aliases/PopupOptions # PopupOptions > **PopupOptions** = `object` Defined in: src/ui/popup.ts:41 The [Popup](../classes/Popup.md) options object ## Properties ### anchor? > `optional` **anchor**: [`PositionAnchor`](PositionAnchor.md) Defined in: src/ui/popup.ts:70 A string indicating the part of the Popup that should be positioned closest to the coordinate set via [Popup#setLngLat](../classes/Popup.md#setlnglat). Options are `'center'`, `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. If unset the anchor will be dynamically set to ensure the popup falls within the map container with a preference for `'bottom'`. *** ### className? > `optional` **className**: `string` Defined in: src/ui/popup.ts:78 Space-separated CSS class names to add to popup container *** ### closeButton? > `optional` **closeButton**: `boolean` Defined in: src/ui/popup.ts:46 If `true`, a close button will appear in the top right corner of the popup. #### Default Value ```ts true ``` *** ### closeOnClick? > `optional` **closeOnClick**: `boolean` Defined in: src/ui/popup.ts:51 If `true`, the popup will closed when the map is clicked. #### Default Value ```ts true ``` *** ### closeOnMove? > `optional` **closeOnMove**: `boolean` Defined in: src/ui/popup.ts:56 If `true`, the popup will closed when the map moves. #### Default Value ```ts false ``` *** ### focusAfterOpen? > `optional` **focusAfterOpen**: `boolean` Defined in: src/ui/popup.ts:61 If `true`, the popup will try to focus the first focusable element inside the popup. #### Default Value ```ts true ``` *** ### locationOccludedOpacity? > `optional` **locationOccludedOpacity**: `number` \| `string` Defined in: src/ui/popup.ts:97 Optional opacity when the location is behind the globe. Note that if a number is provided, it will be converted to a string. #### Default Value ```ts undefined ``` *** ### maxWidth? > `optional` **maxWidth**: `string` Defined in: src/ui/popup.ts:85 A string that sets the CSS property of the popup's maximum width, eg `'300px'`. To ensure the popup resizes to fit its content, set this property to `'none'`. Available values can be found here: https://developer.mozilla.org/en-US/docs/Web/CSS/max-width #### Default Value ```ts '240px' ``` *** ### offset? > `optional` **offset**: [`Offset`](Offset.md) Defined in: src/ui/popup.ts:74 A pixel offset applied to the popup's location *** ### subpixelPositioning? > `optional` **subpixelPositioning**: `boolean` Defined in: src/ui/popup.ts:91 If `true`, rounding is disabled for placement of the popup, allowing for subpixel positioning and smoother movement when the popup is translated. #### Default Value ```ts false ``` --- # PositionAnchor https://docs.mapatlas.xyz/overview/API/type-aliases/PositionAnchor # PositionAnchor > **PositionAnchor** = `"center"` \| `"top"` \| `"bottom"` \| `"left"` \| `"right"` \| `"top-left"` \| `"top-right"` \| `"bottom-left"` \| `"bottom-right"` Defined in: src/ui/anchor.ts:5 Where to position the anchor. Used by a popup and a marker. --- # PossiblyEvaluatedValue\ https://docs.mapatlas.xyz/overview/API/type-aliases/PossiblyEvaluatedValue # PossiblyEvaluatedValue\ > **PossiblyEvaluatedValue**\<`T`\> = \{ `kind`: `"constant"`; `value`: `T`; \} \| `SourceExpression` \| `CompositeExpression` Defined in: src/style/properties.ts:381 "Possibly evaluated value" is an intermediate stage in the evaluation chain for both paint and layout property values. The purpose of this stage is to optimize away unnecessary recalculations for data-driven properties. Code which uses data-driven property values must assume that the value is dependent on feature data, and request that it be evaluated for each feature. But when that property value is in fact a constant or camera function, the calculation will not actually depend on the feature, and we can benefit from returning the prior result of having done the evaluation once, ahead of time, in an intermediate step whose inputs are just the value and "global" parameters such as current zoom level. `PossiblyEvaluatedValue` represents the three possible outcomes of this step: if the input value was a constant or camera expression, then the "possibly evaluated" result is a constant value. Otherwise, the input value was either a source or composite expression, and we must defer final evaluation until supplied a feature. We separate the source and composite cases because they are handled differently when generating GL attributes, buffers, and uniforms. Note that `PossiblyEvaluatedValue` (and `PossiblyEvaluatedPropertyValue`, below) are _not_ used for properties that do not allow data-driven values. For such properties, we know that the "possibly evaluated" result is always a constant scalar value. See below. ## Type Parameters | Type Parameter | | ------ | | `T` | --- # ProjectionData https://docs.mapatlas.xyz/overview/API/type-aliases/ProjectionData # ProjectionData > **ProjectionData** = `object` Defined in: src/geo/projection/projection\_data.ts:8 This type contains all data necessary to project a tile to screen in mapmetrics's shader system. Contains data used for both mercator and globe projection. ## Properties ### clippingPlane > **clippingPlane**: \[`number`, `number`, `number`, `number`\] Defined in: src/geo/projection/projection\_data.ts:34 The plane equation for a plane that intersects the planet's horizon. Assumes the planet to be a unit sphere. Used by globe projection for clipping. Uniform name: `u_projection_clipping_plane`. *** ### fallbackMatrix > **fallbackMatrix**: `mat4` Defined in: src/geo/projection/projection\_data.ts:46 Fallback matrix that projects the current tile according to mercator projection. Used by globe projection to fall back to mercator projection in an animated way. Uniform name: `u_projection_fallback_matrix`. *** ### mainMatrix > **mainMatrix**: `mat4` Defined in: src/geo/projection/projection\_data.ts:14 The main projection matrix. For mercator projection, it usually projects in-tile coordinates 0..EXTENT to screen, for globe projection, it projects a unit sphere planet to screen. Uniform name: `u_projection_matrix`. *** ### projectionTransition > **projectionTransition**: `number` Defined in: src/geo/projection/projection\_data.ts:40 A value in range 0..1 indicating interpolation between mercator (0) and globe (1) projections. Used by globe projection to hide projection transition at high zooms. Uniform name: `u_projection_transition`. *** ### tileMercatorCoords > **tileMercatorCoords**: \[`number`, `number`, `number`, `number`\] Defined in: src/geo/projection/projection\_data.ts:27 The extent of current tile in the mercator square. Used by globe projection. First two components are X and Y offset, last two are X and Y scale. Uniform name: `u_projection_tile_mercator_coords`. Conversion from in-tile coordinates in range 0..EXTENT is done as follows: #### Example ``` vec2 mercator_coords = u_projection_tile_mercator_coords.xy + in_tile.xy * u_projection_tile_mercator_coords.zw; ``` --- # ProjectionDataParams https://docs.mapatlas.xyz/overview/API/type-aliases/ProjectionDataParams # ProjectionDataParams > **ProjectionDataParams** = `object` Defined in: src/geo/projection/projection\_data.ts:53 Parameters object for the transform's `getProjectionData` function. Contains the requested tile ID and more. ## Properties ### aligned? > `optional` **aligned**: `boolean` Defined in: src/geo/projection/projection\_data.ts:61 Set to true if a pixel-aligned matrix should be used, if possible (mostly used for raster tiles under mercator projection) *** ### applyGlobeMatrix? > `optional` **applyGlobeMatrix**: `boolean` Defined in: src/geo/projection/projection\_data.ts:69 Set to true if the globe matrix should be applied (i.e. when rendering globe) *** ### applyTerrainMatrix? > `optional` **applyTerrainMatrix**: `boolean` Defined in: src/geo/projection/projection\_data.ts:65 Set to true if the terrain matrix should be applied (i.e. when rendering terrain) *** ### overscaledTileID > **overscaledTileID**: [`OverscaledTileID`](../classes/OverscaledTileID.md) \| `null` Defined in: src/geo/projection/projection\_data.ts:57 The ID of the current tile --- # QueryRenderedFeaturesOptions https://docs.mapatlas.xyz/overview/API/type-aliases/QueryRenderedFeaturesOptions # QueryRenderedFeaturesOptions > **QueryRenderedFeaturesOptions** = `object` Defined in: src/source/query\_features.ts:21 Options to pass to query the map for the rendered features ## Properties ### availableImages? > `optional` **availableImages**: `string`[] Defined in: src/source/query\_features.ts:34 An array of string representing the available images *** ### filter? > `optional` **filter**: `FilterSpecification` Defined in: src/source/query\_features.ts:30 A [filter](https://mapmetrics.org/mapmetrics-style-spec/layers/#filter) to limit query results. *** ### layers? > `optional` **layers**: `string`[] \| `Set`\<`string`\> Defined in: src/source/query\_features.ts:26 An array or set of [style layer IDs](https://mapmetrics.org/mapmetrics-style-spec/#layer-id) for the query to inspect. Only features within these layers will be returned. If this parameter is undefined, all layers will be checked. *** ### validate? > `optional` **validate**: `boolean` Defined in: src/source/query\_features.ts:38 Whether to check if the [options.filter] conforms to the mapmetrics Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. --- # QuerySourceFeatureOptions https://docs.mapatlas.xyz/overview/API/type-aliases/QuerySourceFeatureOptions # QuerySourceFeatureOptions > **QuerySourceFeatureOptions** = `object` Defined in: src/source/query\_features.ts:48 The options object related to the [Map#querySourceFeatures](../classes/Map.md#querysourcefeatures) method ## Properties ### filter? > `optional` **filter**: `FilterSpecification` Defined in: src/source/query\_features.ts:57 A [filter](https://mapmetrics.org/mapmetrics-style-spec/layers/#filter) to limit query results. *** ### sourceLayer? > `optional` **sourceLayer**: `string` Defined in: src/source/query\_features.ts:52 The name of the source layer to query. *For vector tile sources, this parameter is required.* For GeoJSON sources, it is ignored. *** ### validate? > `optional` **validate**: `boolean` Defined in: src/source/query\_features.ts:62 Whether to check if the [parameters.filter] conforms to the mapmetrics Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. #### Default Value ```ts true ``` --- # RTLPluginStatus https://docs.mapatlas.xyz/overview/API/type-aliases/RTLPluginStatus # RTLPluginStatus > **RTLPluginStatus** = `"unavailable"` \| `"deferred"` \| `"requested"` \| `"loading"` \| `"loaded"` \| `"error"` Defined in: src/source/rtl\_text\_plugin\_status.ts:16 The possible option of the plugin's status `unavailable`: Not loaded. `deferred`: The plugin URL has been specified, but loading has been deferred. `requested`: at least one tile needs RTL to render, but the plugin has not been set `loading`: RTL is in the process of being loaded by worker. `loaded`: The plugin is now loaded `error`: The plugin failed to load --- # Rect https://docs.mapatlas.xyz/overview/API/type-aliases/Rect # Rect > **Rect** = `object` Defined in: src/render/glyph\_atlas.ts:13 A rectangle type with postion, width and height. --- # RemoveSourceParams https://docs.mapatlas.xyz/overview/API/type-aliases/RemoveSourceParams # RemoveSourceParams > **RemoveSourceParams** = `object` Defined in: src/util/actor\_messages.ts:36 Parameters needed to remove a source --- # RequestParameters https://docs.mapatlas.xyz/overview/API/type-aliases/RequestParameters # RequestParameters > **RequestParameters** = `object` Defined in: src/util/ajax.ts:32 A `RequestParameters` object to be returned from Map.options.transformRequest callbacks. ## Example ```ts // use transformRequest to modify requests that begin with `http://myHost` transformRequest: function(url, resourceType) { if (resourceType === 'Source' && url.indexOf('http://myHost') > -1) { return { url: url.replace('http', 'https'), headers: { 'my-custom-header': true }, credentials: 'include' // Include cookies for cross-origin requests } } } ``` ## Properties ### body? > `optional` **body**: `string` Defined in: src/util/ajax.ts:48 Request body. *** ### cache? > `optional` **cache**: `RequestCache` Defined in: src/util/ajax.ts:64 Parameters supported only by browser fetch API. Property of the Request interface contains the cache mode of the request. It controls how the request will interact with the browser's HTTP cache. (https://developer.mozilla.org/en-US/docs/Web/API/Request/cache) *** ### collectResourceTiming? > `optional` **collectResourceTiming**: `boolean` Defined in: src/util/ajax.ts:60 If `true`, Resource Timing API information will be collected for these transformed requests and returned in a resourceTiming property of relevant data events. *** ### credentials? > `optional` **credentials**: `"same-origin"` \| `"include"` Defined in: src/util/ajax.ts:56 `'same-origin'|'include'` Use 'include' to send cookies with cross-origin requests. *** ### headers? > `optional` **headers**: `any` Defined in: src/util/ajax.ts:40 The headers to be sent with the request. *** ### method? > `optional` **method**: `"GET"` \| `"POST"` \| `"PUT"` Defined in: src/util/ajax.ts:44 Request method `'GET' | 'POST' | 'PUT'`. *** ### type? > `optional` **type**: `"string"` \| `"json"` \| `"arrayBuffer"` \| `"image"` Defined in: src/util/ajax.ts:52 Response body type to be returned. *** ### url > **url**: `string` Defined in: src/util/ajax.ts:36 The URL to be requested. --- # RequestResponseMessageMap https://docs.mapatlas.xyz/overview/API/type-aliases/RequestResponseMessageMap # RequestResponseMessageMap > **RequestResponseMessageMap** = `object` Defined in: src/util/actor\_messages.ts:115 This is basically a mapping between all the calls that are made to and from the workers. The key is the event name, the first parameter is the event input type, and the last parameter is the output type. --- # RequestTransformFunction() https://docs.mapatlas.xyz/overview/API/type-aliases/RequestTransformFunction # RequestTransformFunction() > **RequestTransformFunction** = (`url`: `string`, `resourceType?`: [`ResourceType`](../enumerations/ResourceType.md)) => [`RequestParameters`](RequestParameters.md) \| `undefined` Defined in: src/util/request\_manager.ts:21 This function is used to tranform a request. It is used just before executing the relevant request. ## Parameters | Parameter | Type | | ------ | ------ | | `url` | `string` | | `resourceType?` | [`ResourceType`](../enumerations/ResourceType.md) | ## Returns [`RequestParameters`](RequestParameters.md) \| `undefined` --- # RequireAtLeastOne\ https://docs.mapatlas.xyz/overview/API/type-aliases/RequireAtLeastOne # RequireAtLeastOne\ > **RequireAtLeastOne**\<`T`\> = `{ [K in keyof T]-?: Required> & Partial>> }`\[keyof `T`\] Defined in: src/util/util.ts:1047 A helper to allow require of at least one property ## Type Parameters | Type Parameter | | ------ | | `T` | --- # ScaleControlOptions https://docs.mapatlas.xyz/overview/API/type-aliases/ScaleControlOptions # ScaleControlOptions > **ScaleControlOptions** = `object` Defined in: src/ui/control/scale\_control.ts:14 The [ScaleControl](../classes/ScaleControl.md) options object ## Properties ### maxWidth? > `optional` **maxWidth**: `number` Defined in: src/ui/control/scale\_control.ts:19 The maximum length of the scale control in pixels. #### Default Value ```ts 100 ``` *** ### unit? > `optional` **unit**: [`Unit`](Unit.md) Defined in: src/ui/control/scale\_control.ts:24 Unit of the distance (`'imperial'`, `'metric'` or `'nautical'`). #### Default Value ```ts 'metric' ``` --- # Serialized https://docs.mapatlas.xyz/overview/API/type-aliases/Serialized # Serialized > **Serialized** = `null` \| `void` \| `boolean` \| `number` \| `string` \| `Boolean` \| `Number` \| `String` \| `Date` \| `RegExp` \| `ArrayBuffer` \| `ArrayBufferView` \| `ImageData` \| `ImageBitmap` \| `Blob` \| `Serialized`[] \| [`SerializedObject`](SerializedObject.md) Defined in: src/util/web\_worker\_transfer.ts:17 All the possible values that can be serialized and sent to and from the worker --- # SerializedObject\ https://docs.mapatlas.xyz/overview/API/type-aliases/SerializedObject # SerializedObject\ > **SerializedObject**\<`S`\> = `object` Defined in: src/util/web\_worker\_transfer.ts:10 A class that is serialized to and json, that can be constructed back to the original class in the worker or in the main thread ## Type Parameters | Type Parameter | Default type | | ------ | ------ | | `S` *extends* [`Serialized`](Serialized.md) | `any` | ## Index Signature \[`_`: `string`\]: `S` --- # SerializedStructArray https://docs.mapatlas.xyz/overview/API/type-aliases/SerializedStructArray # SerializedStructArray > **SerializedStructArray** = `object` Defined in: src/util/struct\_array.ts:70 An array that can be deserialized --- # SetClusterOptions https://docs.mapatlas.xyz/overview/API/type-aliases/SetClusterOptions # SetClusterOptions > **SetClusterOptions** = `object` Defined in: src/source/geojson\_source.ts:41 The cluster options to set ## Properties ### cluster? > `optional` **cluster**: `boolean` Defined in: src/source/geojson\_source.ts:45 Whether or not to cluster *** ### clusterMaxZoom? > `optional` **clusterMaxZoom**: `number` Defined in: src/source/geojson\_source.ts:50 The cluster's max zoom. Non-integer values are rounded to the closest integer due to supercluster integer value requirements. *** ### clusterRadius? > `optional` **clusterRadius**: `number` Defined in: src/source/geojson\_source.ts:54 The cluster's radius --- # SourceClass() https://docs.mapatlas.xyz/overview/API/type-aliases/SourceClass # SourceClass() > **SourceClass** = (`id`: `string`, `specification`: [`SourceSpecification`](https://mapmetrics.org/mapmetrics-style-spec/sources/) \| [`CanvasSourceSpecification`](CanvasSourceSpecification.md), `dispatcher`: [`Dispatcher`](../classes/Dispatcher.md), `eventedParent`: [`Evented`](../classes/Evented.md)) => [`Source`](../interfaces/Source.md) Defined in: src/source/source.ts:129 A general definition of a [Source](../interfaces/Source.md) class for factory usage ## Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | | `specification` | [`SourceSpecification`](https://mapmetrics.org/mapmetrics-style-spec/sources/) \| [`CanvasSourceSpecification`](CanvasSourceSpecification.md) | | `dispatcher` | [`Dispatcher`](../classes/Dispatcher.md) | | `eventedParent` | [`Evented`](../classes/Evented.md) | ## Returns [`Source`](../interfaces/Source.md) --- # SpriteOnDemandStyleImage https://docs.mapatlas.xyz/overview/API/type-aliases/SpriteOnDemandStyleImage # SpriteOnDemandStyleImage > **SpriteOnDemandStyleImage** = `object` Defined in: src/style/style\_image.ts:15 The sprite data --- # StyleGlyph https://docs.mapatlas.xyz/overview/API/type-aliases/StyleGlyph # StyleGlyph > **StyleGlyph** = `object` Defined in: src/style/style\_glyph.ts:21 A style glyph type --- # StyleImage https://docs.mapatlas.xyz/overview/API/type-aliases/StyleImage # StyleImage > **StyleImage** = [`StyleImageData`](StyleImageData.md) & [`StyleImageMetadata`](StyleImageMetadata.md) Defined in: src/style/style\_image.ts:92 the style's image, including data and metedata --- # StyleImageData https://docs.mapatlas.xyz/overview/API/type-aliases/StyleImageData # StyleImageData > **StyleImageData** = `object` Defined in: src/style/style\_image.ts:26 The style's image metadata --- # StyleImageMetadata https://docs.mapatlas.xyz/overview/API/type-aliases/StyleImageMetadata # StyleImageMetadata > **StyleImageMetadata** = `object` Defined in: src/style/style\_image.ts:58 The style's image metadata ## Properties ### content? > `optional` **content**: \[`number`, `number`, `number`, `number`\] Defined in: src/style/style\_image.ts:78 If `icon-text-fit` is used in a layer with this image, this option defines the part of the image that can be covered by the content in `text-field`. *** ### pixelRatio > **pixelRatio**: `number` Defined in: src/style/style\_image.ts:62 The ratio of pixels in the image to physical pixels on the screen *** ### sdf > **sdf**: `boolean` Defined in: src/style/style\_image.ts:66 Whether the image should be interpreted as an SDF image *** ### stretchX? > `optional` **stretchX**: \[`number`, `number`\][] Defined in: src/style/style\_image.ts:70 If `icon-text-fit` is used in a layer with this image, this option defines the part(s) of the image that can be stretched horizontally. *** ### stretchY? > `optional` **stretchY**: \[`number`, `number`\][] Defined in: src/style/style\_image.ts:74 If `icon-text-fit` is used in a layer with this image, this option defines the part(s) of the image that can be stretched vertically. *** ### textFitHeight? > `optional` **textFitHeight**: [`TextFit`](../enumerations/TextFit.md) Defined in: src/style/style\_image.ts:86 If `icon-text-fit` is used in a layer with this image, this option defines constraints on the vertical scaling of the image. *** ### textFitWidth? > `optional` **textFitWidth**: [`TextFit`](../enumerations/TextFit.md) Defined in: src/style/style\_image.ts:82 If `icon-text-fit` is used in a layer with this image, this option defines constraints on the horizontal scaling of the image. --- # StyleOptions https://docs.mapatlas.xyz/overview/API/type-aliases/StyleOptions # StyleOptions > **StyleOptions** = `object` Defined in: src/style/style.ts:94 The options object related to the [Map](../classes/Map.md)'s style related methods ## Properties ### localIdeographFontFamily? > `optional` **localIdeographFontFamily**: `string` \| `false` Defined in: src/style/style.ts:106 Defines a CSS font-family for locally overriding generation of Chinese, Japanese, and Korean characters. For these characters, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). Set to `false`, to enable font settings from the map's style for these glyph ranges. Forces a full update. *** ### validate? > `optional` **validate**: `boolean` Defined in: src/style/style.ts:98 If false, style validation will be skipped. Useful in production environment. --- # StyleSetterOptions https://docs.mapatlas.xyz/overview/API/type-aliases/StyleSetterOptions # StyleSetterOptions > **StyleSetterOptions** = `object` Defined in: src/style/style.ts:112 Supporting type to add validation to another style related type ## Properties ### validate? > `optional` **validate**: `boolean` Defined in: src/style/style.ts:116 Whether to check if the filter conforms to the mapmetrics Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. --- # StyleSwapOptions https://docs.mapatlas.xyz/overview/API/type-aliases/StyleSwapOptions # StyleSwapOptions > **StyleSwapOptions** = `object` Defined in: src/style/style.ts:179 The options object related to the [Map](../classes/Map.md)'s style related methods ## Properties ### diff? > `optional` **diff**: `boolean` Defined in: src/style/style.ts:184 If false, force a 'full' update, removing the current style and building the given one instead of attempting a diff-based update. *** ### transformStyle? > `optional` **transformStyle**: [`TransformStyleFunction`](TransformStyleFunction.md) Defined in: src/style/style.ts:189 TransformStyleFunction is a convenience function that allows to modify a style after it is fetched but before it is committed to the map state. Refer to [TransformStyleFunction](TransformStyleFunction.md). --- # SymbolQuad https://docs.mapatlas.xyz/overview/API/type-aliases/SymbolQuad # SymbolQuad > **SymbolQuad** = `object` Defined in: src/symbol/quads.ts:26 A textured quad for rendering a single icon or glyph. The zoom range the glyph can be shown is defined by minScale and maxScale. ## Param The offset of the top left corner from the anchor. ## Param The offset of the top right corner from the anchor. ## Param The offset of the bottom left corner from the anchor. ## Param The offset of the bottom right corner from the anchor. ## Param The texture coordinates. --- # TileMesh https://docs.mapatlas.xyz/overview/API/type-aliases/TileMesh # TileMesh > **TileMesh** = `object` Defined in: src/util/create\_tile\_mesh.ts:47 Stores the prepared vertex and index buffer bytes for a mesh. ## Properties ### indices > **indices**: `ArrayBuffer` Defined in: src/util/create\_tile\_mesh.ts:56 The index data. Each triangle is defined by three indices. The indices may either be 16 bit or 32 bit unsigned integers, depending on the mesh creation arguments and on whether the mesh can fit into 16 bit indices. *** ### uses32bitIndices > **uses32bitIndices**: `boolean` Defined in: src/util/create\_tile\_mesh.ts:60 A helper boolean indicating whether the indices are 32 bit. *** ### vertices > **vertices**: `ArrayBuffer` Defined in: src/util/create\_tile\_mesh.ts:51 The vertex data. Each vertex is two 16 bit signed integers, one for X, one for Y. --- # TileParameters https://docs.mapatlas.xyz/overview/API/type-aliases/TileParameters # TileParameters > **TileParameters** = `object` Defined in: src/source/worker\_source.ts:21 Parameters to identify a tile --- # TileState https://docs.mapatlas.xyz/overview/API/type-aliases/TileState # TileState > **TileState** = `"loading"` \| `"loaded"` \| `"reloading"` \| `"unloaded"` \| `"errored"` \| `"expired"` Defined in: src/source/tile.ts:47 The tile's state, can be: - `loading` Tile data is in the process of loading. - `loaded` Tile data has been loaded. Tile can be rendered. - `reloading` Tile data has been loaded and is being updated. Tile can be rendered. - `unloaded` Tile data has been deleted. - `errored` Tile data was not loaded because of an error. - `expired` Tile data was previously loaded, but has expired per its HTTP headers and is in the process of refreshing. --- # TransformStyleFunction() https://docs.mapatlas.xyz/overview/API/type-aliases/TransformStyleFunction # TransformStyleFunction() > **TransformStyleFunction** = (`previous`: `StyleSpecification` \| `undefined`, `next`: `StyleSpecification`) => `StyleSpecification` Defined in: src/style/style.ts:174 Part of [Map#setStyle](../classes/Map.md#setstyle) options, transformStyle is a convenience function that allows to modify a style after it is fetched but before it is committed to the map state. This function exposes previous and next styles, it can be commonly used to support a range of functionalities like: - when previous style carries certain 'state' that needs to be carried over to a new style gracefully; - when a desired style is a certain combination of previous and incoming style; - when an incoming style requires modification based on external state. - when an incoming style uses relative paths, which need to be converted to absolute. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `previous` | `StyleSpecification` \| `undefined` | The current style. | | `next` | `StyleSpecification` | The next style. | ## Returns `StyleSpecification` resulting style that will to be applied to the map ## Example ```ts map.setStyle('https://demotiles.mapmetrics.org/style.json', { transformStyle: (previousStyle, nextStyle) => ({ ...nextStyle, // make relative sprite path like "../sprite" absolute sprite: new URL(nextStyle.sprite, "https://demotiles.mapmetrics.org/styles/osm-bright-gl-style/sprites/").href, // make relative glyphs path like "../fonts/{fontstack}/{range}.pbf" absolute glyphs: new URL(nextStyle.glyphs, "https://demotiles.mapmetrics.org/font/").href, sources: { // make relative vector url like "../../" absolute ...nextStyle.sources.map(source => { if (source.url) { source.url = new URL(source.url, "https://api.maptiler.com/tiles/osm-bright-gl-style/"); } return source; }), // copy a source from previous style 'osm': previousStyle.sources.osm }, layers: [ // background layer nextStyle.layers[0], // copy a layer from previous style previousStyle.layers[0], // other layers from the next style ...nextStyle.layers.slice(1).map(layer => { // hide the layers we don't need from demotiles style if (layer.id.startsWith('geolines')) { layer.layout = {...layer.layout || {}, visibility: 'none'}; // filter out US polygons } else if (layer.id.startsWith('coastline') || layer.id.startsWith('countries')) { layer.filter = ['!=', ['get', 'ADM0_A3'], 'USA']; } return layer; }) ] }) }); ``` --- # Unit https://docs.mapatlas.xyz/overview/API/type-aliases/Unit # Unit > **Unit** = `"imperial"` \| `"metric"` \| `"nautical"` Defined in: src/ui/control/scale\_control.ts:9 The unit type for length to use for the [ScaleControl](../classes/ScaleControl.md) --- # UpdateImageOptions https://docs.mapatlas.xyz/overview/API/type-aliases/UpdateImageOptions # UpdateImageOptions > **UpdateImageOptions** = `object` Defined in: src/source/image\_source.ts:32 The options object for the [ImageSource#updateImage](../classes/ImageSource.md#updateimage) method ## Properties ### coordinates? > `optional` **coordinates**: [`Coordinates`](Coordinates.md) Defined in: src/source/image\_source.ts:40 The image coordinates *** ### url > **url**: `string` Defined in: src/source/image\_source.ts:36 Required image URL. --- # UpdateLayersParameters https://docs.mapatlas.xyz/overview/API/type-aliases/UpdateLayersParameters # UpdateLayersParameters > **UpdateLayersParameters** = `object` Defined in: src/util/actor\_messages.ts:44 Parameters needed to update the layers --- # WorkerDEMTileParameters https://docs.mapatlas.xyz/overview/API/type-aliases/WorkerDEMTileParameters # WorkerDEMTileParameters > **WorkerDEMTileParameters** = [`TileParameters`](TileParameters.md) & `object` Defined in: src/source/worker\_source.ts:48 The parameters needed in order to load a DEM tile ## Type declaration ### baseShift > **baseShift**: `number` ### blueFactor > **blueFactor**: `number` ### encoding > **encoding**: [`DEMEncoding`](DEMEncoding.md) ### greenFactor > **greenFactor**: `number` ### rawImageData > **rawImageData**: [`RGBAImage`](../classes/RGBAImage.md) \| `ImageBitmap` \| `ImageData` ### redFactor > **redFactor**: `number` --- # WorkerTileParameters https://docs.mapatlas.xyz/overview/API/type-aliases/WorkerTileParameters # WorkerTileParameters > **WorkerTileParameters** = [`TileParameters`](TileParameters.md) & `object` Defined in: src/source/worker\_source.ts:30 Parameters that are send when requesting to load a tile to the worker ## Type declaration ### collectResourceTiming? > `optional` **collectResourceTiming**: `boolean` ### globalState > **globalState**: `Record`\<`string`, `any`\> ### maxZoom? > `optional` **maxZoom**: `number` ### pixelRatio > **pixelRatio**: `number` ### promoteId > **promoteId**: `PromoteIdSpecification` ### request? > `optional` **request**: [`RequestParameters`](RequestParameters.md) ### returnDependencies? > `optional` **returnDependencies**: `boolean` ### showCollisionBoxes > **showCollisionBoxes**: `boolean` ### subdivisionGranularity > **subdivisionGranularity**: [`SubdivisionGranularitySetting`](../classes/SubdivisionGranularitySetting.md) ### tileID > **tileID**: [`OverscaledTileID`](../classes/OverscaledTileID.md) ### tileSize > **tileSize**: `number` ### zoom > **zoom**: `number` --- # WorkerTileResult https://docs.mapatlas.xyz/overview/API/type-aliases/WorkerTileResult # WorkerTileResult > **WorkerTileResult** = [`ExpiryData`](ExpiryData.md) & `object` Defined in: src/source/worker\_source.ts:60 The worker tile's result type ## Type declaration ### buckets > **buckets**: [`Bucket`](../interfaces/Bucket.md)[] ### collisionBoxArray > **collisionBoxArray**: `CollisionBoxArray` ### featureIndex > **featureIndex**: [`FeatureIndex`](../classes/FeatureIndex.md) ### glyphAtlasImage > **glyphAtlasImage**: [`AlphaImage`](../classes/AlphaImage.md) ### glyphMap? > `optional` **glyphMap**: \{[`_`: `string`]: `object`; \} \| `null` ### glyphPositions? > `optional` **glyphPositions**: [`GlyphPositions`](GlyphPositions.md) \| `null` ### iconMap? > `optional` **iconMap**: \{[`_`: `string`]: [`StyleImage`](StyleImage.md); \} \| `null` ### imageAtlas > **imageAtlas**: [`ImageAtlas`](../classes/ImageAtlas.md) ### rawTileData? > `optional` **rawTileData**: `ArrayBuffer` ### resourceTiming? > `optional` **resourceTiming**: `PerformanceResourceTiming`[] --- # Type Aliases https://docs.mapatlas.xyz/overview/API/type-aliases/ # Type Aliases This section contains all the type aliases available in the MapMetrics GL API. ## Map Configuration - [MapOptions](./MapOptions.md) - Options for map configuration - [MapEventType](./MapEventType.md) - Types of map events - [MapDataEvent](./MapDataEvent.md) - Data events from the map - [MapContextEvent](./MapContextEvent.md) - Context events from the map - [MapLibreEvent](./MapLibreEvent.md) - Base MapLibre event - [MapLibreZoomEvent](./MapLibreZoomEvent.md) - Zoom events from the map - [MapProjectionEvent](./MapProjectionEvent.md) - Projection events from the map - [MapSourceDataEvent](./MapSourceDataEvent.md) - Source data events from the map - [MapSourceDataType](./MapSourceDataType.md) - Types of source data events - [MapStyleDataEvent](./MapStyleDataEvent.md) - Style data events from the map - [MapStyleImageMissingEvent](./MapStyleImageMissingEvent.md) - Style image missing events - [MapTerrainEvent](./MapTerrainEvent.md) - Terrain events from the map ## Layer and Source Types - [MapLayerEventType](./MapLayerEventType.md) - Types of layer events - [MapLayerMouseEvent](./MapLayerMouseEvent.md) - Mouse events on layers - [MapLayerTouchEvent](./MapLayerTouchEvent.md) - Touch events on layers - [MapGeoJSONFeature](./MapGeoJSONFeature.md) - GeoJSON features on the map - [SourceClass](./SourceClass.md) - Base class for sources - [GeoJSONSourceOptions](./GeoJSONSourceOptions.md) - Options for GeoJSON sources - [GeoJSONSourceDiff](./GeoJSONSourceDiff.md) - Differences in GeoJSON source data - [GeoJSONFeatureDiff](./GeoJSONFeatureDiff.md) - Differences in GeoJSON features - [GeoJSONFeatureId](./GeoJSONFeatureId.md) - Identifier for GeoJSON features - [GeoJSONWorkerOptions](./GeoJSONWorkerOptions.md) - Options for GeoJSON workers - [GeoJSONWorkerSourceLoadDataResult](./GeoJSONWorkerSourceLoadDataResult.md) - Result of loading GeoJSON data ## Controls and UI - [AttributionControlOptions](./AttributionControlOptions.md) - Options for attribution control - [FullscreenControlOptions](./FullscreenControlOptions.md) - Options for fullscreen control - [GeolocateControlOptions](./GeolocateControlOptions.md) - Options for geolocate control - [LogoControlOptions](./LogoControlOptions.md) - Options for logo control - [MarkerOptions](./MarkerOptions.md) - Options for markers - [NavigationControlOptions](./NavigationControlOptions.md) - Options for navigation control - [PopupOptions](./PopupOptions.md) - Options for popups - [ScaleControlOptions](./ScaleControlOptions.md) - Options for scale control ## Camera and Navigation - [CameraOptions](./CameraOptions.md) - Camera configuration options - [CameraForBoundsOptions](./CameraForBoundsOptions.md) - Options for camera bounds - [CameraUpdateTransformFunction](./CameraUpdateTransformFunction.md) - Function for updating camera transform - [JumpToOptions](./JumpToOptions.md) - Options for jumping to a location - [EaseToOptions](./EaseToOptions.md) - Options for easing to a location - [FlyToOptions](./FlyToOptions.md) - Options for flying to a location - [FitBoundsOptions](./FitBoundsOptions.md) - Options for fitting bounds - [AroundCenterOptions](./AroundCenterOptions.md) - Options for operations around center ## Gestures and Interactions - [DragPanOptions](./DragPanOptions.md) - Options for drag pan interactions - [DragRotateHandlerOptions](./DragRotateHandlerOptions.md) - Options for drag rotate handler - [GestureOptions](./GestureOptions.md) - Options for gesture interactions - [HandlerResult](./HandlerResult.md) - Result of handler operations ## Coordinates and Geometry - [LngLatLike](./LngLatLike.md) - Longitude/latitude like coordinates - [LngLatBoundsLike](./LngLatBoundsLike.md) - Longitude/latitude bounds like coordinates - [PointLike](./PointLike.md) - Point-like coordinates - [PointProjection](./PointProjection.md) - Point projection - [Coordinates](./Coordinates.md) - Coordinate types - [CenterZoomBearing](./CenterZoomBearing.md) - Center, zoom, and bearing configuration ## Styling and Rendering - [StyleOptions](./StyleOptions.md) - Style configuration options - [StyleSetterOptions](./StyleSetterOptions.md) - Options for setting styles - [StyleSwapOptions](./StyleSwapOptions.md) - Options for swapping styles - [StyleImage](./StyleImage.md) - Style image type - [StyleImageData](./StyleImageData.md) - Style image data - [StyleImageMetadata](./StyleImageMetadata.md) - Style image metadata - [StyleGlyph](./StyleGlyph.md) - Style glyph type - [SpriteOnDemandStyleImage](./SpriteOnDemandStyleImage.md) - Sprite on-demand style image - [UpdateImageOptions](./UpdateImageOptions.md) - Options for updating images - [UpdateLayersParameters](./UpdateLayersParameters.md) - Parameters for updating layers - [TransformStyleFunction](./TransformStyleFunction.md) - Function for transforming styles ## Workers and Tiles - [WorkerTileParameters](./WorkerTileParameters.md) - Parameters for worker tiles - [WorkerTileResult](./WorkerTileResult.md) - Result of worker tile operations - [WorkerDEMTileParameters](./WorkerDEMTileParameters.md) - Parameters for DEM worker tiles - [TileMesh](./TileMesh.md) - Tile mesh type - [TileParameters](./TileParameters.md) - Parameters for tiles - [TileState](./TileState.md) - State of tiles - [CreateTileMeshOptions](./CreateTileMeshOptions.md) - Options for creating tile meshes ## Data and Resources - [RequestParameters](./RequestParameters.md) - Parameters for requests - [RequestTransformFunction](./RequestTransformFunction.md) - Function for transforming requests - [RequestResponseMessageMap](./RequestResponseMessageMap.md) - Map of request/response messages - [GetGlyphsParameters](./GetGlyphsParameters.md) - Parameters for getting glyphs - [GetGlyphsResponse](./GetGlyphsResponse.md) - Response for getting glyphs - [GetImagesParameters](./GetImagesParameters.md) - Parameters for getting images - [GetImagesResponse](./GetImagesResponse.md) - Response for getting images - [GetResourceResponse](./GetResourceResponse.md) - Response for getting resources - [GetClusterLeavesParams](./GetClusterLeavesParams.md) - Parameters for getting cluster leaves - [LoadGeoJSONParameters](./LoadGeoJSONParameters.md) - Parameters for loading GeoJSON ## Utilities and Helpers - [Listener](./Listener.md) - Event listener type - [MessageData](./MessageData.md) - Message data type - [ExpiryData](./ExpiryData.md) - Expiry data type - [FeatureIdentifier](./FeatureIdentifier.md) - Feature identifier type - [PluginState](./PluginState.md) - Plugin state type - [RTLPluginStatus](./RTLPluginStatus.md) - RTL plugin status type - [Unit](./Unit.md) - Unit type - [Offset](./Offset.md) - Offset type - [OverlapMode](./OverlapMode.md) - Overlap mode type - [PositionAnchor](./PositionAnchor.md) - Position anchor type - [Rect](./Rect.md) - Rectangle type - [RemoveSourceParams](./RemoveSourceParams.md) - Parameters for removing sources - [SetClusterOptions](./SetClusterOptions.md) - Options for setting clusters - [Serialized](./Serialized.md) - Serialized type - [SerializedObject](./SerializedObject.md) - Serialized object type - [SerializedStructArray](./SerializedStructArray.md) - Serialized struct array type - [SymbolQuad](./SymbolQuad.md) - Symbol quad type - [PaddingOptions](./PaddingOptions.md) - Padding options type - [PossiblyEvaluatedValue](./PossiblyEvaluatedValue.md) - Possibly evaluated value type - [GridKey](./GridKey.md) - Grid key type - [IndicesType](./IndicesType.md) - Indices type - [GlyphPosition](./GlyphPosition.md) - Glyph position type - [GlyphPositions](./GlyphPositions.md) - Glyph positions type - [GlyphMetrics](./GlyphMetrics.md) - Glyph metrics type - [CustomRenderMethod](./CustomRenderMethod.md) - Custom render method type - [DistributiveOmit](./DistributiveOmit.md) - Distributive omit type - [RequireAtLeastOne](./RequireAtLeastOne.md) - Require at least one type - [ProjectionData](./ProjectionData.md) - Projection data type - [ProjectionDataParams](./ProjectionDataParams.md) - Projection data parameters type - [QueryRenderedFeaturesOptions](./QueryRenderedFeaturesOptions.md) - Options for querying rendered features - [QuerySourceFeatureOptions](./QuerySourceFeatureOptions.md) - Options for querying source features --- # API Keys & Security https://docs.mapatlas.xyz/overview/api-keys --- title: "API Keys & Security" description: "How MapMetrics Atlas API keys are authenticated, scoped, and restricted" --- # API Keys & Security Every request against the gateway is authenticated by an API key. This page covers how the key is passed, what it can and can't do, and how to read the errors it produces when something is wrong. ## How Keys Are Passed The key is sent as a **`token` query parameter** — not a header, not a bearer token. ```bash curl "https://gateway.mapmetrics-atlas.net/v2/autocomplete/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&token=YOUR_API_KEY" ``` ```javascript fetch(`https://gateway.mapmetrics-atlas.net/v2/forward-geocode/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&format=pelias&token=YOUR_API_KEY`); ``` ::: warning No `Authorization: Bearer` header There is no header-based auth on this gateway. `Authorization: Bearer YOUR_API_KEY` is silently ignored — the key must be the `token` query parameter on the request URL. ::: ## Scopes A key carries one or more **scopes**, and each endpoint requires a specific scope to authorize it: | Scope | Endpoints | |---------------|--------------------------------------------------------------------------| | `autocomplete`| `/v2/autocomplete/`, `/v2/retrieve/`, `/v2/retrieve-batch/` | | `geocode` | `/v2/forward-geocode/`, `/v2/reverse-geocode/` | | `osm-geocode` | `/osm-geocode/`, `/osm-reverse/` (free OpenStreetMap tier) | Scopes are **additive**: one key can carry `autocomplete`, `geocode` and `osm-geocode` at the same time, and a key holding all three can call both the paid v2 endpoints and the free OpenStreetMap tier. There is no separate "OSM key" — it is a scope, not a kind of key. Because scopes are checked per endpoint, a key that works fine against `/v2/autocomplete/` can still `401` against `/osm-geocode/` if it was not issued the `osm-geocode` scope. That is expected scoping behavior, not a broken key — see [`scope_not_allowed`](#understanding-auth-errors) below. Note that holding the scope does not route requests for you: the two tiers are different endpoints, so switching to the free tier is an explicit choice at call time (in the SDKs, a `tier` option on the client). ## Origin Restrictions — and Their Hard Limit A key can be restricted to a list of allowed website origins. When a key has an origin restriction set, the request's `Origin` header must match one of the listed domains. ::: danger This is a browser-only control Origin restriction only works because **browsers** set the `Origin` header themselves and don't let page JavaScript forge it. Native apps, backend servers, curl, and Postman send **no `Origin` header at all** — there's nothing there to check. An origin-restricted key used from any non-browser client returns **`403 origin_required`**. This is by design, not a misconfiguration on your end — origin restriction fundamentally cannot apply to a client that sends no origin. Do not use origin-restricted keys outside the browser. ::: The two origin-related errors look similar but have opposite fixes: - **`403 origin_required`** — no `Origin` header was present on the request at all. This means the client is not a browser (native app, server, curl, Postman). **Adding the domain to the key's allow-list will not help** — the fix is to use a key without an origin restriction for that client. - **`403 origin_not_allowed`** — an `Origin` header was present (it's a browser request), but its domain isn't in the key's allow-list. **This one is fixable**: add the domain to the key's allowed origins. Telling someone with `origin_required` to "just add your origin" sends them in circles — their client has no origin to add. Check which of the two errors you're looking at before suggesting a fix. ## Understanding Auth Errors | Error | Meaning | |-------------------------------|--------------------------------------------------------------------------| | `401 Token required` | No `token` parameter was present on the request. | | `401 token_not_found` | The key is not provisioned. A correctly-formed token still fails if it was never issued — regenerating the token string does not fix this; the key has to actually exist server-side. | | `401 token_inactive` | The key exists but has been deactivated. | | `401 scope_not_allowed` | The key is valid but lacks the scope this endpoint requires. | | `403 origin_required` | No `Origin` header on the request — see [above](#origin-restrictions-and-their-hard-limit). | | `403 origin_not_allowed` | An `Origin` header was present but not on the key's allow-list — see [above](#origin-restrictions-and-their-hard-limit). | ## Key Exposure — Be Honest About It An API key embedded in browser JavaScript is visible to anyone who opens DevTools. A key embedded in a mobile app is recoverable from the compiled binary by anyone willing to decompile it. Neither environment can actually keep a key secret. Origin restriction is a **best-effort mitigation for browser keys**, not a secret-keeping mechanism — it stops casual reuse of a key copied off your site, but it is not cryptographic protection, and it does nothing at all for a key shipped inside an app. **Recommendation:** - Scope browser-exposed keys to only what the page actually calls (e.g. `autocomplete` only, if that's all the page uses). - Keep higher-value scopes — `geocode`, `osm-geocode` — on server-side keys that never reach the client. ## Usage Limits The OSM free tier is **10,000 requests per key per day**, plus a global monthly cap across all free-tier keys. ## See Also - [Autocomplete (v2)](./geocoder/v2/autocomplete.md) - [Sessions (v2 billing model)](./geocoder/v2/sessions.md) - [Common Errors & Troubleshooting](./common-errors.md) --- # Common Errors & Troubleshooting https://docs.mapatlas.xyz/overview/common-errors --- title: "Common Errors & Troubleshooting" description: "Solutions to common MapMetrics Atlas API errors and issues" --- # Common Errors & Troubleshooting Quick solutions to the most common issues when using MapMetrics Atlas API. --- ## 🔴 Map Not Loading (Blank Screen) ### Symptoms: - Map container shows blank/white screen - Console error: `Failed to load map style` or `401 Unauthorized` - Browser developer tools show 401 errors ### Cause: **Missing or invalid API token** (99% of cases) ### Solution: **Step 1:** Check if you have a token ```javascript // ❌ WRONG - Placeholder not replaced style: 'YOUR_STYLE_URL_WITH_TOKEN' // ✅ CORRECT - Actual URL from portal style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=abc-123/style.json&token=eyJhbG...' ``` **Step 2:** Get your API token 1. Visit [MapMetrics Portal](https://portal.mapmetrics.org/) 2. Sign up (free) 3. Copy your complete style URL 4. Paste it in your code **Step 3:** Verify the token is in the URL Your style URL should include `&token=` parameter at the end. --- ## 🔴 Error: 401 Unauthorized ### For Map Loading: **Error Message:** ``` GET https://gateway.mapmetrics-atlas.net/styles/... 401 (Unauthorized) ``` **Cause:** Invalid or missing token in style URL **Solution:** ```javascript // Make sure your style URL includes the token const map = new mapmetricsgl.Map({ container: 'map', style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_ID/style.json&token=YOUR_TOKEN', center: [lng, lat], zoom: 12 }); ``` ### For REST API Calls: **Error Message:** ```json { "error": "401 Token required" } ``` **Cause:** Missing `token` query parameter **Solution:** ```javascript // ❌ WRONG - No token fetch('https://gateway.mapmetrics-atlas.net/directions/', { method: 'POST', body: JSON.stringify({...}) }); // ✅ CORRECT - Token in query parameter fetch('https://gateway.mapmetrics-atlas.net/directions/?token=YOUR_TOKEN', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({...}) }); ``` --- ## 🔴 Map Container Has No Height ### Symptoms: - Map container exists but has 0px height - Can't see the map ### Cause: Missing CSS height on map container ### Solution: **Add CSS:** ```html ``` **Or inline:** ```html
``` --- ## 🔴 React: Map Renders Multiple Times ### Symptoms: - Multiple map instances created - Memory leaks - Map behaves strangely ### Cause: Not properly managing map lifecycle in React ### Solution: ```jsx import { useEffect, useRef } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; function MapComponent() { const mapContainerRef = useRef(null); const mapRef = useRef(null); // ✅ Store map instance useEffect(() => { // ✅ Check if map already exists if (mapRef.current) return; const map = new mapmetricsgl.Map({ container: mapContainerRef.current, style: 'YOUR_STYLE_URL', center: [lng, lat], zoom: 12 }); mapRef.current = map; // ✅ Cleanup on unmount return () => { map.remove(); mapRef.current = null; }; }, []); // ✅ Empty dependency array return
; } ``` --- ## 🔴 Map Shows Error Screen Despite Loading Correctly ### Symptoms: - Error UI appears briefly or permanently even though the map renders fine - Error overlay triggered by tile 404s or slow network requests ### Cause: Using `map.on('error')` to detect fatal failures. This event also fires for non-fatal issues such as missing tiles, failed sprites, or slow network requests. ### Solution: Use a load timeout instead of relying on the `error` event: ```javascript const timeout = setTimeout(() => { // map genuinely failed to load }, 15000); map.on('load', () => clearTimeout(timeout)); ``` See the [React example page](/sdk/examples/react-map-example) for the full recommended pattern. --- ## 🔴 Map Fails in React StrictMode (Development Only) ### Symptoms: - Map works in production but breaks in development - Console shows `map.remove is not a function` or double-initialisation errors ### Cause: React 18 StrictMode double-invokes effects in development (mount → cleanup → mount). If the cleanup function does not fully reset the map ref, the second mount finds a stale instance and fails. ### Solution: Ensure your `useEffect` cleanup calls `map.remove()` **and** sets the ref to `null`: ```jsx return () => { if (map.current) { map.current.remove(); map.current = null; // ← required for StrictMode } }; ``` --- ## 🔴 Map Ref Becomes Null After Error State Is Set ### Symptoms: - Map disappears when an error occurs - Console: `Cannot read properties of null` after error state change ### Cause: The error UI replaces the map container `
`, removing it from the DOM. The map ref then points to nothing and the map cannot recover. ### Solution: Keep the container div always mounted and use an overlay for error messages: ```jsx // ❌ Wrong — replaces the container div if (error) return
Something went wrong
; // ✅ Correct — overlay on top of the container return ( <>
{error &&
Something went wrong
} ); ``` --- ## 🔴 Coordinates Not Displaying Correctly ### Symptoms: - Marker appears in wrong location - Map centered on wrong place ### Cause: Mixing up longitude and latitude order ### Solution: **MapMetrics uses [longitude, latitude] order:** ```javascript // ❌ WRONG - [latitude, longitude] center: [40.7128, -74.0060] // This is backwards! // ✅ CORRECT - [longitude, latitude] center: [-74.0060, 40.7128] // Longitude first! // ✅ For API requests, use object notation: { "lat": 40.7128, "lon": -74.0060 } ``` **Remember:** - **Map/SDK**: `[longitude, latitude]` (array) - **REST API**: `{lat: number, lon: number}` (object) --- ## 🔴 Geocoding Returns Empty Results ### Symptoms: - API returns `[]` or no features - Can't find addresses ### Common Causes: **1. Missing Token** ```javascript // ❌ WRONG fetch('https://gateway.mapmetrics-atlas.net/forward-geocode/?text=Paris') // ✅ CORRECT fetch('https://gateway.mapmetrics-atlas.net/forward-geocode/?token=YOUR_TOKEN&text=Paris') ``` **2. Text Not URL Encoded** ```javascript // ❌ WRONG - Spaces not encoded const url = `...?text=New York City` // ✅ CORRECT - Use encodeURIComponent const text = encodeURIComponent('New York City'); const url = `...?text=${text}` ``` **3. Too Specific Query** ```javascript // ❌ Might not find "1234 Some Random Street That Doesn't Exist" // ✅ Better - Start broad "Paris, France" "New York" ``` --- ## 🔴 Geocoding Returns 200 But No Results This is specifically about the **v2 geocoder** (`/v2/autocomplete/`, `/v2/retrieve/`, `/v2/retrieve-batch/`, `/v2/forward-geocode/`, `/v2/reverse-geocode/`). Almost every v2 misuse below answers with **HTTP 200** and an empty or unexpected result set rather than an error — if a v2 request "isn't finding anything," check this list before assuming the data is missing. ### 1. Using `text` instead of `q` v2 endpoints take **`q`**, not `text` (the v1 parameter name). ```bash # ❌ WRONG - v1 parameter name on a v2 endpoint curl "https://gateway.mapmetrics-atlas.net/v2/autocomplete/?text=Nieuwezijds%20Voorburgwal%20147&token=YOUR_API_KEY" # → HTTP 200, {"results": [], "q": ""} # ✅ CORRECT curl "https://gateway.mapmetrics-atlas.net/v2/autocomplete/?q=Nieuwezijds%20Voorburgwal%20147&token=YOUR_API_KEY" ``` ### 2. Missing `format=pelias` on forward/reverse geocode Without `format=pelias`, `/v2/forward-geocode/` and `/v2/reverse-geocode/` don't error — they silently return a **different, flat response shape** instead of the documented GeoJSON `FeatureCollection`. Code written against the `FeatureCollection` shape then reads `features[0]` as `undefined`. ```bash # ❌ WRONG - format omitted, returns the flat shape curl "https://gateway.mapmetrics-atlas.net/v2/forward-geocode/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&token=YOUR_API_KEY" # ✅ CORRECT curl "https://gateway.mapmetrics-atlas.net/v2/forward-geocode/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&format=pelias&token=YOUR_API_KEY" ``` ### 3. `retrieve-batch` called with repeated params instead of one `items` array `/v2/retrieve-batch/` takes exactly **one** parameter, `items`, holding a URL-encoded JSON array. Repeated params (`ord=`, `ords=`, `ids=`) return HTTP 200 with `count: 0` — no error. ```bash # ❌ WRONG - repeated params, silently returns count: 0 curl "https://gateway.mapmetrics-atlas.net/v2/retrieve-batch/?ord=128371&ord=128372&token=YOUR_API_KEY" # ✅ CORRECT - one items param, URL-encoded JSON array curl "https://gateway.mapmetrics-atlas.net/v2/retrieve-batch/?items=%5B%7B%22country%22%3A%22nl%22%2C%22layer%22%3A%22address%22%2C%22ord%22%3A128371%7D%5D&token=YOUR_API_KEY" ``` ### 4. Retrieving by `id` instead of `ord` `/v2/retrieve/` is keyed on **`ord`**, the handle returned by autocomplete — not `id`. Passing `id` 404s on every layer. ```bash # ❌ WRONG - id instead of ord curl "https://gateway.mapmetrics-atlas.net/v2/retrieve/?country=nl&layer=address&id=osm:ext:7f3a9c1d2e4b5a6f&token=YOUR_API_KEY" # → 404 # ✅ CORRECT - ord from the autocomplete suggestion curl "https://gateway.mapmetrics-atlas.net/v2/retrieve/?country=nl&layer=address&ord=128371&token=YOUR_API_KEY" ``` ### 5. A `country` filter that excludes the result A `country` param that doesn't match the actual country of the result filters it out entirely — this returns 200 with an empty or short result set, with no indication the filter was the cause. ```bash # ❌ WRONG - country=de excludes a Dutch address, empty results curl "https://gateway.mapmetrics-atlas.net/v2/forward-geocode/?q=Nieuwezijds%20Voorburgwal%20147&country=de&format=pelias&token=YOUR_API_KEY" # ✅ CORRECT - country matches where the address actually is curl "https://gateway.mapmetrics-atlas.net/v2/forward-geocode/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&format=pelias&token=YOUR_API_KEY" ``` See also [API Keys & Security](./api-keys.md) for auth-related 401/403s, which look different from these silent-empty-result cases. --- ## 🔴 CORS Errors ### Symptoms: ``` Access to fetch at '...' from origin 'http://localhost' has been blocked by CORS policy ``` ### Cause: Browser security - usually only in development ### Solution: **For Development:** Use a local server, not `file://` ```bash # Use any local server python -m http.server 8000 # or npx serve . # or npm run dev ``` **For Production:** Deploy to a proper domain (Vercel, Netlify, etc.) --- ## 🔴 NPM Package Import Errors ### Symptoms: ``` Module not found: Can't resolve '@mapmetrics/mapmetrics-gl' ``` ### Solution: **1. Install the CORRECT package:** ```bash # ✅ CORRECT - MapMetrics package npm install @mapmetrics/mapmetrics-gl # ❌ WRONG - This is MapLibre, NOT MapMetrics! npm install maplibre-gl ``` **2. Import correctly:** ```javascript // ✅ CORRECT import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; // ❌ WRONG import maplibregl from 'maplibre-gl'; // Wrong package! ``` **3. If using TypeScript, you may need:** ```typescript declare module '@mapmetrics/mapmetrics-gl'; ``` --- ## 🔴 Using Wrong Package (maplibre-gl vs @mapmetrics/mapmetrics-gl) ### Symptoms: - Map doesn't load even with valid token - Authentication errors with MapMetrics style URLs - Missing MapMetrics-specific features ### Cause: Using `maplibre-gl` (the base library) instead of `@mapmetrics/mapmetrics-gl` (MapMetrics custom fork) ### Solution: **Uninstall wrong package and install correct one:** ```bash # Remove wrong package npm uninstall maplibre-gl # Install correct package npm install @mapmetrics/mapmetrics-gl ``` **Update imports:** ```javascript // ❌ Before (WRONG) import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; // ✅ After (CORRECT) import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; ``` **Update all references:** ```javascript // ❌ WRONG const map = new maplibregl.Map({...}); const marker = new maplibregl.Marker(); // ✅ CORRECT const map = new mapmetricsgl.Map({...}); const marker = new mapmetricsgl.Marker(); ``` **Why this matters:** - MapMetrics GL is a **custom fork** of MapLibre with proprietary features - MapMetrics style URLs **only work** with the MapMetrics package - The packages are **NOT interchangeable** --- ## 🔴 Directions API: No Route Found ### Symptoms: ```json { "error": "No route found" } ``` ### Common Causes: **1. Unreachable Locations** ```javascript // Locations on different continents with no ferry routes locations: [ { lat: 40.7128, lon: -74.0060 }, // New York { lat: 51.5074, lon: -0.1278 } // London - needs ferry! ] ``` **2. Invalid Costing Model** ```javascript // ❌ Wrong costing for the route costing: "bicycle" // But route includes highways // ✅ Use appropriate costing costing: "auto" ``` **3. Coordinates Reversed** ```javascript // ❌ WRONG - lat/lon format in REST API locations: [ { lon: -74.0060, lat: 40.7128 } // Backwards! ] // ✅ CORRECT - lat comes first in objects locations: [ { lat: 40.7128, lon: -74.0060 } ] ``` --- ## 🆘 Still Having Issues? ### Checklist: - [ ] **Do you have a valid token?** Get one at [portal.mapmetrics.org](https://portal.mapmetrics.org/) - [ ] **Is the token in the URL?** Check for `?token=` or `&token=` - [ ] **Is your map container styled?** Add `height: 500px` to CSS - [ ] **Are coordinates in correct order?** `[longitude, latitude]` for maps - [ ] **Check browser console** for error messages - [ ] **Is JavaScript loading?** Check Network tab in DevTools - [ ] **Using HTTPS in production?** Some features require secure context ### Get Help: - 📖 [Documentation](/) - 💬 [Discord Community](https://discord.com/invite/uRXQRfbb7d) - 📧 Support: Contact via portal --- ## 🎓 Prevention Tips ### Before You Start: 1. ✅ Sign up at [portal.mapmetrics.org](https://portal.mapmetrics.org/) 2. ✅ Get your style URL (includes token) 3. ✅ Test with simple example first 4. ✅ Check browser console for errors 5. ✅ Read relevant example docs ### While Developing: 1. ✅ Always check token is present 2. ✅ Use browser DevTools to debug 3. ✅ Test API calls with curl first 4. ✅ Verify coordinates are correct 5. ✅ Keep documentation handy --- **Most issues are solved by:** 1. Getting a valid API token 2. Adding it to your requests 3. Setting proper container height **99% of "map not loading" issues = missing/invalid token!** 🔑 --- # Directions Api https://docs.mapatlas.xyz/overview/directions/directions # Directions Api Routing Direction Service (a.k.a. turn-by-turn), is an open-source routing service that lets you integrate routing and navigation into a web or mobile application. The **`/directions`** endpoint calculates a route between two points (origin → destination). use your token in Query Param: ```http POST /directions/?token=YOUR_TOKEN ``` ## Example Request ```json { "locations": [ { "lat": "42.358528", "lon": "-83.271400" }, { "lat": "42.996613", "lon": "-78.749855" } ], "costing": "auto" ,"id":"my_work_route"} ``` There is an option to name your route request. You can do this by appending the following to your request &id=. The id is returned with the response so a user could match to the corresponding request. | Parameter | Location |Required| Description | | --------------| ----------- |--------| ------------------------------------------------------------------------------------------ | | **token** | Query Param |✅ Yes | API authentication token. Required, otherwise request will fail with `401 Token required`. | | **locations** | bodyjson |✅ Yes | Latitude,Longitude of the starting point to the destination point, "locations":[ { "lat": "42.358528", "lon": "-83.271400" }, { "lat": "42.996613", "lon": "-78.749855" } ], | | **costing** | bodyjson |✅ Yes | "auto","bicycle","bus","truck","taxi","pedestrian" | # Costing models directions routing service uses dynamic, run-time costing to generate the route path. The route request must include the name of the costing model and can include optional parameters available for the chosen costing model. | Costing model | Description | |---------------|-------------| | **auto** | Standard costing for driving routes by car, motorcycle, truck, and so on that obeys automobile driving rules, such as access and turn restrictions. `auto` provides a short time path (though not guaranteed to be shortest time) and uses intersection costing to minimize turns and maneuvers or road name changes. Routes also tend to favor highways and higher classification roads, such as motorways and trunks. | | **bicycle** | Standard costing for travel by bicycle, with a slight preference for using cycleways or roads with bicycle lanes. Bicycle routes follow regular roads when needed, but avoid roads without bicycle access. | | **bus** | Standard costing for bus routes. Bus costing inherits the auto costing behaviors, but checks for bus access on the roads. | | **truck** | Standard costing for trucks. Truck costing inherits the auto costing behaviors, but checks for truck access, width and height restrictions, and weight limits on the roads. | | **taxi** | Standard costing for taxi routes. Taxi costing inherits the auto costing behaviors, but checks for taxi lane access on the roads and favors those roads. | | **motor_scooter** | Standard costing for travel by motor scooter or moped. By default, `motor_scooter` costing will avoid higher class roads unless the country overrides allows motor scooters on these roads. Motor scooter routes follow regular roads when needed, but avoid roads without motor_scooter, moped, or mofa access. | | **pedestrian** | Standard walking route that excludes roads without pedestrian access. In general, pedestrian routes are shortest distance with the following exceptions: walkways and footpaths are slightly favored, while steps or stairs and alleys are slightly avoided. | ## Trip legs and maneuvers A `trip` contains one or more `legs`. For *n* number of `break` locations, there are *n-1* legs. `Through` locations do not create separate legs. Each leg of the trip includes a summary, which is comprised of the same information as a trip summary but applied to the single leg of the trip. It also includes a `shape`, which is an encoded polyline of the route path (with 6 digits decimal precision), and a list of `maneuvers` as a JSON array. For more about decoding route shapes, see these [code examples](#). If `elevation_interval` is specified, each leg of the trip will return `elevation` along the route as a JSON array. The `elevation_interval` is also returned. Units for both `elevation` and `elevation_interval` are either meters or feet based on the input units specified. --- ## Each maneuver includes | Maneuver item | Description | |----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **type** | Type of maneuver. See below for a list. | | **instruction** | Written maneuver instruction. Describes the maneuver, such as `"Turn right onto Main Street"`. | | **verbal_transition_alert_instruction** | Text suitable for use as a verbal alert in a navigation application. The transition alert instruction will prepare the user for the forthcoming transition.
Example: `"Turn right onto North Prince Street"`. | | **verbal_pre_transition_instruction** | Text suitable for use as a verbal message immediately prior to the maneuver transition.
Example: `"Turn right onto North Prince Street, U.S. 2 22"`. | | **verbal_post_transition_instruction** | Text suitable for use as a verbal message immediately after the maneuver transition.
Example: `"Continue on U.S. 2 22 for 3.9 miles"`. | | **street_names** | List of street names that are consistent along the entire nonobvious maneuver. | | **begin_street_names** | When present, these are the street names at the beginning (transition point) of the nonobvious maneuver (if they are different than the names that are consistent along the entire nonobvious maneuver). | | **time** | Estimated time along the maneuver in seconds. | | **length** | Maneuver length in the units specified. | | **begin_shape_index** | Index into the list of shape points for the start of the maneuver. | | **end_shape_index** | Index into the list of shape points for the end of the maneuver. | | **toll** | `true` if the maneuver has any toll, or portions of the maneuver are subject to a toll. | | **highway** | `true` if a highway is encountered on this maneuver. | | **rough** | `true` if the maneuver is unpaved or rough pavement, or has any portions that have rough pavement. | | **gate** | `true` if a gate is encountered on this maneuver. | | **ferry** | `true` if a ferry is encountered on this maneuver. | | **sign** | Contains the interchange guide information at a road junction associated with this maneuver. See below for details. | | **roundabout_exit_count** | The spoke to exit roundabout after entering. | | **depart_instruction** | Written depart time instruction. Typically used with a transit maneuver, such as `"Depart: 8:04 AM from 8 St - NYU"`. | | **verbal_depart_instruction** | Text suitable for use as a verbal depart time instruction. Typically used with a transit maneuver, such as `"Depart at 8:04 AM from 8 St - NYU"`. | | **arrive_instruction** | Written arrive time instruction. Typically used with a transit maneuver, such as `"Arrive: 8:10 AM at 34 St - Herald Sq"`. | | **verbal_arrive_instruction** | Text suitable for use as a verbal arrive time instruction. Typically used with a transit maneuver, such as `"Arrive at 8:10 AM at 34 St - Herald Sq"`. | | **transit_info** | Contains the attributes that describe a specific transit route. See below for details. | | **verbal_multi_cue** | `true` if the `verbal_pre_transition_instruction` has been appended with the verbal instruction of the next maneuver. | | **travel_mode** | Travel mode:
• `"drive"`
• `"pedestrian"`
• `"bicycle"`
• `"transit"` | | **travel_type** | Travel type for drive:
• `"car"`
• `"motorcycle"`
• `"motor_scooter"`
• `"truck"`
• `"bus"`

Travel type for pedestrian:
• `"foot"`
• `"wheelchair"`

Travel type for bicycle:
• `"road"`
• `"hybrid"`
• `"cross"`
• `"mountain"`

Travel type for transit:
• Tram or light rail = `"tram"`
• Metro or subway = `"metro"`
• Rail = `"rail"`
• Bus = `"bus"`
• Ferry = `"ferry"`
• Cable car = `"cable_car"`
• Gondola = `"gondola"`
• Funicular = `"funicular"` | | **bss_maneuver_type** | Used when `travel_mode` is `bikeshare`. Describes bike share maneuver. The default value is `"NoneAction"`.

Options:
• `"NoneAction"`
• `"RentBikeAtBikeShare"`
• `"ReturnBikeAtBikeShare"` | | **bearing_before** | The clockwise angle from true north to the direction of travel immediately before the maneuver. | | **bearing_after** | The clockwise angle from true north to the direction of travel immediately after the maneuver. | | **lanes** | An array describing lane-level guidance. Used when `turn_lanes` is enabled. See below for details. | ## Directions Options Directions options should be specified at the top level of the JSON object. | Options | Description | |--------------------|-------------| | **units** | Distance units for output. Allowable unit types are miles (or mi) and kilometers (or km). If no unit type is specified, the units default to kilometers. | | **language** | The language of the narration instructions based on the [IETF BCP 47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tag string. If no language is specified or the specified language is unsupported, United States-based English (en-US) is used. [Currently supported language list](https://valhalla.readthedocs.io/en/latest/api/languages/). | | **directions_type** | An enum with 3 values:
- `none` indicating no maneuvers or instructions should be returned.
- `maneuvers` indicating that only maneuvers be returned.
- `instructions` indicating that maneuvers with instructions should be returned (this is the default if not specified). | | **format** | Four options are available:
- `json` is default Valhalla routing directions JSON format.
- `gpx` returns the route as a GPX (GPS exchange format) XML track.
- `osrm` creates an OSRM compatible route directions JSON.
- `pbf` formats the result using protocol buffers. | | **shape_format** | If `"format": "osrm"` is set: Specifies the optional format for the path shape of each connection. One of `polyline6` (default), `polyline5`, `geojson` or `no_shape`. | | **banner_instructions** | If the format is `osrm`, this boolean indicates if each step should have the additional `bannerInstructions` attribute, which can be displayed in some navigation system SDKs. | | **voice_instructions** | If the format is `osrm`, this boolean indicates if each step should have the additional `voiceInstructions` attribute, which can be heard in some navigation system SDKs. | | **alternates** | A number denoting how many alternate routes should be provided. There may be no alternates or less alternates than the user specifies. Alternates are not yet supported on multipoint routes (that is, routes with more than 2 locations). They are also not supported on time dependent routes. | --- ## Example with language ```json {"locations":[{"lat":40.730930,"lon":-73.991379},{"lat":40.749706,"lon":-73.991562}],"format":"osrm","costing":"bus","banner_instructions":true,"voice_instructions":true,"language":"es-ES"} ``` --- ## Costing Options You can customize route preferences using `costing_options` for different vehicle types. These options allow you to control route behavior such as avoiding tolls or highways. ### Using Tolls and Highways For the `auto` costing model, you can specify whether to use or avoid tolls and highways: - `use_tolls`: `1` (use tolls) or `0` (avoid tolls) - `use_highways`: `1` (use highways) or `0` (avoid highways) ### Example with Tolls and Highways ```json { "locations": [ {"lat": "24.938833", "lon": "67.089141"}, {"lat": "25.385833", "lon": "68.367643"} ], "costing": "auto", "units": "km", "alternates": 3, "id": "route_request", "directions_options": { "alternates": 3 }, "costing_options": { "auto": { "use_tolls": 1, "use_highways": 1 } } } ``` In this example: - `"use_tolls": 1` means the route **will include** toll roads - `"use_highways": 1` means the route **will include** highways - Set to `0` to **avoid** tolls or highways respectively # Supported Language Tags | Language tag | Language alias | Description | |---------------|----------------|------------------------| | **bg-BG** | bg | Bulgarian (Bulgaria) | | **ca-ES** | ca | Catalan (Spain) | | **cs-CZ** | cs | Czech (Czech Republic) | | **da-DK** | da | Danish (Denmark) | | **de-DE** | de | German (Germany) | | **el-GR** | el | Greek (Greece) | | **en-GB** | en | English (United Kingdom) | | **en-US-x-pirate** | en-x-pirate | English (United States) Pirate | | **en-US** | en | English (United States) | | **es-ES** | es | Spanish (Spain) | | **et-EE** | et | Estonian (Estonia) | | **fi-FI** | fi | Finnish (Finland) | | **fr-FR** | fr | French (France) | | **hi-IN** | hi | Hindi (India) | | **hu-HU** | hu | Hungarian (Hungary) | | **it-IT** | it | Italian (Italy) | | **ja-JP** | ja | Japanese (Japan) | | **nb-NO** | nb | Bokmål (Norway) | | **nl-NL** | nl | Dutch (Netherlands) | | **pl-PL** | pl | Polish (Poland) | | **pt-BR** | pt | Portuguese (Brazil) | | **pt-PT** | pt | Portuguese (Portugal) | | **ro-RO** | ro | Romanian (Romania) | | **ru-RU** | ru | Russian (Russia) | | **sk-SK** | sk | Slovak (Slovakia) | | **sl-SI** | sl | Slovenian (Slovenia) | | **sv-SE** | sv | Swedish (Sweden) | | **tr-TR** | tr | Turkish (Turkey) | | **uk-UA** | uk | Ukrainian (Ukraine) | ## Outputs of a route The route results are returned as a `trip`. This is a JSON object that contains details about the trip, including locations, a summary with basic information about the entire trip, and a list of `legs`. | Trip item | Description | |--------------|-----------------------------------------------------------------------------| | `status` | Status code. | | `status_messa` | Status message. | | `units` | The specified units of length are returned, either kilometers or miles. | | `language` | The language of the narration instructions. If the user specified a language in the directions options and the specified language was supported - this returned value will be equal to the specified value. Otherwise, this value will be the default (en-US) language. | | `locations` | Location information is returned in the same form as it is entered with additional fields to indicate the side of the street. | | `warnings` (optional) | This array may contain warning objects informing about deprecated request parameters, clamped values etc. | --- ## Example Response ```json { "trip": { "locations": [ { "type": "break", "lat": 42.358528, "lon": -83.271400, "original_index": 0 }, { "type": "break", "lat": 42.996613, "lon": -78.749855, "original_index": 1 } ], "legs": [ { "maneuvers": [ { "type": 2, "instruction": "Drive northeast on Appleton Avenue.", "verbal_pre_transition_instruction": "Drive northeast on Appleton Avenue for 1.2 miles.", "street_names": ["Appleton Avenue"], "time": 142.5, "length": 1.932, "cost": 142.5, "begin_shape_index": 0, "end_shape_index": 23, "highway": false, "toll": false, "rough": false, "gate": false, "ferry": false, "travel_mode": "drive", "travel_type": "car" }, { "type": 15, "instruction": "Turn right onto Main Street.", "verbal_pre_transition_instruction": "Turn right onto Main Street.", "street_names": ["Main Street"], "time": 45.2, "length": 0.542, "cost": 45.2, "begin_shape_index": 23, "end_shape_index": 35, "highway": false, "toll": false, "rough": false, "gate": false, "ferry": false, "travel_mode": "drive", "travel_type": "car" }, { "type": 4, "instruction": "You have arrived at your destination.", "verbal_pre_transition_instruction": "You have arrived at your destination.", "time": 0, "length": 0, "cost": 0, "begin_shape_index": 35, "end_shape_index": 35, "travel_mode": "drive", "travel_type": "car" } ], "summary": { "has_time_restrictions": false, "has_toll": false, "has_highway": true, "has_ferry": false, "min_lat": 42.358421, "min_lon": -83.271523, "max_lat": 42.997234, "max_lon": -78.749234, "time": 18734.567, "length": 453.211, "cost": 18734.567 }, "shape": "gysalAlg~j}ClFhBnHdCrKrDfKjDdK~CjGtB|@b@n@TdG`Cd@VzEnBb@l@\\Vh@d@b@n@f@d@nRzQpCvCNPPRNNFHHJFDDDJLVTFJTVFDx@z@d@j@\\^r@r@`@`@NPNPJLJJLJHHHJDFJHb@b@j@h@nBnBlBnBvBvBpBpBxBxBvBvBjBjB^`@Z\\TTNR^\\~@z@hDfDbArATVrBnBlAfAbEhEXXt@v@dCdCZ\\vBvB`@`@RPLJJJ~ArAXVlEjEdDfDVVHHJHRRPPNNrFrFbBbBfLjLhBlBp@n@~D`EtCxCv@t@~@`AxIbJzD~DnLrLbDdDTVFHn@l@bBfBdDdDdAhAfC~BrBnBXV^l@d@\\l@dArBv@rBnA|Bd@x@PZHJp@rAnAtB`BjCjBxChCbEfApB|GjLxBtDl@pA^v@`BdDlBfDrBvDHNvExH\\n@z@bBrBhDxBzDNTjAtBpC~EbBvCdAnBjAjBbDbFpA`C^r@hAnBnB~Cl@dA|@xAlFbJfAnBtBhDvFlJv@tAfCxDpD~FZj@dApB|E|HfBzC^p@tDhGtAnClBpDvA~BhAlBx@~A`AjBlBnDx@|AnCfFzGjMf@bAfChFp@tAZ~@h@v@pB|D|@bBzBbEjHzMfBxCz@zAvDbHNTpCtF`BfCfCdE~C|FpBnDn@~@|BfE`D`HdAxBlAnC" } ], "summary": { "has_time_restrictions": false, "has_toll": false, "has_highway": true, "has_ferry": false, "min_lat": 42.358421, "min_lon": -83.271523, "max_lat": 42.997234, "max_lon": -78.749234, "time": 18734.567, "length": 453.211, "cost": 18734.567 }, "status_message": "Found route between points", "status": 0, "units": "kilometers", "language": "en-US" } } ``` --- # Elevation API https://docs.mapatlas.xyz/overview/directions/elevation # Elevation API Elevation lookup service provides digital elevation model (DEM) data as the result of a query. The elevation service data has many applications when combined with other routing and navigation data, including computing the steepness of roads and paths or generating an elevation profile chart along a route. ## Inputs of the elevation service An elevation service request takes the form of json={}, where the JSON inputs inside the {} includes location information. # Use Shape list for input location The elevation request run locally takes the form of json={}, where the JSON inputs inside the {} are described below. A shape request must include a latitude and longitude in decimal degrees, and the locations are visited in the order specified. The input coordinates can come from many input sources, such as a GPS location, a point or a click on a map, a geocoding service, and so on. These parameters are available for shape. # Shape Parameters | Parameter | Description | |-----------|------------------------------------| | `lat` | Latitude of the location in degrees | | `lon` | Longitude of the location in degrees | | `token` | token parameter is required /elevation/?token=YOUR TOKEN --- ## Example 1: Request with `range` and `shape` ```json {"range":true,"shape":[{"lat":40.712431,"lon":-76.504916},{"lat":40.712275,"lon":-76.605259},{"lat":40.712122,"lon":-76.805694},{"lat":40.722431,"lon":-76.884916},{"lat":40.812275,"lon":-76.905259},{"lat":40.912122,"lon":-76.965694}]} ``` ## Example 2: ```json {"shape":[{"lat":40.712433,"lon":-76.504913},{"lat":40.712276,"lon":-76.605263},{"lat":40.712124,"lon":-76.805695},{"lat":40.722431,"lon":-76.884918},{"lat":40.812275,"lon":-76.905258},{"lat":40.912121,"lon":-76.965691}],"range_height":[[0,307],[8467,272],[25380,204],[32162,204],[42309,180],[54533,198]]} ``` ## Example 3: ```json {"range":true,"shape":[{"lat":40.712431,"lon":-76.504916},{"lat":40.712275,"lon":-76.605259},{"lat":40.712122,"lon":-76.805694},{"lat":40.722431,"lon":-76.884916},{"lat":40.812275,"lon":-76.905259},{"lat":40.912122,"lon":-76.965694}]} ``` --- ## Example Response ```json { "shape": [ {"lat": 40.712431, "lon": -76.504916}, {"lat": 40.712275, "lon": -76.605259}, {"lat": 40.712122, "lon": -76.805694}, {"lat": 40.722431, "lon": -76.884916}, {"lat": 40.812275, "lon": -76.905259}, {"lat": 40.912122, "lon": -76.965694} ], "range_height": [ [0, 307], [8467, 272], [25380, 204], [32162, 204], [42309, 180], [54533, 198] ], "height": [307, 272, 204, 204, 180, 198] } ``` **Response Fields:** - `shape`: Array of lat/lon coordinates that were queried - `range_height`: Array of `[distance, elevation]` pairs where distance is in meters from the start point - `height`: Simple array of elevation values in meters corresponding to each coordinate in the shape --- # Isochrones Maps https://docs.mapatlas.xyz/overview/directions/isochrone # Isochrones Maps The ability to return these amazing structures called isochrones. What's an isochrone? The word is a combination of two greek roots iso meaning equal and chrono meaning time. So indeed, an isochrone is a structure representing equal time. In our case it's a line that represents constant travel time about a given location. One can think of isochrone maps as somewhat similar to the familiar topographic maps except that instead of lines of constant height, lines are of constant travel time are depicted. For this reason other terms common in topography apply such as contours or isolines. This is an example of 15, 30, 45 and 60 minute bicycle isochrones centered in Lancaster, PA. isochrone map image # Inputs of the Isochrone Service An isochrone request run locally takes the form of json={}, where the JSON inputs inside the {} includes an array of at least one location For example, you can use the isochrone service to find out where you can travel within a 15-minute walk from your office building. The API request for this uses isochrone? as the request action, pedestrian costing, and a single contour for a 15-minute time interval. The response is GeoJSON, which you can display on a map to visualize where you might be able to walk. ```json {"locations":[{"lat":40.744014,"lon":-73.990508}],"costing":"pedestrian","contours":[{"time":15.0,"color":"ff0000"}]} ``` # Parameters & bodyjson | Parameter | Description | |-----------|------------------------------------| | `lat` | Latitude of the location in degrees | | `lon` | Longitude of the location in degrees | | `token` | token parameter is required /elevation/?token=YOUR TOKEN --- # Output of Isochrone Service In the service response, the isochrone contours are returned as GeoJSON, which can be integrated into mapping applications. The isochrone service returns contours as GeoJSON line or polygon features for the requested intervals (depending on the value of the polygons request parameter). These contours are calculated using a two dimensional grid. If the format request parameter is set to geotiff, the underlying grid data is returned directly instead of the contours derived from it. It will return one band for each requested metric (i.e. one for time and one for distance). If an isochrone request has been named using the optional &id= input, then the id is returned as a name property for the feature collection within the GeoJSON response. A metric attribute lets you know whether it's a distance or time contour. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. # Other request bodyjson content | Parameter | Description | |--------------|-----------------------------------------------------------------------------| | `date_time` | The local date and time at the location.
- `type`
- `0` - Current departure time.
- `1` - Specified departure time.
- `2` - Specified arrival time. Note: This is not yet implemented for `multimodal`.
- `value` - the date and time specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example, "2016-07-03T08:06". | | `contours` | A JSON array of contour objects with the time in minutes or distance in kilometers and color to use for each isochrone contour. You can specify up to four contours (by default).
- `time` - A floating point value specifying the time in minutes for the contour.
- `distance` - A floating point value specifying the distance in kilometers for the contour.
- `color` - The color for the output of the contour. Specify it as a Hex value, but without the `#`, such as `"ff0000"` for red. If no color is specified, the isochrone service will assign a default color to the output.
You can only specify one metric per contour, i.e. time or distance. | | `polygons` | A Boolean value to determine whether to return geojson polygons or linestrings as the contours. The default is `false`, which returns lines; when `true`, polygons are returned. Note: When polygons is `true`, a feature's geometry type can be either `Polygon` or `MultiPolygon`, depending on the number of exterior rings formed for a given interval. | | `denoise` | A floating point value from 0 to 1 (default of 1) which can be used to remove smaller contours. A value of 1 will only return the largest contour for a given time value. A value of 0.5 drops any contours that are less than half the area of the largest contour in the set of contours for that same time value. | | `generalize` | A floating point value in meters used as the tolerance for Douglas-Peucker generalization. Note: Generalization of contours can lead to self-intersections, as well as intersections of adjacent contours. | | `show_locations` | A boolean indicating whether the input locations should be returned as MultiPoint features; one feature for the exact input coordinates and one feature for the coordinates of the network node it snapped to. Default false. | --- ## Example Response (GeoJSON Polygon) ```json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [-73.990508, 40.744014], [-73.989234, 40.745123], [-73.987654, 40.746234], [-73.986123, 40.747345], [-73.985432, 40.748456], [-73.984321, 40.749567], [-73.983210, 40.750678], [-73.990508, 40.744014] ] ] }, "properties": { "fill": "#ff0000", "fillOpacity": 0.33, "fill-opacity": 0.33, "fillColor": "#ff0000", "color": "#ff0000", "contour": 0, "opacity": 0.33, "metric": "time", "value": 15 } } ], "bbox": [-73.995234, 40.738765, -73.983210, 40.750678] } ``` ## Example Response (GeoJSON LineString - polygons: false) ```json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [-73.990508, 40.744014], [-73.989234, 40.745123], [-73.987654, 40.746234], [-73.986123, 40.747345], [-73.985432, 40.748456], [-73.984321, 40.749567], [-73.983210, 40.750678], [-73.990508, 40.744014] ] }, "properties": { "fill": "#ff0000", "fillOpacity": 0.33, "fill-opacity": 0.33, "fillColor": "#ff0000", "color": "#ff0000", "contour": 0, "opacity": 0.33, "metric": "time", "value": 15 } } ], "bbox": [-73.995234, 40.738765, -73.983210, 40.750678] } ``` --- # Map Matching https://docs.mapatlas.xyz/overview/directions/mapMatch # Map Matching Map Matching service, you can match coordinates, such as GPS locations, to roads and paths that have been mapped in OpenStreetMap. By doing this, you can turn a path into a route with narrative instructions and also get the attribute values from that matched line. # Map Matching API (`/map-matching/`) The **`/map-matching`** API is used to match noisy GPS traces onto the road network, reconstructing the most probable route. --- ## Example Request ```http GET /map-matching/?token=YOUR_API_TOKEN ``` ```json { "shape": [ { "lat": 39.983841, "lon": -76.735741, "type": "break" }, { "lat": 39.983704, "lon": -76.735298, "type": "via" }, { "lat": 39.983578, "lon": -76.734848, "type": "via" }, { "lat": 39.983551, "lon": -76.734253, "type": "break" }, { "lat": 39.983555, "lon": -76.734116, "type": "via" }, { "lat": 39.983589, "lon": -76.733315, "type": "via" }, { "lat": 39.983719, "lon": -76.732445, "type": "via" }, { "lat": 39.983818, "lon": -76.731712, "type": "via" }, { "lat": 39.983776, "lon": -76.731506, "type": "via" }, { "lat": 39.983696, "lon": -76.731369, "type": "break" } ], "costing": "auto", "shape_match": "map_snap" } ``` | Parameter | Location | Required | Description | | ---------------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | | **token** | Query Param | ✅ Yes | API authentication token. Required, otherwise request will fail with `401 Token required`. | | **shape** | Body JSON | ✅ Yes | Array of GPS coordinates with at least **2 points**. Each point must have `lat`, `lon`, and a `type` (`break` or `via`). | | **costing** | Body JSON | ✅ Yes | Travel mode. Options: `auto`, `pedestrian`, `bicycle`, `bus`,`motor_scooter`. | | **shape\_match** | Body JSON | ❌ No | Controls the map matching behavior. Common values: `map_snap` (default) Options: `walk_or_snap`, `map_snap`, `edge_walk`| ## Shape Match Options details `shape_match` is an optional string input parameter. It allows some control of the matching algorithm based on the type of input. | shape_match type | Description | |-------------------|-----------------------------------------------------------------------------| | edge_walk | Indicates an edge walking algorithm can be used. This algorithm requires nearly exact shape matching, so it should only be used when the shape is from a prior Valhalla route. | | map_snap | Indicates that a map-matching algorithm should be used because the input shape might not closely match Valhalla edges. This algorithm is more expensive. | | walk_or_snap | Also the default option. This will try edge walking and if this does not succeed, it will fall back and use map matching. | ## Attribute filters (trace_attributes only) The trace_attributes action allows you to apply filters to include or exclude specific attribute filter keys in your response. These filters are optional and can be added to the action string inside of the filters object. ``` // Edge filter Keys edge.names edge.length # can also set source/target_percent_along edge.speed edge.speeds_faded edge.speeds_non_faded edge.road_class edge.begin_heading edge.end_heading edge.begin_shape_index edge.end_shape_index edge.traversability edge.use edge.toll edge.unpaved edge.tunnel edge.bridge edge.roundabout edge.internal_intersection edge.drive_on_right edge.surface edge.sign.exit_number edge.sign.exit_branch edge.sign.exit_toward edge.sign.exit_name edge.travel_mode edge.vehicle_type edge.pedestrian_type edge.bicycle_type edge.transit_type edge.id edge.indoor edge.way_id edge.weighted_grade edge.max_upward_grade edge.max_downward_grade edge.mean_elevation edge.lane_count edge.cycle_lane edge.bicycle_network edge.sac_scale edge.shoulder edge.sidewalk edge.density edge.speed_limit edge.truck_speed edge.truck_route edge.country_crossing edge.forward edge.traffic_signal // Node filter keys node.intersecting_edge.begin_heading node.intersecting_edge.from_edge_name_consistency node.intersecting_edge.to_edge_name_consistency node.intersecting_edge.driveability node.intersecting_edge.cyclability node.intersecting_edge.walkability node.intersecting_edge.use node.intersecting_edge.road_class node.intersecting_edge.lane_count node.elapsed_time node.admin_index node.type node.traffic_signal node.fork node.time_zone // Other filter keys osm_changeset shape admin.country_code admin.country_text admin.state_code admin.state_text matched.point matched.type matched.edge_index matched.begin_route_discontinuity matched.end_route_discontinuity matched.distance_along_edge matched.distance_from_trace_point ``` ## Edge items include: | Item | Description | |-----------------|-----------------------------------------------------------------------------| | names | List of names | | source | The start of an edge's matched percentage of its length (0, range if edge was fully matched; end of an edge's matched percentage of its length (0, range if edge was fully matched); we set value to percentage of its length in units specified (default is kilometers). If percentage is unavailable, we set value to -1. | | target | The end of an edge's matched percentage of its length (0, range if edge was fully matched); we set value to percentage of its length in units specified (default is kilometers). If percentage is unavailable, we set value to -1. | | length | The retrieved edge's length in the units specified (default is kilometers). If unavailable, we represent the partially matched length, otherwise full length percent. | | speed | Edge's specified speed. The default is kilometers per hour. | | from.type | Describes how the shape was assigned to the edge. Either it’s inferred from road class (default value) or set by the user. | | speed.faded | Contains all speed values that are faded in the current time (if available). The current value fades with the flow rates when they are available. | | speed.non_faded | Contains all flow rates when speed is available that the edge is not the units specified (default flow rates are in kilometers per hour). All flows are available when speed is available. | | road_class | Road class value. | | begin.heading | The direction at the beginning of the edge. The units are degrees from north in a clockwise direction. | | end.heading | The direction at the end of the edge. The units are degrees from north in a clockwise direction. | | begin_shape_index | Index of the list of shape points for the start of the edge. | | end_shape_index | Index of the list of shape points for the end of the edge. | | traversability | Traversability value, if available. | | use | Use value. | | toll | Toll value. | | unpaved | Unpaved value. | | tunnel | Tunnel value. | | bridge | Bridge value. | | roundabout | Roundabout value. | | internal_intersection | Internal intersection value. | | drive_on_right | Drive on right value. | | surface | Surface value. | | sign.exit_number | Sign exit number value. | | sign.exit_branch | Sign exit branch value. | | sign.exit_toward | Sign exit toward value. | | sign.exit_name | Sign exit name value. | | travel_mode | Travel mode value. | | vehicle_type | Vehicle type value. | | pedestrian_type | Pedestrian type value. | | bicycle_type | Bicycle type value. | | transit_type | Transit type value. | | id | ID value. | | indoor | Indoor value. | | way_id | Way ID value. | | weighted_grade | Weighted grade value. | | max_upward_grade | Maximum upward grade value. | | max_downward_grade | Maximum downward grade value. | | mean_elevation | Mean elevation value. | | lane_count | Lane count value. | | cycle_lane | Cycle lane value. | | bicycle_network | Bicycle network value. | | sac_scale | SAC scale value. | | shoulder | Shoulder value. | | sidewalk | Sidewalk value. | | density | Density value. | | speed_limit | Speed limit value. | | truck_speed | Truck speed value. | | truck_route | Truck route value. | | country_crossing | Country crossing value. | | forward | Forward value. | | traffic_signal | Traffic signal value. | ## Outputs of trace_attributes The `trace_attributes` results contains a list of edges and, optionally, the following items: `osm_changeset`, list of `admins`, `shape`, `matched_points`, and `units`. | Result item | Description | |-----------------|--------------------------------------------------| | edges | List of edges associated with input shape. See the list of edge items for details. | | osm_changeset | Identifier of the OpenStreetMap base data version. | | admins | List of the administrative codes and names. See the list of admin items for details. | | shape | The encoded polyline of the matched path. | | matched_points | List of match results when using the `map_snap` shape match algorithm. There is a one-to-one correspondence with the input set of latitude, longitude coordinates and this list of match results. See the list of matched point items for details. | | units | The specified units with the request, in either kilometers or miles. | | warnings | A warnings array. This array may contain descriptive text about notices of deprecated request parameters, clamped values etc. | ## trace_attributes with shape parameter ```json { "shape": [ { "lat": 39.983841, "lon": -76.735741, "type": "break" }, { "lat": 39.983704, "lon": -76.735298, "type": "via" }, { "lat": 39.983578, "lon": -76.734848, "type": "via" }, { "lat": 39.983551, "lon": -76.734253, "type": "break" }, { "lat": 39.983555, "lon": -76.734116, "type": "via" }, { "lat": 39.983589, "lon": -76.733315, "type": "via" }, { "lat": 39.983719, "lon": -76.732445, "type": "via" }, { "lat": 39.983818, "lon": -76.731712, "type": "via" }, { "lat": 39.983776, "lon": -76.731506, "type": "via" }, { "lat": 39.983696, "lon": -76.731369, "type": "break" } ], "costing": "motor_scooter", "shape_match": "walk_or_snap", "filters": { "attributes": [ "edge.names", "edge.id", "edge.weighted_grade", "edge.speed" ], "action": "include" } } ``` ## trace_attributes with encoded polyline parameter ```json {"shape":[{"lat":39.983841,"lon":-76.735741,"type":"break"},{"lat":39.983704,"lon":-76.735298,"type":"via"},{"lat":39.983578,"lon":-76.734848,"type":"via"},{"lat":39.983551,"lon":-76.734253,"type":"break"},{"lat":39.983555,"lon":-76.734116,"type":"via"},{"lat":39.983589,"lon":-76.733315,"type":"via"},{"lat":39.983719,"lon":-76.732445,"type":"via"},{"lat":39.983818,"lon":-76.731712,"type":"via"},{"lat":39.983776,"lon":-76.731506,"type":"via"},{"lat":39.983696,"lon":-76.731369,"type":"break"}],"encoded_polyline":"_grbgAh~{nhF?lBAzBFvBHxBEtBKdB?fB@dBZdBb@hBh@jBb@x@\\|@x@pB\\x@v@hBl@nBPbCXtBn@|@z@ZbAEbAa@~@q@z@QhA]pAUpAVhAPlAWtASpAAdA[dASdAQhAIlARjANnAZhAf@n@`A?lB^nCRbA\\xB`@vBf@tBTbCFbARzBZvBThBRnBNrBP`CHbCF`CNdCb@vBX`ARlAJfADhA@dAFdAP`AR`Ah@hBd@bBl@rBV|B?vB]tBCvBBhAF`CFnBXtAVxAVpAVtAb@|AZ`Bd@~BJfA@fAHdADhADhABjAGzAInAAjAB|BNbCR|BTjBZtB`@lBh@lB\\|Bl@rBXtBN`Al@g@t@?nAA~AKvACvAAlAMdAU`Ac@hAShAI`AJ`AIdAi@bAu@|@k@p@]p@a@bAc@z@g@~@Ot@Bz@f@X`BFtBXdCLbAf@zBh@fBb@xAb@nATjAKjAW`BI|AEpAHjAPdAAfAGdAFjAv@p@XlAVnA?~A?jAInAPtAVxAXnAf@tBDpBJpBXhBJfBDpAZ|Ax@pAz@h@~@lA|@bAnAd@hAj@tAR~AKxAc@xAShA]hAIdAAjA]~A[v@BhB?dBSv@Ct@CvAI~@Oz@Pv@dAz@lAj@~A^`B^|AXvAVpAXdBh@~Ap@fCh@hB\\zBN`Aj@xBFdA@jALbAPbAJdAHdAJbAHbAHfAJhALbA\\lBTvBAdC@bC@jCKjASbC?`CM`CDpB\\xAj@tB\\fA\\bAVfAJdAJbAXz@L|BO`AOdCDdA@~B\\z@l@v@l@v@l@r@j@t@b@x@b@r@z@jBVfCJdAJdANbCPfCF|BRhBS~BS`AYbAe@~BQdA","shape_match":"map_snap","costing":"pedestrian","directions_options":{"units":"miles"}} ``` ## trace_attributes with include attribute filter ```json { "shape":[{"lat":39.983841,"lon":-76.735741},{"lat":39.983704,"lon":-76.735298},{"lat":39.983578,"lon":-76.734848},{"lat":39.983551,"lon":-76.734253},{"lat":39.983555,"lon":-76.734116},{"lat":39.983589,"lon":-76.733315},{"lat":39.983719,"lon":-76.732445},{"lat":39.983818,"lon":-76.731712},{"lat":39.983776,"lon":-76.731506},{"lat":39.983696,"lon":-76.731369}],"costing":"auto","shape_match":"walk_or_snap","filters":{"attributes":["edge.names","edge.id", "edge.weighted_grade","edge.speed"],"action":"include"} } ``` ## trace_attributes with exclude attribute filter ```json {"shape":[{"lat":39.983841,"lon":-76.735741},{"lat":39.983704,"lon":-76.735298},{"lat":39.983578,"lon":-76.734848},{"lat":39.983551,"lon":-76.734253},{"lat":39.983555,"lon":-76.734116},{"lat":39.983589,"lon":-76.733315},{"lat":39.983719,"lon":-76.732445},{"lat":39.983818,"lon":-76.731712},{"lat":39.983776,"lon":-76.731506},{"lat":39.983696,"lon":-76.731369}],"costing":"auto","shape_match":"walk_or_snap","filters":{"attributes":["edge.names","edge.begin_shape_index","edge.end_shape_index","shape"],"action":"exclude"}} ``` --- # Matrix Service https://docs.mapatlas.xyz/overview/directions/matrix # Matrix Service The time distance matrix service takes a sources and targets to list locations. This allows you to set the source (origin) locations separately from the target (destination) locations. The set of origins may be disjoint (not overlapping) with the set of destinations. In other words, the target locations do not have to include any locations from source locations. The time-distance matrix can return a row matrix, a column matrix, or a general matrix of computed time and distance, depending on your input for the sources and targets parameters. The general case is a row ordered matrix with the time and distance from each source location to each target location. A row vector is considered a one_to_many time-distance matrix where there is one source location and multiple target locations. The time and distance from the source location to all target locations is returned. A column matrix represents a many_to_one time-distance matrix where there are many sources and one target. Another special case is when the source location list is the same as the target location list. Here, a diagonal (square matrix with [0,0.00] on the diagonal elements) matrix is returned. The is special case is often used as the input to optimized routing problems. ## Required Parameters - **Query Param**: - `token` → Required /matrix/?token=YOUR_TOKEN (Authorization, otherwise `401 Token required` error will occur) - **Body JSON**: - `sources` → Required (List of source coordinates) - `targets` → Optional (List of target coordinates, defaults to sources if omitted) - `costing` → Required (Travel mode, e.g. `pedestrian`, `auto`, `bicycle`) --- ## Examples (Copy & Paste) ## One-to-Many ```json { "sources": [ { "lat": 40.744014, "lon": -73.990508 } ], "targets": [ { "lat": 40.744014, "lon": -73.990508 }, { "lat": 40.739735, "lon": -73.979713 }, { "lat": 40.752522, "lon": -73.985015 }, { "lat": 40.750117, "lon": -73.983704 }, { "lat": 40.750552, "lon": -73.993519 } ], "costing": "pedestrian" } ``` ## Many-to-One ```json { "sources": [ { "lat": 40.744014, "lon": -73.990508 }, { "lat": 40.739735, "lon": -73.979713 }, { "lat": 40.752522, "lon": -73.985015 }, { "lat": 40.750117, "lon": -73.983704 }, { "lat": 40.750552, "lon": -73.993519 } ], "targets": [ { "lat": 40.750552, "lon": -73.993519 } ], "costing": "pedestrian" } ``` ## Many-to-Many ```json { "sources": [ { "lat": 40.744014, "lon": -73.990508 }, { "lat": 40.739735, "lon": -73.979713 }, { "lat": 40.752522, "lon": -73.985015 }, { "lat": 40.750117, "lon": -73.983704 }, { "lat": 40.750552, "lon": -73.993519 } ], "targets": [ { "lat": 40.744014, "lon": -73.990508 }, { "lat": 40.739735, "lon": -73.979713 }, { "lat": 40.752522, "lon": -73.985015 }, { "lat": 40.750117, "lon": -73.983704 }, { "lat": 40.750552, "lon": -73.993519 } ], "costing": "pedestrian" } ``` # Costing parameter The Time-Distance Matrix service uses the auto, bicycle, pedestrian and bikeshare and other costing models available in the route service. Exception: multimodal costing is not supported for the time-distance matrix service at this time. ## Other Request Option | Option | Description | |-----------------|-------------| | **verbose** | If `true`, it will output a flat list of objects for `distances` & `durations` explicitly specifying the source & target indices. If `false`, will return more compact, nested row-major `distances` & `durations` arrays and not echo `sources` and `targets`.
**Default:** `true`. | | **shape_format** | Specifies the optional format for the path shape of each connection. One of `polyline6`, `polyline5`, `geojson`, or `no_shape` (default). | --- ## Outputs of the matrix service Depending on the `verbose` (default: `true`) request parameter, the result of the Time-Distance Matrix service is different. In both (`"verbose": true` and `"verbose": false`) cases, these parameters are present: | Item | Description | |--------------|-----------------------------------------------------------------------------| | `algorithm` | The algorithm used to compute the results. Can be `"timedistancematrix"`, `"costmatrix"`, or `"timedistancebssmatrix"`. | | `units` | Distance units for output. Allowable unit types are `"miles"` and `"kilometers"`. If no unit type is specified in the input, the units default to `"kilometers"`. | | `warnings` (optional) | This array may contain warning objects informing about deprecated request parameters, clamped values etc. | # Verbose mode ("verbose": true) The following parameters are only present in "verbose": true mode: | Item | Description | |-----------------|--------------------------------------------------| | sources | The sources passed to the request. | | targets | The targets passed to the request. | | sources_to_targets | An array of time and distance between the sources and the targets. The array is row-ordered, meaning the time and distance from the first location to all others forms the first row of the array, followed by the time and distance from the second source location to all target locations, etc. The Object contained in the arrays contains the following fields:
  • distance: The computed distance between each set of points. Distance will always be 0.00 for the first element of the time-distance array for one_to_many, the last element in a many_to_one, and the first and last elements of a many_to_many.
  • time: The computed time between each set of points. Time will always be 0 for the first element of the time-distance array for one_to_many, the last element in a many_to_one, and the first and last elements of a many_to_many.
  • to_index: The destination index into the locations array.
  • from_index: The origin index into the locations array.
When a user will arrive at/depart from this location. See the part above where we explain how time dependent matrices work for further context. Note: If the time is above the setting max_time_dep_distance_matrix this is skipped. | ## Concise mode ("verbose": false) | Item | Description | |-----------------|--------------------------------------------------| | sources_to_target | Returns an object with durations and distances as row-ordered contents of the values above. | --- ## Example Response (Verbose Mode) ```json { "sources": [ {"lat": 40.744014, "lon": -73.990508}, {"lat": 40.739735, "lon": -73.979713} ], "targets": [ {"lat": 40.752522, "lon": -73.985015}, {"lat": 40.750117, "lon": -73.983704}, {"lat": 40.750552, "lon": -73.993519} ], "sources_to_targets": [ [ { "distance": 1.342, "time": 294, "to_index": 0, "from_index": 0 }, { "distance": 0.987, "time": 215, "to_index": 1, "from_index": 0 }, { "distance": 0.532, "time": 142, "to_index": 2, "from_index": 0 } ], [ { "distance": 2.134, "time": 412, "to_index": 0, "from_index": 1 }, { "distance": 1.876, "time": 387, "to_index": 1, "from_index": 1 }, { "distance": 2.245, "time": 456, "to_index": 2, "from_index": 1 } ] ], "units": "kilometers", "algorithm": "timedistancematrix" } ``` ## Example Response (Concise Mode - verbose: false) ```json { "sources_to_targets": { "durations": [ [294, 215, 142], [412, 387, 456] ], "distances": [ [1.342, 0.987, 0.532], [2.134, 1.876, 2.245] ] }, "units": "kilometers", "algorithm": "timedistancematrix" } ``` --- # Optimization https://docs.mapatlas.xyz/overview/directions/optimization # Optimization The Optimized Route service provides a quick computation of time and distance between a set of location sources and location targets and returns them in an optimized route order, along with the shape. # Inputs of the optimized route The optimized route request run locally takes the form of json={}, where the JSON inputs inside the {} includes location information (at least four locations), as well as the name and options for the costing model Here is an example of an Optimized Route scenario: Given a list of cities and the distances and times between each pair, a salesperson wants to visit each city one time by taking the most optimized route and end at a destination (either return to origin or a different destination) ```json { "locations": [ { "lat": 40.042072, "lon": -76.306572 }, { "lat": 39.992115, "lon": -76.781559 }, { "lat": 39.984519, "lon": -76.695600 }, { "lat": 39.996586, "lon": -76.769028 }, { "lat": 39.984322, "lon": -76.706672 } ], "costing": "auto", "units": "miles" } ``` | Parameter | Location | Required | Description | | ------------- | ----------- | -------- | --------------------------------------------------------------------------------------- | | **token** | Query Param | ✅ Yes | API authentication token. Required, otherwise the API will return `401 Token required`. | | **locations** | Body JSON | ✅ Yes | Array of coordinates (`lat`, `lon`). **At least 2 locations must be provided**. | | **costing** | Body JSON | ✅ Yes | Travel mode. Options: `auto`, `pedestrian`, `bicycle`. | | **units** | Body JSON | ❌ No | Distance units. Options: `miles` (default) or `kilometers`. | # Outputs of the optimized route service These are the results of a request to the Optimized Route service. | Item | Description | |-----------------|-----------------------------------------------------------------------------| | optimized_route | Returns an optimized route path from point 'a' to point 'n'. Given a list of locations, an optimized route with stops at each intermediate location exactly one time, always starting at the first location in the list and ending at the last location. | | locations | The specified array of lat/lngs from the input request. The first and last locations in the array will remain the same as the input request. The intermediate locations may be returned reordered in the response. Due to the reordering of the intermediate locations, an `original_index` is also part of the `locations` object within the response. This is an identifier of the location index that will allow a user to easily correlate input locations with output locations. | | units | Distance units for output. Allowable unit types are mi (miles) and km (kilometers). If no unit type is specified, the units default to kilometers. | | warnings (optional) | This array may contain warning objects informing about deprecated request parameters, clamped values etc. | --- ## Example Response ```json { "optimized_route": [ { "trip": { "locations": [ { "type": "break", "lat": 40.042072, "lon": -76.306572, "original_index": 0 }, { "type": "break", "lat": 39.984519, "lon": -76.695600, "original_index": 2 }, { "type": "break", "lat": 39.984322, "lon": -76.706672, "original_index": 4 }, { "type": "break", "lat": 39.996586, "lon": -76.769028, "original_index": 3 }, { "type": "break", "lat": 39.992115, "lon": -76.781559, "original_index": 1 } ], "legs": [ { "summary": { "time": 2156.785, "length": 41.812, "cost": 2156.785 }, "shape": "encoded_polyline_string_here" } ], "summary": { "time": 2156.785, "length": 41.812, "cost": 2156.785 }, "status_message": "Found route between points", "status": 0, "units": "miles", "language": "en-US" } } ], "units": "miles" } ``` **Note:** The `locations` array shows the optimized order of stops. Use the `original_index` field to map back to your input locations. --- # Autocomplete Endpoint https://docs.mapatlas.xyz/overview/geocoder/autocomplete # Autocomplete Endpoint ::: danger Deprecated — do not use for new development This is the **v1** autocomplete endpoint. It is still served and existing integrations keep working, but it is no longer the recommended API and will not receive new features. **For anything new, use [v2](/overview/geocoder/v2/autocomplete).** v2 adds session-based billing (materially cheaper for as-you-type search), richer results, and a [suggest → retrieve](/overview/geocoder/v2/retrieve) flow. The easiest path is one of the [geocoding SDKs](/overview/sdk/geocoding/), which handle sessions, debouncing and error typing for you. ::: The **Autocomplete API** provides instant place and address suggestions based on partial user input. It is typically used in search boxes where results update in real time. ## How it Works - Send a **partial query** (`text`) such as `"YMCA"`. - The API returns a **list of matching places** (venues, streets, cities, etc.) with coordinates and metadata. - You can restrict search by location using **boundary parameters**. - **Important:** You must include a valid `token` parameter in the request. If missing or invalid, the API will return `401 Token required`. ## Endpoint ``` GET https://gateway.mapmetrics-atlas.net/autocomplete/ ``` ## Parameters # Search API Parameters | Parameter | Type | Req | Example | Description | |---------------------------|--------------------------|-----|---------------------|-----------------------------------------------------------------------------| | `text` | string | ✅ | `Union Square` | The search query text. | | `token` | string (auth token) | ✅ | `abcd1234` | Required authentication token, otherwise request returns `401 Token required`. | | `focus.point.lat` | float | ❌ | `48.581755` | Latitude for search focus. | | `focus.point.lon` | float | ❌ | `7.745843` | Longitude for search focus. | | `boundary.rect.min_lon` | float | ❌ | `139.2794` | Minimum longitude of bounding box. | | `boundary.rect.max_lon` | float | ❌ | `140.1471` | Maximum longitude of bounding box. | | `boundary.rect.min_lat` | float | ❌ | `35.53308` | Minimum latitude of bounding box. | | `boundary.rect.max_lat` | float | ❌ | `35.81346` | Maximum latitude of bounding box. | | `boundary.circle.lat` | float | ❌ | `43.818156` | Circle boundary center latitude. | | `boundary.circle.lon` | float | ❌ | `-79.186484` | Circle boundary center longitude. | | `boundary.circle.radius` | float | ❌ | `35` | Circle radius (in kilometers). | | `sources` | string (comma-separated) | ❌ | `openstreetmap,wof` | Data sources to include. | | `layers` | string (comma-separated) | ❌ | `address,venue` | Feature layers to include. | | `boundary.country` | string (comma-separated) | ❌ | `GBR,FRA` | ISO country codes to limit search. | | `boundary.gid` | Pelias `gid` | ❌ | `whosonfirst:locality:101748355` | Restrict results to a specific boundary by gid. | | `size` | integer | ❌ | `20` | Number of results to return. | ## Example ```bash GET https://gateway.mapmetrics-atlas.net/autocomplete/?text=YMCA&boundary.circle.lon=-79.186484&boundary.circle.lat=43.818156&boundary.circle.radius=35&token=YOUR_API_KEY ``` ## Example Response ```json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-78.861037, 43.900526] }, "properties": { "name": "Durham Family YMCA", "country": "Canada", "region": "Ontario", "locality": "Oshawa", "label": "Durham Family YMCA, Oshawa, ON, Canada" } } ] } ``` --- # Forward Geocode https://docs.mapatlas.xyz/overview/geocoder/forwardgeocode # Forward Geocode ::: danger Deprecated — do not use for new development This is the **v1** forward geocoding endpoint. It is still served and existing integrations keep working, but it is no longer the recommended API and will not receive new features. **For anything new, use [v2](/overview/geocoder/v2/forwardgeocode).** v2 adds session-based billing (materially cheaper for as-you-type search), richer results, and a [suggest → retrieve](/overview/geocoder/v2/retrieve) flow. The easiest path is one of the [geocoding SDKs](/overview/sdk/geocoding/), which handle sessions, debouncing and error typing for you. ::: Geocoding is the process of matching an address or other text to its corresponding geographic coordinates. ## Search the world In the simplest search, you can provide only one parameter, the text you want to match in any part of the location details. To do this, build a query where the text parameter is set to the item you want to find. For example, if you want to find a YMCA facility, here's what you'd need to append to the base URL of the service. # Note the parameter values are set as follows: | parameter | value | |-----------|-------| |`token` |*string| | text | YMCA | In the example above, you will find the name of each matched locations in a property named `'label'`. The top 10 labels returned at the time of writing were: - YMCA, Bargoed Community, United Kingdom - YMCA, Nunspeet, Gelderland - YMCA, Belleville, IL - YMCA, Forest City, IA - YMCA, Fargo, ND - YMCA, Taipei, Taipei City - YMCA, Orpington, Greater London - YMCA, Frisco, TX - YMCA, Jefferson, OH - YMCA, Belleville, IL Spelling matters, but not capitalization. You can type `ymca`, `YMCA`, or even `yMcA`. See for yourself by comparing the results of the earlier search to the following: [/forward-geocode/?token=YOUR_TOKEN&text=ymcA](https://gateway.mapmetrics-atlas.net/forward-geocode/?token=YOUR_TOKEN&text=ymcA) Note that the results are spread out throughout the world because you have not given your current location or provided any other geographic context in which to search. ## Set the number of results returned By default, returns up to 10 results. If you want a different number, set the size parameter to the desired number. This example shows returning only the first result. | parameter | value | |-----------|-------| |`token` |*string| | text | YMCA | | size | 1 | If you want 25 results, you can build the query where size is 25. ## Narrow your search If you are looking for places in a particular region, or country, or only want to look in the immediate vicinity of a user with a known location, you can narrow your search to an area. There are different ways of including a region in your query. we supports three types: country, rectangle, and circle. Sometimes your work might require that all the search results be from a particular country or a list of countries. To do this, you can set the boundary.country parameter value to a comma separated list of alpha-2 or alpha-3 ISO-3166 country code. Now, you want to search for YMCA again, but this time only in Great Britain. To do this, you will need to know that the alpha-3 code for Great Britain is GBR and set the parameters like this: [/forward-geocode/?token=YOUR_TOKEN&text=YMCA&boundary.country=GBR](https://gateway.mapmetrics-atlas.net/forward-geocode/?token=YOUR_TOKEN&text=YMCA&boundary.country=GBR) | parameter | value | | :--- | :--- | | `token` |*string| | `text` | YMCA | | `boundary.country` | GBR | Note that all the results are within Great Britain: * YMCA, Bargoed Community, United Kingdom * YMCA, Orpington, Greater London * YMCA, Erdington, West Midlands * YMCA, Malvern CP, United Kingdom * YMCA, Ancoats, Greater Manchester * YMCA, Carmarthen Community, United Kingdom * YMCA, Halebank, Cheshire * YMCA, Brightlingsea CP, United Kingdom * YMCA, Lenton Abbey, Nottinghamshire * YMCA, Old Clee, Lincolnshire If you try the same search request with different country codes, the results change to show YMCA locations within this region. Results in the United States: * YMCA, Belleville, IL * YMCA, Forest City, IA * YMCA, Fargo, ND * YMCA, Frisco, TX * YMCA, Jefferson, OH * YMCA, Belleville, IL * YMCA, Chapel Hill, NC * YMCA, West Lampeter, PA * YMCA, Bremerton, WA * YMCA, Westerly, RI ## Search within a rectangular region To specify the boundary using a rectangle, you need latitude, longitude coordinates for two diagonals of the bounding box (the minimum and the maximum latitude, longitude). For example, to find a YMCA within the state of Texas, you can set the `boundary.rect.*` parameter to values representing the bounding box around Texas: min_lon=-106.65 min_lat=25.84 max_lon=-93.51 max_lat=36.5 [/forward-geocode/?token=YOUR_TOKEN&text=YMCA&boundary.rect.min_lat=25.84&boundary.rect.min_lon=-106.65&boundary.rect.max_lat=36.5&boundary.rect.max_lon=-93.51] | parameter | value | | :--- | :--- | | `token` |*string| | `text` | YMCA | | `boundary.rect.min_lat` | 25.84 | | `boundary.rect.min_lon` | -106.65 | | `boundary.rect.max_lat` | 36.5 | | `boundary.rect.max_lon` | -93.51 | * YMCA, Austin, TX * YMCA, Frisco, TX * Y.M.C.A, Fort Worth, TX * YMCA, Rockwall, TX * YMCA, Missouri City, TX * YMCA, Northshore, TX * YMCA, Austin, TX * YMCA, Tulsa, OK * YMCA, Los Alamos, NM * YMCA, Tulsa, OK ## Search within a circular region Sometimes you don't have a rectangle to work with, but rather you have a point on earth—for example, your location coordinates—and a maximum distance within which acceptable results can be located. In this example, you want to find all YMCA locations within a 35-kilometer radius of a location in Ontario, Canada. This time, you can use the `boundary.circle.*` parameter group, where `boundary.circle.lat` and `boundary.circle.lon` is your location in Ontario and `boundary.circle.radius` is the acceptable distance from that location. Note that the `boundary.circle.radius` parameter is always specified in kilometers. [/forward-geocode/?token=YOUR_TOKEN&text=YMCA&boundary.circle.lat=43.818156&boundary.circle.lon=-79.186484&boundary.circle.radius=35] | parameter | value | | :--- | :--- | | `token` |*string| | `text` | YMCA | | `boundary.circle.lat` | 43.818156 | | `boundary.circle.lon` | -79.186484 | | `boundary.circle.radius` | 35 | You can see the results have fewer than the standard 10 items because there are not that many YMCA locations in the specified radius: * YMCA, Toronto, Ontario * YMCA, Markham, Ontario * YMCA, Toronto, Ontario * Metro Central YMCA, Toronto, Ontario * Pinnacle Jr YMCA, Toronto, Ontario * Cooper Koo Family Cherry Street YMCA Centre, Toronto, Ontario ## Search within a parent administrative area forward-geocode has a powerful understanding of relationships between places. In particular, it has a concept called the administrative hierarchy: each record in our map is listed as belonging to a parent neighbourhood, city, region, country, and other regions. This has many uses, including filtering. The map global id (gid) of any record can be used with the boundary.gid filter to return only records with a given parent. For example, finding YMCAs in [Oklahoma](https://en.wikipedia.org/wiki/Oklahoma) with only a bounding box would be challenging: the bounding box would include much of nearby Texas, possibly leading to incorrect results. With `boundary.gid`, this query can return accurate results. [/v1/search?text=YMCA&__boundary.gid=whosonfirst:region:85688585__](http://pelias.github.io/compare/#/v1/search%3Fboundary.gid=whosonfirst:region:85688585&text=ymca) * YMCA, Stillwater, OK, USA * YMCA, Edmond, OK, USA * YMCA, Guymon, OK, USA * YMCA, Grove, OK, USA * YMCA, Midwest City, OK, USA * YMCA, Shawnee, OK, USA * YMCA, Owasso, OK, USA * YMCA, Tulsa, OK, USA * YMCA, The Village, OK, USA * YMCA, Broken Arrow, OK, USA ![Searching within multiple regions](../assets/images/overlapping_boundaries.gif) By specifying a `focus.point`, results will be sorted in part by their proximity to the given coordinate. All else being equal, results closest to the point will show up higher. However, unlike a `boundary.circle` query, important results far from the given coordinate may still be returned. This allows. To find YMCAs again, but this time near a specific coordinate location (representing the Sydney Opera House) in Sydney, Australia, use `focus.point`. [/forward-geocode/?token=YOUR_TOKEN&text=YMCA&focus.point.lat=-33.856680&focus.point.lon=151.215281] | parameter | value | | :--- | :--- | | `token`| *string | | `text` | YMCA | | `focus.point.lat` | -33.856680 | | `focus.point.lon` | 151.215281 | Looking at the results, you can see that the few locations closer to this location show up at the top of the list, sorted by distance. You also still get back a significant amount of remote locations, for a well balanced mix. Because you provided a focus point, we can compute distance from that point for each resulting feature. * YMCA, Redfern, New South Wales [distance: 3.836] * YMCA, St Ives (NSW), New South Wales [distance: 14.844] * YMCA, Epping (NSW), New South Wales [distance: 16.583] * YMCA, Revesby, New South Wales [distance: 21.335] * YMCA, Kochâang, South Gyeongsang [distance: 8071.436] * YMCA, Center, IN [distance: 14882.675] * YMCA, Lake Villa, IL [distance: 14847.667] * YMCA, Onondaga, NY [distance: 15818.08] * YMCA, 's-Gravenhage, Zuid-Holland [distance: 16688.292] * YMCA, Loughborough, United Kingdom [distance: 16978.367] ## Prioritize around a point | parameter | value | | :--- | :--- | | `token`| *string | | `text` | YMCA | | `focus.point.lat` | -33.856680 | | `focus.point.lon` | 151.215281 | | `boundary.country` | AUS | The results below look different from the ones you saw before with only a focus point specified. These results are all from within Australia. You'll note the closest results show up at the top of the list, which is helped by the focus parameter. * YMCA, Redfern, New South Wales [distance: 3.836] * YMCA, St Ives (NSW), New South Wales [distance: 14.844] * YMCA, Epping (NSW), New South Wales [distance: 16.583] * YMCA, Revesby, New South Wales [distance: 21.335] * YMCA, Larrakeyah, Northern Territory [distance: 3144.296] * YMCA, Kepnock, Queensland [distance: 1001.657] * YMCA, Kings Meadows, Tasmania [distance: 917.144] * YMCA, Katherine East, Northern Territory [distance: 2873.376] * YMCA, Sadadeen, Northern Territory [distance: 2026.731] * YMCA, Ararat, Victoria [distance: 841.022] ## Prioritize within a circular region | parameter | value | | :--- | :--- | | `token`| *string | | `text` | YMCA | | `focus.point.lat` | -33.856680 | | `focus.point.lon` | 151.215281 | | `boundary.circle.lat` | -33.856680 | | `boundary.circle.lon` | 151.215281 | | `boundary.circle.radius` | 50 | Looking at these results, they are all less than 50 kilometers away from the focus point: * YMCA, Redfern, New South Wales [distance: 3.836] * YMCA, St Ives (NSW), New South Wales [distance: 14.844] * YMCA, Epping (NSW), New South Wales [distance: 16.583] * YMCA, Revesby, New South Wales [distance: 21.335] * Caringbah YMCA, Caringbah, New South Wales [distance: 22.543] * YMCA building, Loftus, New South Wales [distance: 25.756] ## Filter by data source The search examples so far have returned a mix of results from all the data sources available to Pelias. Here are the sources being searched: | source | name | short name | |---|---|---| | [OpenStreetMap](http://www.openstreetmap.org/) | `openstreetmap` | `osm` | | [OpenAddresses](http://openaddresses.io/) | `openaddresses` | `oa` | | [Who's on First](https://whosonfirst.org) | `whosonfirst` | `wof` | | [GeoNames](http://www.geonames.org/) | `geonames` | `gn` | | parameter | value | | :--- | :--- | | `token`| *string | | `text` | YMCA | | `sources` | oa | Because OpenAddresses is, as the name suggests, only address data, here's what you can expect to find: * 20 Ymca Drive, Niagara, ON, Canada * 341 Ymca Rd, New Hope, AL, USA * 318 Ymca Rd, New Hope, AL, USA * 138 Ymca Rd, New Hope, AL, USA * 304 Ymca Rd, New Hope, AL, USA * 1919 Ymca Lane, Minnetonka, MN, USA * 101 Ymca Dr, Kannapolis, NC, USA * 2121 Ymca Camp Road, Stokes County, NC, USA * 1110 Ymca Camp Road, Stokes County, NC, USA * 1581 Ymca Camp Road, Stokes County, NC, USA ## Filters by data type Here's a list of the types of places you could find in the results, sorted by granularity: |layer|description| |----|----| |`venue`|points of interest, businesses, things with walls| |`address`|places with a street address| |`street`|streets,roads,highways| |`neighbourhood`|social communities, neighbourhoods| |`borough`|a local administrative boundary, currently only used for New York City| |`localadmin`|local administrative boundaries| |`locality`|towns, hamlets, cities| |`county`|official governmental area; usually bigger than a locality, almost always smaller than a region| |`macrocounty`|a related group of counties. Mostly in Europe.| |`region`|states and provinces| |`macroregion`|a related group of regions. Mostly in Europe| |`country`|places that issue passports, nations, nation-states| |`coarse`|alias for simultaneously using all administrative layers (everything except `venue` and `address`)| [/forward-geocode/?token=YOUR_TOKEN&text=YMCA&layers=street,venue] ## Available search parameters | Parameter | Type | Required | Default | Example | | --- | --- | --- | --- | --- | | `token`| *string |yes|---|---| | `text` | string | yes | none | `Union Square` | | `focus.point.lat` | floating point number | no | none | `48.581755` | | `focus.point.lon` | floating point number | no | none | `7.745843` | | `boundary.rect.min_lon` | floating point number | no | none | `139.2794` | | `boundary.rect.max_lon` | floating point number | no | none | `140.1471` | | `boundary.rect.min_lat` | floating point number | no | none | `35.53308` | | `boundary.rect.max_lat` | floating point number | no | none | `35.81346` | | `boundary.circle.lat` | floating point number | no | none | `43.818156` | | `boundary.circle.lon` | floating point number | no | none | `-79.186484` | | `boundary.circle.radius` | floating point number | no | 50 | `35` | | `boundary.gid` | Pelias `gid` | no | none | `whosonfirst:locality:101748355` | | `sources` | string | no | all sources: osm,oa,gn,wof | openstreetmap,wof | | `layers` | string | no | all layers: address,venue,neighbourhood,locality,borough,localadmin,county,macrocounty,region,macroregion,country,coarse,postalcode | address,venue | | `boundary.country` | string | no | none | `GBR,FRA` | | `size` | integer | no | 10 | 20 | --- # Reverse Geocoding API https://docs.mapatlas.xyz/overview/geocoder/reversegeocode # Reverse Geocoding API ::: danger Deprecated — do not use for new development This is the **v1** reverse geocoding endpoint. It is still served and existing integrations keep working, but it is no longer the recommended API and will not receive new features. **For anything new, use [v2](/overview/geocoder/v2/reversegeocode).** v2 adds session-based billing (materially cheaper for as-you-type search), richer results, and a [suggest → retrieve](/overview/geocoder/v2/retrieve) flow. The easiest path is one of the [geocoding SDKs](/overview/sdk/geocoding/), which handle sessions, debouncing and error typing for you. ::: The Reverse Geocoding API takes a **latitude/longitude point** and returns the closest address or place. | Parameter | Type | Req | Example | Description | | ------------------------ | ------------------------ | --- |--------------------------------|---------------------------------------------------------------------------- | | `point.lat` | float | ✅ | `48.858268` | Latitude of the point to reverse geocode. | | `point.lon` | float | ✅ | `2.294471` | Longitude of the point to reverse geocode. | | `token` | string (auth token) | ✅ | `abcd1234` | Required authentication token, otherwise request returns `401 Token required`.| | `boundary.circle.lat` | float | ❌ | `48.858268` | Circle boundary center latitude. | | `boundary.circle.lon` | float | ❌ | `2.294471` | Circle boundary center longitude. | | `boundary.circle.radius` | float | ❌ | `35` | Circle radius (in kilometers). *(Note: ignored for coarse reverse lookups)* | | `sources` | string (comma-separated) | ❌ | `oa,gn` | Data sources to include (`oa=openaddresses`, `gn=geonames`). | | `layers` | string (comma-separated) | ❌ | `address,locality` | Feature layers to include. | | `boundary.country` | string (comma-separated) | ❌ | `FR,GBR` | ISO country codes to limit search. | | `boundary.gid` | Pelias `gid` | ❌ | `whosonfirst:region:85683497` | Restrict results to a specific boundary by gid. | | `size` | integer | ❌ | `3` | Number of results to return. | ## Example ```bash GET https://gateway.mapmetrics-atlas.net/reverse-geocode/?point.lat=48.858268&point.lon=2.294471&boundary.circle.radius=7&size=3&layers=locality,address&sources=oa,gn&boundary.country=FR&boundary.gid=whosonfirst:region:85683497&token=YOUR_API_KEY ``` ## Example Response ```json { "geocoding": { "version": "0.2", "attribution": "http://localhost:4000/attribution", "query": { "layers": [ "locality", "address" ], "sources": [ "openaddresses", "geonames" ], "size": 3, "private": false, "point.lat": 48.858268, "point.lon": 2.294471, "boundary.circle.radius": 5, "boundary.circle.lat": 48.858268, "boundary.circle.lon": 2.294471, "boundary.country": [ "FRA" ], "boundary.gid": "85683497", "lang": { "name": "English", "iso6391": "en", "iso6393": "eng", "via": "default", "defaulted": true }, "querySize": 6 }, "engine": { "name": "Pelias", "author": "Mapzen", "version": "1.0" }, "timestamp": 1755669190951 }, "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 2.294511, 48.857747 ] }, "properties": { "id": "fr/countrywide:3edd29e1f32d7408", "gid": "openaddresses:address:fr/countrywide:3edd29e1f32d7408", "layer": "address", "source": "openaddresses", "source_id": "fr/countrywide:3edd29e1f32d7408", "country_code": "FR", "name": "12 Avenue Pierre Loti", "housenumber": "12", "street": "Avenue Pierre Loti", "postalcode": "75007", "confidence": 0.8, "distance": 0.058, "accuracy": "point", "country": "France", "country_gid": "whosonfirst:country:85633147", "country_a": "FRA", "macroregion": "Ile-of-France", "macroregion_gid": "whosonfirst:macroregion:404227465", "macroregion_a": "IF", "region": "Paris", "region_gid": "whosonfirst:region:85683497", "region_a": "VP", "localadmin": "Paris", "localadmin_gid": "whosonfirst:localadmin:1159322569", "locality": "Paris", "locality_gid": "whosonfirst:locality:101751119", "borough": "7th Arrondissement", "borough_gid": "whosonfirst:borough:1158894245", "neighbourhood": "Gros Caillou", "neighbourhood_gid": "whosonfirst:neighbourhood:85873841", "continent": "Europe", "continent_gid": "whosonfirst:continent:102191581", "label": "12 Avenue Pierre Loti, Paris, France" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 2.293651, 48.858317 ] }, "properties": { "id": "fr/countrywide:afc231c551e25607", "gid": "openaddresses:address:fr/countrywide:afc231c551e25607", "layer": "address", "source": "openaddresses", "source_id": "fr/countrywide:afc231c551e25607", "country_code": "FR", "name": "2 Avenue Pierre Loti", "housenumber": "2", "street": "Avenue Pierre Loti", "postalcode": "75007", "confidence": 0.8, "distance": 0.06, "accuracy": "point", "country": "France", "country_gid": "whosonfirst:country:85633147", "country_a": "FRA", "macroregion": "Ile-of-France", "macroregion_gid": "whosonfirst:macroregion:404227465", "macroregion_a": "IF", "region": "Paris", "region_gid": "whosonfirst:region:85683497", "region_a": "VP", "localadmin": "Paris", "localadmin_gid": "whosonfirst:localadmin:1159322569", "locality": "Paris", "locality_gid": "whosonfirst:locality:101751119", "borough": "7th Arrondissement", "borough_gid": "whosonfirst:borough:1158894245", "neighbourhood": "Gros Caillou", "neighbourhood_gid": "whosonfirst:neighbourhood:85873841", "continent": "Europe", "continent_gid": "whosonfirst:continent:102191581", "label": "2 Avenue Pierre Loti, Paris, France" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 2.294373, 48.858816 ] }, "properties": { "id": "fr/countrywide:c15503f350dea57a", "gid": "openaddresses:address:fr/countrywide:c15503f350dea57a", "layer": "address", "source": "openaddresses", "source_id": "fr/countrywide:c15503f350dea57a", "country_code": "FR", "name": "3 Avenue Anatole France", "housenumber": "3", "street": "Avenue Anatole France", "postalcode": "75007", "confidence": 0.8, "distance": 0.061, "accuracy": "point", "country": "France", "country_gid": "whosonfirst:country:85633147", "country_a": "FRA", "macroregion": "Ile-of-France", "macroregion_gid": "whosonfirst:macroregion:404227465", "macroregion_a": "IF", "region": "Paris", "region_gid": "whosonfirst:region:85683497", "region_a": "VP", "localadmin": "Paris", "localadmin_gid": "whosonfirst:localadmin:1159322569", "locality": "Paris", "locality_gid": "whosonfirst:locality:101751119", "borough": "7th Arrondissement", "borough_gid": "whosonfirst:borough:1158894245", "neighbourhood": "Gros Caillou", "neighbourhood_gid": "whosonfirst:neighbourhood:85873841", "continent": "Europe", "continent_gid": "whosonfirst:continent:102191581", "label": "3 Avenue Anatole France, Paris, France" } } ], "bbox": [ 2.293651, 48.857747, 2.294511, 48.858816 ] } ``` # Description With reverse geocoding with Pelias, you can look up all sorts of information about points on a map, # Size A basic parameter for filtering is `size`, which is used to limit the number of results returned. In the earlier request that returned the Eiffel Tower (or 'Tour Eiffel', to be exact), notice that other results were returned including "Bureau de Gustave Eiffel" (a museum) and "Le Jules Verne" (a restaurant). To limit a reverse geocode to only the first result, pass the `size` parameter: > /reverse?point.lat=48.858268&point.lon=2.294471&___size=1___ --- # Autocomplete Endpoint (v2) https://docs.mapatlas.xyz/overview/geocoder/v2/autocomplete # Autocomplete Endpoint (v2) The **v2 Autocomplete API** provides instant place and address suggestions based on partial user input. It is typically used in search boxes where results update in real time as the user types. > **Base URL:** all v2 examples on this page use `https://gateway.mapmetrics-atlas.net`. > **Auth:** the `token` parameter is a **query parameter**, not a header. > **Trailing slash is mandatory** on every v2 path — `/v2/autocomplete` (no > trailing slash) returns `404`. Always call `/v2/autocomplete/`. ## How it Works - Send a **partial query** using the `q` parameter, such as `"Nieuwezijds Voorburgwal 147"`. - The API returns a **list of matching suggestions** (venues, streets, addresses, localities) with no coordinates attached — see below. - To resolve a suggestion to actual coordinates, take its `ord` value and call [`/v2/retrieve/`](./retrieve.md). - Pass a stable `session_token` across all keystrokes of one search so the whole typing sequence bills as a single session — see [Sessions](./sessions.md) for the full billing model. ::: danger Common mistake: `q`, not `text` The parameter is **`q`**. Sending `text=Nieuwezijds Voorburgwal 147` does **not** error — it returns HTTP 200 with `"results": []` and `"q": ""`. It silently looks like "no matches" instead of failing loudly. This is the single most common integration mistake against this endpoint. ::: ::: warning `proximity` is longitude first `proximity` takes the form `,` — **longitude before latitude**. Reversing the order does not error either; it returns plausible-looking but wrong results. A query centred on Maastricht with lat/lon swapped returned hits 22–27 km away, in the wrong town entirely. ::: ## Endpoint ``` GET https://gateway.mapmetrics-atlas.net/v2/autocomplete/ ``` ## Parameters | Parameter | Type | Req | Example | Description | |-----------------|--------|-----|----------------------|----------------------------------------------------------------------| | `q` | string | ✅ | `Nieuwezijds Voorburgwal 147` | The partial search text. **Not** `text`. | | `token` | string | ✅ | `YOUR_API_KEY` | Auth token, passed as a query parameter. | | `country` | string | ❌ | `nl` | ISO-2 lowercase country code to restrict results. | | `session_token` | string | ❌ | `sess_a1b2c3` | Groups keystrokes into one billable session. See [Sessions](./sessions.md). | | `proximity` | string | ❌ | `5.7423,50.8514` | `,` bias point. **Longitude first.** | ## Example ```bash curl "https://gateway.mapmetrics-atlas.net/v2/autocomplete/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&session_token=sess_a1b2c3&token=YOUR_API_KEY" ``` ## Example Response ```json { "count": 1, "q": "Nieuwezijds Voorburgwal 147", "mode": "autocomplete", "country": "nl", "elapsed_ms": 4, "results": [ { "id": "osm:ext:7f3a9c1d2e4b5a6f", "ord": 128371, "layer": "address", "country": "nl", "hn": true, "housenumber": "25", "text": "Nieuwezijds Voorburgwal 147", "place_name": "Nieuwezijds Voorburgwal 147, Maastricht", "locality": "Maastricht", "category": null, "brand": null } ] } ``` Note that no result carries `center`, `geometry`, or `bbox` — coordinates are deliberately withheld. Suggestions are cheap to serve at every keystroke; coordinate resolution is the billable step, and it only happens when you call [`/v2/retrieve/`](./retrieve.md) on the suggestion the user picked. --- # Forward Geocode (v2) https://docs.mapatlas.xyz/overview/geocoder/v2/forwardgeocode # Forward Geocode (v2) The **v2 Forward Geocode API** matches a full free-text query to its corresponding geographic coordinates and administrative context. ## How it Works - Send the text you want to match using the `q` parameter, such as `"Nieuwezijds Voorburgwal 147"`. - Restrict results to a country with `country`, and cap the number of results with `size`. - Pass `format=pelias` to get back a full GeoJSON `FeatureCollection` — see the trap below. ::: danger `format=pelias` is required for the GeoJSON envelope Without `format=pelias` — or with any other value — the endpoint silently returns a **different, flat shape** instead of the documented `FeatureCollection`. This is not an error; it's a different response contract. Always pass `format=pelias` explicitly. ::: ## Endpoint ``` GET https://gateway.mapmetrics-atlas.net/v2/forward-geocode/ ``` ## Parameters | Parameter | Type | Req | Example | Description | |-----------|---------|-----|------------------|------------------------------------------------------------------------| | `q` | string | ✅ | `Nieuwezijds Voorburgwal 147` | The search query text. **Not** `text`. | | `country` | string | ❌ | `nl` | ISO-2 lowercase country code to restrict search. | | `size` | integer | ❌ | `10` | Number of results to return. | | `format` | string | ✅ | `pelias` | Must be `pelias` to get the GeoJSON `FeatureCollection` shape. | | `token` | string | ✅ | `YOUR_API_KEY` | Auth token, passed as a query parameter. | ## Example ```bash curl "https://gateway.mapmetrics-atlas.net/v2/forward-geocode/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&size=10&format=pelias&token=YOUR_API_KEY" ``` ## Example Response ```json { "geocoding": { "version": "0.2", "attribution": "http://localhost:4000/attribution", "query": { "q": "Nieuwezijds Voorburgwal 147", "country": "nl", "size": 10 }, "engine": { "name": "Pelias", "author": "Mapzen", "version": "1.0" } }, "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [5.7423, 50.8514] }, "properties": { "id": "osm:ext:7f3a9c1d2e4b5a6f", "gid": "osm:address:7f3a9c1d2e4b5a6f", "layer": "address", "name": "Nieuwezijds Voorburgwal 147", "label": "Nieuwezijds Voorburgwal 147, Maastricht, Netherlands", "housenumber": "25", "street": "Nieuwezijds Voorburgwal", "locality": "Maastricht", "country": "Netherlands", "country_a": "NLD" } } ], "bbox": [5.7423, 50.8514, 5.7423, 50.8514] } ``` ::: warning `region` and `postalcode` are frequently absent Unlike the v1 Pelias response, `region` and `postalcode` are frequently **missing** from v2 `properties` — do not assume either field is always present. ::: ::: tip Attribution must be surfaced `geocoding.attribution` is the ODbL licence credit. Any client displaying results **must** surface this attribution. ::: --- # Retrieve Endpoint (v2) https://docs.mapatlas.xyz/overview/geocoder/v2/retrieve # Retrieve Endpoint (v2) The **v2 Retrieve API** resolves one [autocomplete](./autocomplete.md) suggestion into full coordinates. This is the billable step in the v2 autocomplete flow — see [Sessions](./sessions.md) for how retrieve closes a session. ## How it Works - Take the `ord` value from an autocomplete suggestion the user picked. - Call `/v2/retrieve/` with that `ord`, plus the suggestion's `country` and `layer`, to get back a `center` coordinate pair. - Pass the same `session_token` used during autocomplete so the session is closed correctly and billed once. ::: danger `ord` is the handle — `id` 404s Retrieval is keyed on **`ord`**, not `id`. Retrieving by `id` returns `404` on every layer, with no exception. Always take `ord` straight from the autocomplete suggestion you're resolving — never construct or reuse an `id`. ::: ## Endpoint ``` GET https://gateway.mapmetrics-atlas.net/v2/retrieve/ ``` ## Parameters | Parameter | Type | Req | Example | Description | |-----------------|---------|-----|----------------|----------------------------------------------------------------| | `country` | string | ✅ | `nl` | ISO-2 lowercase country code, from the suggestion. | | `layer` | string | ✅ | `address` | Layer of the suggestion, from the suggestion. | | `ord` | integer | ✅ | `128371` | The suggestion handle from `/v2/autocomplete/`. Not `id`. | | `hn` | boolean | ❌ | `true` | Housenumber flag, from the suggestion. | | `session_token` | string | ❌ | `sess_a1b2c3` | Same token used during autocomplete; closes the session. | | `token` | string | ✅ | `YOUR_API_KEY` | Auth token, passed as a query parameter. | ## Example ```bash curl "https://gateway.mapmetrics-atlas.net/v2/retrieve/?country=nl&layer=address&ord=128371&hn=true&session_token=sess_a1b2c3&token=YOUR_API_KEY" ``` ## Example Response ```json { "center": [5.7423, 50.8514], "country": "nl", "layer": "address", "locality": "Maastricht", "id": "" } ``` ::: warning `center` is `[lon, lat]` `center` is `[longitude, latitude]`, in that order — easy to swap by mistake. `id` in the response is currently a placeholder empty string; don't rely on it for anything. ::: ## Retrieve Batch `/v2/retrieve-batch/` resolves several suggestions in one call. ### Endpoint ``` GET https://gateway.mapmetrics-atlas.net/v2/retrieve-batch/ ``` ### Parameters | Parameter | Type | Req | Example | Description | |-----------|--------|-----|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------| | `items` | string | ✅ | `[{"country":"nl","layer":"address","ord":128371,"hn":true}]` (URL-encoded) | URL-encoded JSON array of `{country, layer, ord, hn?}` objects. | | `token` | string | ✅ | `YOUR_API_KEY` | Auth token, passed as a query parameter. | ::: danger Only `items` works — no repeated params `/v2/retrieve-batch/` takes **one** parameter, `items`, holding a URL-encoded JSON array. It does **not** accept repeated params: `ord=128371&ord=128372`, `ords=128371,128372`, and `ids=...` all silently return HTTP 200 with `count: 0` — no error, no results. ::: ### Example ```bash curl "https://gateway.mapmetrics-atlas.net/v2/retrieve-batch/?items=%5B%7B%22country%22%3A%22nl%22%2C%22layer%22%3A%22address%22%2C%22ord%22%3A128371%2C%22hn%22%3Atrue%7D%2C%7B%22country%22%3A%22nl%22%2C%22layer%22%3A%22address%22%2C%22ord%22%3A128372%7D%5D&token=YOUR_API_KEY" ``` The decoded `items` value in the example above is: ```json [ { "country": "nl", "layer": "address", "ord": 128371, "hn": true }, { "country": "nl", "layer": "address", "ord": 128372 } ] ``` ### Example Response ```json { "count": 2, "results": [ { "center": [5.7423, 50.8514], "id": "" }, { "center": [5.7431, 50.8519], "id": "" } ] } ``` ## See Also - [Autocomplete](./autocomplete.md) — produces the `ord` values retrieve consumes. - [Sessions](./sessions.md) — retrieve closes the session it belongs to. --- # Reverse Geocode (v2) https://docs.mapatlas.xyz/overview/geocoder/v2/reversegeocode # Reverse Geocode (v2) The **v2 Reverse Geocode API** takes a latitude/longitude point and returns the closest address or place. ## How it Works - Send the point to reverse-geocode using `point.lat` and `point.lon` — note that these parameter names literally contain dots; that is correct, not a typo. - Cap the number of results with `size`. - Pass `format=pelias` to get back a full GeoJSON `FeatureCollection` — the same trap as forward geocode applies here. ::: danger `format=pelias` is required for the GeoJSON envelope Without `format=pelias` — or with any other value — the endpoint silently returns a **different, flat shape** instead of the documented `FeatureCollection`. Always pass `format=pelias` explicitly. ::: ## Endpoint ``` GET https://gateway.mapmetrics-atlas.net/v2/reverse-geocode/ ``` ## Parameters | Parameter | Type | Req | Example | Description | |-------------|---------|-----|------------------|--------------------------------------------------------------------| | `point.lat` | float | ✅ | `50.8514` | Latitude of the point. Parameter name contains a literal dot. | | `point.lon` | float | ✅ | `5.7423` | Longitude of the point. Parameter name contains a literal dot. | | `size` | integer | ❌ | `10` | Number of results to return. | | `format` | string | ✅ | `pelias` | Must be `pelias` to get the GeoJSON `FeatureCollection` shape. | | `token` | string | ✅ | `YOUR_API_KEY` | Auth token, passed as a query parameter. | ## Example ```bash curl "https://gateway.mapmetrics-atlas.net/v2/reverse-geocode/?point.lat=50.8514&point.lon=5.7423&size=10&format=pelias&token=YOUR_API_KEY" ``` ## Example Response ```json { "geocoding": { "version": "0.2", "attribution": "http://localhost:4000/attribution", "query": { "point.lat": 50.8514, "point.lon": 5.7423, "size": 10 }, "engine": { "name": "Pelias", "author": "Mapzen", "version": "1.0" } }, "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [5.7423, 50.8514] }, "properties": { "id": "osm:ext:7f3a9c1d2e4b5a6f", "gid": "osm:address:7f3a9c1d2e4b5a6f", "layer": "address", "name": "Nieuwezijds Voorburgwal 147", "label": "Nieuwezijds Voorburgwal 147, Maastricht, Netherlands", "housenumber": "25", "street": "Nieuwezijds Voorburgwal", "locality": "Maastricht", "country": "Netherlands", "country_a": "NLD" } } ], "bbox": [5.7423, 50.8514, 5.7423, 50.8514] } ``` ::: warning `region` and `postalcode` are frequently absent As with forward geocode, `region` and `postalcode` are frequently **missing** from v2 `properties` — do not assume either field is always present. ::: ::: tip Attribution must be surfaced `geocoding.attribution` is the ODbL licence credit. Any client displaying results **must** surface this attribution. ::: --- # Sessions (v2 Autocomplete Billing) https://docs.mapatlas.xyz/overview/geocoder/v2/sessions # Sessions (v2 Autocomplete Billing) The v2 [autocomplete](./autocomplete.md) API bills by **session**, not by request. Understanding how a session starts, continues, and ends is essential to avoid unexpectedly high usage. ## How it Works - The billable unit is a **session start**, not each individual keystroke. - Pass one stable `session_token` across every autocomplete request that belongs to the same user search. A run of keystrokes sharing a token is **one session**. - A session ends — and the next request starts a new one — on any of: - a [`/v2/retrieve/`](./retrieve.md) call, which **closes** the session; - **50 suggest calls** sharing a token (the roll to a new session happens on request 51); - **2 minutes of idle time** with no request on that token. ::: danger Omitting `session_token` costs roughly 5x If you omit `session_token`, a **fresh session is minted for every request**. Each keystroke then bills as its own session instead of one session covering the whole search — in practice this multiplies cost by roughly 5x for a typical search. Always pass a `session_token`. ::: ::: warning Three retrieves in a row is three sessions Each [`/v2/retrieve/`](./retrieve.md) call **closes** the session it belongs to. If you call retrieve three times in a row on the same token — for example to try resolving several candidates — that is **three separate sessions**, not one, because each retrieve both starts (if none was already open) and closes a session. ::: ## Recommendations - **Debounce input** by roughly 150ms before firing an autocomplete request, to avoid burning through the 50-request cap on fast typists. - **Reuse the same `session_token`** for the full lifetime of one search box interaction, from the first keystroke until the user picks a result or abandons the search. - Generate a new `session_token` only when the user starts a genuinely new search (e.g. clears the box, or 2 minutes have passed). ## Worked Example A user types `"Nieuwezijds"` into a search box, one character at a time, with a single `session_token` (`sess_a1b2c3`) attached to every request: 1. `q=v` → autocomplete call, `session_token=sess_a1b2c3` 2. `q=vo` → autocomplete call, same token 3. `q=vog` → autocomplete call, same token 4. `q=voge` → autocomplete call, same token 5. `q=vogel` → autocomplete call, same token 6. `q=Nieuwezijds` → autocomplete call, same token That's **6 suggest calls sharing one token**. The user then picks `Nieuwezijds Voorburgwal 147, Maastricht` from the suggestion list, and the client calls: 7. `/v2/retrieve/?ord=128371&country=nl&layer=address&session_token=sess_a1b2c3` The retrieve call **closes the session**. **Total: one billable session**, regardless of the 6 keystrokes that led up to it. ## See Also - [Autocomplete](./autocomplete.md) — where `session_token` is attached to each suggest request. - [Retrieve](./retrieve.md) — where a session is closed. --- # MapMetrics Atlas API Overview https://docs.mapatlas.xyz/overview/ # MapMetrics Atlas API Overview Welcome to the MapMetrics Atlas API documentation. Build powerful, location-based applications with our comprehensive suite of mapping, geocoding, and routing services. ## What is MapMetrics Atlas API? MapMetrics Atlas provides a complete set of location services including interactive maps, geocoding, turn-by-turn directions, and routing optimization. Our platform is built for developers who need reliable, high-performance mapping solutions. ## Quick Start > **⚠️ Important:** You need an API key to use MapMetrics Atlas API. All API requests require authentication. [Sign up now](https://portal.mapmetrics.org/) to get started! Get started in three simple steps: 1. **[Create an API Key](https://portal.mapmetrics.org/)** - Sign up at MapMetrics Portal to get your API token and map style URL 2. **[Build Your First Map](/sdk/examples/simple-map-cdn)** - Follow our quick start guide ## API Services ### 🗺️ Maps & Vector Tiles Access customizable vector map tiles with dynamic styling options. Our maps support both light and dark themes and can be fully customized to match your application's design. **Features:** - High-performance vector tiles - Customizable map styles (dark/light themes) - Global map coverage - Support for multiple zoom levels **Get Started:** - [Map Styles Documentation](#map-styles) - [Create Custom Styles](/sdk/examples/style-creation) ### 📍 Geocoding Services Convert addresses to coordinates (forward geocoding) and coordinates to addresses (reverse geocoding). Includes autocomplete search for building location-aware features. **Endpoints:** - [Autocomplete](./geocoder/autocomplete) - Real-time search suggestions - [Forward Geocoding](./geocoder/forwardgeocode) - Address → Coordinates - [Reverse Geocoding](./geocoder/reversegeocode) - Coordinates → Address **Use Cases:** - Location search in apps - Address validation - Store locators - Location-based forms ### 🧭 Directions & Routing Calculate optimal routes, analyze reachability, and optimize multi-stop trips with our powerful routing engine. **Endpoints:** - [Directions](./directions/directions) - Turn-by-turn navigation - [Optimization](./directions/optimization) - Multi-stop route optimization - [Isochrone](./directions/isochrone) - Reachability analysis - [Matrix](./directions/matrix) - Distance/time matrices - [Map Matching](./directions/mapMatch) - GPS trace matching - [Elevation](./directions/elevation) - Elevation profiles **Supported Transport Modes:** - 🚗 Auto (car, motorcycle, taxi) - 🚴 Bicycle - 🚶 Pedestrian - 🚌 Bus - 🚛 Truck - 🛵 Motor Scooter **Use Cases:** - Navigation apps - Delivery route optimization - Fleet management - Travel time analysis ## SDKs & Platforms Choose the right SDK for your platform: | Platform | SDK | Status | Documentation | |----------|-----|--------|---------------| | **Web** | MapMetrics GL JS | ✅ Stable | [Get Started](./sdk/mapmetrics) | | **iOS** | MapMetrics Native | ✅ Stable | [Get Started](./sdk/ios-native/GettingStarted) | | **Android** | MapMetrics Native | ✅ Stable | [Get Started](./sdk/android-native/getting-started) | | **Flutter** | MapMetrics Flutter | 🟡 Beta | [Get Started](/sdk/examples/flutter-mapmetrics-intro) | ### Web SDK Features - Interactive vector maps - Markers, popups, and custom overlays - 3D buildings and terrain - Heatmaps and clustering - Custom styling **Installation:** - CDN: [Simple Map (CDN)](/sdk/examples/simple-map-cdn) - NPM: [Simple Map (NPM)](/sdk/examples/simple-map-npm) - Package: [@mapmetrics/mapmetrics-gl](https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl) - React: [React Integration](/sdk/examples/react-map-example) ### Mobile SDK Features - Native performance - Offline map support - Location tracking - Custom annotations - Gesture controls ## Authentication > **🔒 API Key Required:** MapMetrics Atlas API requires authentication for all requests. Without a valid API key, maps won't load and API calls will return 401 Unauthorized errors. All API requests require authentication using your API token. You must create an account on the [MapMetrics Portal](https://portal.mapmetrics.org/) to get your API token and map style URL. **Get Started:** 1. Visit [MapMetrics Portal](https://portal.mapmetrics.org/) 2. Create your account (free to start) 3. Get your complete map style URL (includes your API token) 4. Copy and use it in your application **Detailed Guide:** [Create API Key](/sdk/examples/key-creation) ### Don't Have an API Key Yet? If you try to use MapMetrics without an API key: - ❌ Maps will fail to load - ❌ API requests will return authentication errors - ❌ No access to geocoding or routing services **Solution:** [Create your free account](https://portal.mapmetrics.org/) in minutes! ## Map Styles All map styles are created and managed through the [MapMetrics Portal](https://portal.mapmetrics.org/). When you create an account, you'll receive a unique map style URL for your applications. ### Getting Your Map Style URL 1. **Sign up** at [MapMetrics Portal](https://portal.mapmetrics.org/) 2. **Create a new style** or use the default style provided 3. **Copy your complete style URL** from the portal (it includes your API token) 4. **Paste it directly** in your application code The portal provides you with a ready-to-use URL - just copy and paste it into your application! **Example URL:** ```text https://gateway.mapmetrics-atlas.net/styles/?fileName=753b9b14-2fcc-44d3-b273-c8b2b701647a/stylefile.json&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` ### Customize Your Map Style Through the MapMetrics Portal, you can: - **Customize colors** - Match your brand's color scheme - **Choose fonts** - Select typography for map labels - **Add/remove layers** - Control which map features are displayed - **Create multiple styles** - Different styles for different use cases - **Dark/Light themes** - Create styles optimized for different themes **Learn More:** [Style Creation Guide](/sdk/examples/style-creation) ## Migration Guides Switching from another mapping provider? We've got you covered: - [Migrate from Google Maps](/sdk/examples/google-map-to-mapmetrics) - [Migrate from Mapbox](./sdk/mapbox-migration-guide) ## Support & Resources ### Documentation - [Code Examples](/sdk/examples/Intro) - Ready-to-use code samples - [Migration Guides](./migration-guide) - Switch from other providers ### Community - [Discord Community](https://discord.com/invite/uRXQRfbb7d) - Chat with developers - [Facebook](https://www.facebook.com/MapMetrics) - Latest updates - [YouTube](https://www.youtube.com/@mapmetrics3435) - Video tutorials ### Need Help? - Browse our [examples](/sdk/examples/Intro) - Join our [Discord community](https://discord.com/invite/uRXQRfbb7d) - Check out [video tutorials](https://www.youtube.com/@mapmetrics3435) - Contact support for technical assistance ## What's Next? Ready to start building? Here are some recommended next steps: 1. **[Create your account](https://portal.mapmetrics.org/)** - Sign up and get your API key 2. **[Display your first map](/sdk/examples/simple-map-cdn)** - Quick start guide 3. **[Add markers](/sdk/examples/add-a-marker)** - Display points of interest 4. **[Implement routing](./directions/directions)** - Add turn-by-turn directions 5. **[Customize your map style](https://portal.mapmetrics.org/)** - Create custom styles in the portal --- **Let's build something amazing together!** 🚀 --- # Migration Guide https://docs.mapatlas.xyz/overview/migration-guide # Migration Guide Migrate your existing maps from Google Maps or Mapbox to MapMetrics Atlas. ## Available Migration Guides ### [Google Maps to MapMetrics](/sdk/examples/google-map-to-mapmetrics) Learn how to migrate your application from Google Maps to MapMetrics Atlas. ### [Mapbox to MapMetrics](/overview/sdk/mapbox-migration-guide) Learn how to migrate your application from Mapbox to MapMetrics Atlas. --- # Add Markers in Bulk https://docs.mapatlas.xyz/overview/sdk/android-native/annotations/add-markers # Add Markers in Bulk This example demonstrates how you can add markers in bulk. [//]: # (
) [//]: # ( 100 images on map) [//]: # () [//]: # ( 1000 images on map) [//]: # (
) ```kotlin title="BulkMarkerActivity.kt" class BulkMarkerActivity : AppCompatActivity(), OnItemSelectedListener { private lateinit var mapMetricsMap: MapMetricsMap private lateinit var mapView: MapView private var locations: List? = null private var progressDialog: ProgressDialog? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_marker_bulk) mapView = findViewById(R.id.mapView) mapView.onCreate(savedInstanceState) mapView.getMapAsync { initMap(it) } } private fun initMap(mapMetricsMap: MapMetricsMap) { this.mapMetricsMap = mapMetricsMap mapMetricsMap.setStyle(TestStyles.getPredefinedStyleWithFallback("Streets")) } override fun onCreateOptionsMenu(menu: Menu): Boolean { val spinnerAdapter = ArrayAdapter.createFromResource( this, R.array.bulk_marker_list, android.R.layout.simple_spinner_item ) spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) menuInflater.inflate(R.menu.menu_bulk_marker, menu) val item = menu.findItem(R.id.spinner) val spinner = item.actionView as Spinner spinner.adapter = spinnerAdapter spinner.onItemSelectedListener = this@BulkMarkerActivity return true } override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { val amount = Integer.valueOf(resources.getStringArray(R.array.bulk_marker_list)[position]) if (locations == null) { progressDialog = ProgressDialog.show(this, "Loading", "Fetching markers", false) lifecycleScope.launch(Dispatchers.IO) { locations = loadLocationTask(this@BulkMarkerActivity) withContext(Dispatchers.Main) { onLatLngListLoaded(locations, amount) } } } else { showMarkers(amount) } } private fun onLatLngListLoaded(latLngs: List?, amount: Int) { progressDialog!!.hide() locations = latLngs showMarkers(amount) } private fun showMarkers(amount: Int) { if (!this::mapMetricsMap.isInitialized || locations == null || mapView.isDestroyed) { return } mapMetricsMap.clear() showGlMarkers(min(amount, locations!!.size)) } private fun showGlMarkers(amount: Int) { val markerOptionsList: MutableList = ArrayList() val formatter = DecimalFormat("#.#####") val random = Random() var randomIndex: Int for (i in 0 until amount) { randomIndex = random.nextInt(locations!!.size) val latLng = locations!![randomIndex] markerOptionsList.add( MarkerOptions() .position(latLng) .title(i.toString()) .snippet(formatter.format(latLng.latitude) + "`, " + formatter.format(latLng.longitude)) ) } mapMetricsMap.addMarkers(markerOptionsList) } override fun onNothingSelected(parent: AdapterView<*>?) { // nothing selected, nothing to do! } override fun onStart() { super.onStart() mapView.onStart() } override fun onResume() { super.onResume() mapView.onResume() } override fun onPause() { super.onPause() mapView.onPause() } override fun onStop() { super.onStop() mapView.onStop() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView.onSaveInstanceState(outState) } override fun onDestroy() { super.onDestroy() if (progressDialog != null) { progressDialog!!.dismiss() } mapView.onDestroy() } override fun onLowMemory() { super.onLowMemory() mapView.onLowMemory() } private fun loadLocationTask( activity: BulkMarkerActivity, ) : List? { try { val json = GeoParseUtil.loadStringFromAssets( activity.applicationContext, "points.geojson" ) return GeoParseUtil.parseGeoJsonCoordinates(json) } catch (exception: IOException) { Timber.e(exception, "Could not add markers") } return null } } ``` --- # Annotation: Marker https://docs.mapatlas.xyz/overview/sdk/android-native/annotations/marker-annotations # Annotation: Marker This guide will show you how to add Markers in the map. `Annotation` is an overlay on top of a Map. 1. [Marker] 2. [Polyline] 3. [Polygon] A Marker shows an icon image at a geographical location. By default, marker uses a [provided image] as its icon. ![marker image] Or, the icon can be customized using [IconFactory] to generate an [Icon] using a provided image. For more customization, please read the documentation about [MarkerOptions]. In this showcase, we continue the code from the [Quickstart], rename Activity into `JsonApiActivity`, and pull the GeoJSON data from a free and public API. Then add markers to the map with GeoJSON: 1. In your module Gradle file (usually `//build.gradle`), add `okhttp` to simplify code for making HTTP requests. ```gradle dependencies { ... implementation 'com.squareup.okhttp3:okhttp:4.10.0' ... } ``` 2. Sync your Android project the with Gradle files. 3. In `JsonApiActivity` we add a new variable for `MapLibreMap`. It is used to add annotations to the map instance. ```kotlin class JsonApiActivity : AppCompatActivity() { // Declare a variable for MapView private lateinit var mapView: MapView // Declare a variable for MapMetricsMap private lateinit var mapMetricsMap: MapMetricsMap} ``` 4. Call `mapview.getMapSync()` in order to get a `MapMetricsMap` object. After `mapMetricsMap` is assigned, call the `getEarthQuakeDataFromUSGS()` method to make a HTTP request and transform data into the map annotations. ```kotlin mapView.getMapAsync { map -> mapMetricsMap = map mapMetricsMap.setStyle(styleName) // Fetch data from USGS getEarthQuakeDataFromUSGS() } ``` 5. Define a function `getEarthQuakeDataFromUSGS()` to fetch GeoJSON data from a public API. If we successfully get the response, call `addMarkersToMap()` on the UI thread. ```kotlin // Get Earthquake data from usgs.gov, read API doc at: // https://earthquake.usgs.gov/fdsnws/event/1/ private fun getEarthQuakeDataFromUSGS() { val url = "https://earthquake.usgs.gov/fdsnws/event/1/query".toHttpUrl().newBuilder() .addQueryParameter("format", "geojson") .addQueryParameter("starttime", "2022-01-01") .addQueryParameter("endtime", "2022-12-31") .addQueryParameter("minmagnitude", "5.8") .addQueryParameter("latitude", "24") .addQueryParameter("longitude", "121") .addQueryParameter("maxradius", "1.5") .build() val request: Request = Request.Builder().url(url).build() OkHttpClient().newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { Toast.makeText(this@JsonApiActivity, "Fail to fetch data", Toast.LENGTH_SHORT) .show() } override fun onResponse(call: Call, response: Response) { val featureCollection = response.body?.string() ?.let(FeatureCollection::fromJson) ?: return // If FeatureCollection in response is not null // Then add markers to map runOnUiThread { addMarkersToMap(featureCollection) } } }) } ``` 6. Now it is time to add markers into the map. - In the `addMarkersToMap()` method, we define two types of bitmap for the marker icon. - For each feature in the GeoJSON, add a marker with a snippet about earthquake details. - If the magnitude of an earthquake is bigger than 6.0, we use the red icon. Otherwise, we use the blue one. - Finally, move the camera to the bounds of the newly added markers ```kotlin private fun addMarkersToMap(data: FeatureCollection) { val bounds = mutableListOf() // Get bitmaps for marker icon val infoIconDrawable = ResourcesCompat.getDrawable( this.resources, // Intentionally specify package name // This makes copy from another project easier org.maplibre.android.R.drawable.mapmetrics_info_icon_default, theme )!! val bitmapBlue = infoIconDrawable.toBitmap() val bitmapRed = infoIconDrawable .mutate() .apply { setTint(Color.RED) } .toBitmap() // Add symbol for each point feature data.features()?.forEach { feature -> val geometry = feature.geometry()?.toJson() ?: return@forEach val point = Point.fromJson(geometry) ?: return@forEach val latLng = LatLng(point.latitude(), point.longitude()) bounds.add(latLng) // Contents in InfoWindow of each marker val title = feature.getStringProperty("title") val epochTime = feature.getNumberProperty("time") val dateString = SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.TAIWAN).format(epochTime) // If magnitude > 6.0, show marker with red icon. If not, show blue icon instead val mag = feature.getNumberProperty("mag") val icon = IconFactory.getInstance(this) .fromBitmap(if (mag.toFloat() > 6.0) bitmapRed else bitmapBlue) // Use MarkerOptions and addMarker() to add a new marker in map val markerOptions = MarkerOptions() .position(latLng) .title(dateString) .snippet(title) .icon(icon) maplibreMap.addMarker(markerOptions) } // Move camera to newly added annotations maplibreMap.getCameraForLatLngBounds(LatLngBounds.fromLatLngs(bounds))?.let { val newCameraPosition = CameraPosition.Builder() .target(it.target) .zoom(it.zoom - 0.5) .build() maplibreMap.cameraPosition = newCameraPosition } } ``` [//]: # (7. Here is the final result. For the full contents of `JsonApiActivity`, please visit source code of our [Test App].) [//]: # () [//]: # (
) [//]: # ( Screenshot with the map in demotile style) [//]: # (
) [Marker]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-marker/index.html [provided image]: https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroid/src/main/res/drawable-xxxhdpi/maplibre_marker_icon_default.png [Polyline]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-polyline/index.html [Polygon]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-polygon/index.html [marker image]: https://raw.githubusercontent.com/maplibre/maplibre-native/main/test/fixtures/sprites/default_marker.png [IconFactory]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-icon-factory/index.html [Icon]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-icon/index.html [Quickstart]: ../getting-started.md [mvn]: https://mvnrepository.com/artifact/org.maplibre.gl/android-plugin-annotation-v9 [Android Developer Documentation]: https://developer.android.com/topic/libraries/architecture/coroutines [MarkerOptions]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-marker-options/index.html [Test App]: https://github.com/MapMetrics/mapmetrics-native-sdk/tree/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/JsonApiActivity.kt --- # Animation Types https://docs.mapatlas.xyz/overview/sdk/android-native/camera/animation-types # Animation Types [//]: # ({{ activity_source_note("CameraAnimationTypeActivity.kt") }}) This example showcases the different animation types. - **Move**: available via the `MapMetricsMap.moveCamera` method. - **Ease**: available via the `MapMetricsMap.easeCamera` method. - **Animate**: available via the `MapMetricsMap.animateCamera` method. ### Move The `MapMetricsMap.moveCamera` method jumps to the camera position provided. ```kotlin val cameraPosition = CameraPosition.Builder() .target(nextLatLng) .zoom(14.0) .tilt(30.0) .tilt(0.0) .build() mapMetricsMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)) ``` [//]: # (
) [//]: # ( ) [//]: # (
) ### Ease The `MapMetricsMap.moveCamera` eases to the camera position provided (with constant ground speed). ```kotlin val cameraPosition = CameraPosition.Builder() .target(nextLatLng) .zoom(15.0) .bearing(180.0) .tilt(30.0) .build() mapMetricsMap.easeCamera( CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback ) ``` [//]: # (
) [//]: # ( ) [//]: # (
) ### Animate The `MapMetricsMap.animateCamera` uses a powered flight animation move to the camera position provided[^1]. [^1]: The implementation is based on Van Wijk, Jarke J.; Nuij, Wim A. A. “Smooth and efficient zooming and panning.” INFOVIS ’03. pp. 15–22. [https://www.win.tue.nl/~vanwijk/zoompan.pdf#page=5](https://www.win.tue.nl/~vanwijk/zoompan.pdf#page=5) ```kotlin val cameraPosition = CameraPosition.Builder().target(nextLatLng).bearing(270.0).tilt(20.0).build() mapMetricsMap.animateCamera( CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback ) ``` [//]: # (
) [//]: # ( ) [//]: # (
) ## Animation Callbacks In the previous section a `CancellableCallback` was passed to the last two animation methods. This callback shows a toast message when the animation is cancelled or when it is finished. ```kotlin private val callback: CancelableCallback = object : CancelableCallback { override fun onCancel() { Timber.i("Duration onCancel Callback called.") Toast.makeText( applicationContext, "Ease onCancel Callback called.", Toast.LENGTH_LONG ) .show() } override fun onFinish() { Timber.i("Duration onFinish Callback called.") Toast.makeText( applicationContext, "Ease onFinish Callback called.", Toast.LENGTH_LONG ) .show() } } ``` --- # Animator Animation https://docs.mapatlas.xyz/overview/sdk/android-native/camera/animator-animation # Animator Animation This example showcases how to use the Animator API to schedule a sequence of map animations. ```kotlin title="CameraAnimatorActivity.kt" /** Test activity showcasing using Android SDK animators to animate camera position changes. */ class CameraAnimatorActivity : AppCompatActivity(), OnMapReadyCallback { private val animators = LongSparseArray() private lateinit var set: Animator private lateinit var mapView: MapView private lateinit var mapMetricsMap: MapMetricsMap override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera_animator) mapView = findViewById(R.id.mapView) as MapView if (::mapView.isInitialized) { mapView.onCreate(savedInstanceState) mapView.getMapAsync(this) } } override fun onMapReady(map: MapMetricsMap) { mapMetricsMap = map map.setStyle(TestStyles.getPredefinedStyleWithFallback("Streets")) initFab() } private fun initFab() { findViewById(R.id.fab).setOnClickListener { view: View -> view.visibility = View.GONE val animatedPosition = CameraPosition.Builder() .target(LatLng(37.789992, -122.402214)) .tilt(60.0) .zoom(14.5) .bearing(135.0) .build() set = createExampleAnimator(mapMetricsMap.cameraPosition, animatedPosition) set.start() } } // // Animator API used for the animation on the FAB // private fun createExampleAnimator( currentPosition: CameraPosition, targetPosition: CameraPosition ): Animator { val animatorSet = AnimatorSet() animatorSet.play(createLatLngAnimator(currentPosition.target!!, targetPosition.target!!)) animatorSet.play(createZoomAnimator(currentPosition.zoom, targetPosition.zoom)) animatorSet.play(createBearingAnimator(currentPosition.bearing, targetPosition.bearing)) animatorSet.play(createTiltAnimator(currentPosition.tilt, targetPosition.tilt)) return animatorSet } private fun createLatLngAnimator(currentPosition: LatLng, targetPosition: LatLng): Animator { val latLngAnimator = ValueAnimator.ofObject(LatLngEvaluator(), currentPosition, targetPosition) latLngAnimator.duration = (1000 * ANIMATION_DELAY_FACTOR).toLong() latLngAnimator.interpolator = FastOutSlowInInterpolator() latLngAnimator.addUpdateListener { animation: ValueAnimator -> mapMetricsMap.moveCamera( CameraUpdateFactory.newLatLng((animation.animatedValue as LatLng)) ) } return latLngAnimator } private fun createZoomAnimator(currentZoom: Double, targetZoom: Double): Animator { val zoomAnimator = ValueAnimator.ofFloat(currentZoom.toFloat(), targetZoom.toFloat()) zoomAnimator.duration = (2200 * ANIMATION_DELAY_FACTOR).toLong() zoomAnimator.startDelay = (600 * ANIMATION_DELAY_FACTOR).toLong() zoomAnimator.interpolator = AnticipateOvershootInterpolator() zoomAnimator.addUpdateListener { animation: ValueAnimator -> mapMetricsMap.moveCamera( CameraUpdateFactory.zoomTo((animation.animatedValue as Float).toDouble()) ) } return zoomAnimator } private fun createBearingAnimator(currentBearing: Double, targetBearing: Double): Animator { val bearingAnimator = ValueAnimator.ofFloat(currentBearing.toFloat(), targetBearing.toFloat()) bearingAnimator.duration = (1000 * ANIMATION_DELAY_FACTOR).toLong() bearingAnimator.startDelay = (1000 * ANIMATION_DELAY_FACTOR).toLong() bearingAnimator.interpolator = FastOutLinearInInterpolator() bearingAnimator.addUpdateListener { animation: ValueAnimator -> mapMetricsMap.moveCamera( CameraUpdateFactory.bearingTo((animation.animatedValue as Float).toDouble()) ) } return bearingAnimator } private fun createTiltAnimator(currentTilt: Double, targetTilt: Double): Animator { val tiltAnimator = ValueAnimator.ofFloat(currentTilt.toFloat(), targetTilt.toFloat()) tiltAnimator.duration = (1000 * ANIMATION_DELAY_FACTOR).toLong() tiltAnimator.startDelay = (1500 * ANIMATION_DELAY_FACTOR).toLong() tiltAnimator.addUpdateListener { animation: ValueAnimator -> mapMetricsMap.moveCamera( CameraUpdateFactory.tiltTo((animation.animatedValue as Float).toDouble()) ) } return tiltAnimator } // // Interpolator examples // private fun obtainExampleInterpolator(menuItemId: Int): Animator? { return animators[menuItemId.toLong()] } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_animator, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (!::mapMetricsMap.isInitialized) { return false } if (item.itemId != android.R.id.home) { findViewById(R.id.fab).visibility = View.GONE resetCameraPosition() playAnimation(item.itemId) } return super.onOptionsItemSelected(item) } private fun resetCameraPosition() { mapMetricsMap.moveCamera( CameraUpdateFactory.newCameraPosition( CameraPosition.Builder() .target(START_LAT_LNG) .zoom(11.0) .bearing(0.0) .tilt(0.0) .build() ) ) } private fun playAnimation(itemId: Int) { val animator = obtainExampleInterpolator(itemId) if (animator != null) { animator.cancel() animator.start() } } private fun obtainExampleInterpolator(interpolator: Interpolator, duration: Long): Animator { val zoomAnimator = ValueAnimator.ofFloat(11.0f, 16.0f) zoomAnimator.duration = (duration * ANIMATION_DELAY_FACTOR).toLong() zoomAnimator.interpolator = interpolator zoomAnimator.addUpdateListener { animation: ValueAnimator -> mapMetricsMap.moveCamera( CameraUpdateFactory.zoomTo((animation.animatedValue as Float).toDouble()) ) } return zoomAnimator } // // MapView lifecycle // override fun onStart() { super.onStart() mapView.onStart() } override fun onResume() { super.onResume() mapView.onResume() } override fun onPause() { super.onPause() mapView.onPause() } override fun onStop() { super.onStop() mapView.onStop() for (i in 0 until animators.size()) { animators[animators.keyAt(i)]!!.cancel() } if (this::set.isInitialized) { set.cancel() } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView.onSaveInstanceState(outState) } override fun onDestroy() { super.onDestroy() if (::mapView.isInitialized) { mapView.onDestroy() } } override fun onLowMemory() { super.onLowMemory() if (::mapView.isInitialized) { mapView.onLowMemory() } } /** Helper class to evaluate LatLng objects with a ValueAnimator */ private class LatLngEvaluator : TypeEvaluator { private val latLng = LatLng() override fun evaluate(fraction: Float, startValue: LatLng, endValue: LatLng): LatLng { latLng.latitude = startValue.latitude + (endValue.latitude - startValue.latitude) * fraction latLng.longitude = startValue.longitude + (endValue.longitude - startValue.longitude) * fraction return latLng } } companion object { private const val ANIMATION_DELAY_FACTOR = 1.5 private val START_LAT_LNG = LatLng(37.787947, -122.407432) } init { val accelerateDecelerateAnimatorSet = AnimatorSet() accelerateDecelerateAnimatorSet.playTogether( createLatLngAnimator(START_LAT_LNG, LatLng(37.826715, -122.422795)), obtainExampleInterpolator(FastOutSlowInInterpolator(), 2500) ) animators.put( R.id.menu_action_accelerate_decelerate_interpolator.toLong(), accelerateDecelerateAnimatorSet ) val bounceAnimatorSet = AnimatorSet() bounceAnimatorSet.playTogether( createLatLngAnimator(START_LAT_LNG, LatLng(37.787947, -122.407432)), obtainExampleInterpolator(BounceInterpolator(), 3750) ) animators.put(R.id.menu_action_bounce_interpolator.toLong(), bounceAnimatorSet) animators.put( R.id.menu_action_anticipate_overshoot_interpolator.toLong(), obtainExampleInterpolator(AnticipateOvershootInterpolator(), 2500) ) animators.put( R.id.menu_action_path_interpolator.toLong(), obtainExampleInterpolator( PathInterpolatorCompat.create(.22f, .68f, 0f, 1.71f), 2500 ) ) } } ``` --- # CameraPosition Capabilities https://docs.mapatlas.xyz/overview/sdk/android-native/camera/cameraposition # CameraPosition Capabilities [//]: # ({{ activity_source_note("CameraPositionActivity.kt") }}) [//]: # (This example showcases how to listen to camera change events.) [//]: # () [//]: # (
) [//]: # ( ) [//]: # (
) The camera animation is kicked off with this code: ```kotlin val cameraPosition = CameraPosition.Builder().target(LatLng(latitude, longitude)).zoom(zoom).bearing(bearing).tilt(tilt).build() mapMetricsMap?.animateCamera( CameraUpdateFactory.newCameraPosition(cameraPosition), 5000, object : CancelableCallback { override fun onCancel() { Timber.v("OnCancel called") } override fun onFinish() { Timber.v("OnFinish called") } } ) ``` Notice how the color of the button in the bottom right changes color. Depending on the state of the camera. We can listen for changes to the state of the camera by registering a `OnCameraMoveListener`, `OnCameraIdleListener`, `OnCameraMoveCanceledListener` or `OnCameraMoveStartedListener` with the `MapLibreMap`. For example, the `OnCameraMoveListener` is defined with: ```kotlin private val moveListener = OnCameraMoveListener { Timber.e("OnCameraMove") fab.setColorFilter( ContextCompat.getColor(this@CameraPositionActivity, android.R.color.holo_orange_dark) ) } ``` And registered with: ```kotlin mapMetricsMap.addOnCameraMoveListener(moveListener) ``` Refer to the full example to learn the methods to register the other types of camera change events. --- # Gesture Detector https://docs.mapatlas.xyz/overview/sdk/android-native/camera/gesture-detector # Gesture Detector The gesture detector of MapMetrics Android is encapsulated in the [`mapmetrics-gestures-android`](https://github.com/MapMetrics/MapMetrics-gestures-android) package. #### Gesture Listeners You can add listeners for move, rotate, scale and shove gestures. For example, adding a move gesture listener with `MapMetricsMap.addOnRotateListener`: ```kotlin mapMetricsMap.addOnMoveListener( object : OnMoveListener { override fun onMoveBegin(detector: MoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_START, "MOVE START") ) } override fun onMove(detector: MoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_PROGRESS, "MOVE PROGRESS") ) } override fun onMoveEnd(detector: MoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_END, "MOVE END") ) recalculateFocalPoint() } } ) ``` Refer to the full example below for examples of listeners for the other gesture types. #### Settings You can access an `UISettings` object via `MapMetricsMap.uiSettings`. Available settings include: - **Toggle Quick Zoom**. You can double tap on the map to use quick zoom. You can toggle this behavior on and off (`UiSettings.isQuickZoomGesturesEnabled`). - **Toggle Velocity Animations**. By default flicking causes the map to continue panning (while decelerating). You can turn this off with `UiSettings.isScaleVelocityAnimationEnabled`. - **Toggle Rotate Enabled**. Use `uiSettings.isRotateGesturesEnabled`. - **Toggle Zoom Enabled**. Use `uiSettings.isZoomGesturesEnabled`. ## Full Example Activity ```kotlin title="GestureDetectorActivity.kt" /** Test activity showcasing APIs around gestures implementation. */ class GestureDetectorActivity : AppCompatActivity() { private lateinit var mapView: MapView private lateinit var mapMetricsMap: MapMetricsMap private lateinit var recyclerView: RecyclerView private var gestureAlertsAdapter: GestureAlertsAdapter? = null private var gesturesManager: AndroidGesturesManager? = null private var marker: Marker? = null private var focalPointLatLng: LatLng? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_gesture_detector) mapView = findViewById(R.id.mapView) mapView.onCreate(savedInstanceState) mapView.getMapAsync { map: MapMetricsMap -> mapMetricsMap = map mapMetricsMap.setStyle(TestStyles.getPredefinedStyleWithFallback("Streets")) initializeMap() } recyclerView = findViewById(R.id.alerts_recycler) recyclerView.setLayoutManager(LinearLayoutManager(this)) gestureAlertsAdapter = GestureAlertsAdapter() recyclerView.setAdapter(gestureAlertsAdapter) } override fun onResume() { super.onResume() mapView.onResume() } override fun onPause() { super.onPause() gestureAlertsAdapter!!.cancelUpdates() mapView.onPause() } override fun onStart() { super.onStart() mapView.onStart() } override fun onStop() { super.onStop() mapView.onStop() } override fun onLowMemory() { super.onLowMemory() mapView.onLowMemory() } override fun onDestroy() { super.onDestroy() mapView.onDestroy() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView.onSaveInstanceState(outState) } private fun initializeMap() { gesturesManager = mapMetricsMap.gesturesManager val layoutParams = recyclerView.layoutParams as RelativeLayout.LayoutParams layoutParams.height = (mapView.height / 1.75).toInt() layoutParams.width = mapView.width / 3 recyclerView.layoutParams = layoutParams attachListeners() fixedFocalPointEnabled(mapMetricsMap.uiSettings.focalPoint != null) } fun attachListeners() { // # --8<-- [start:addOnMoveListener] mapMetricsMap.addOnMoveListener( object : OnMoveListener { override fun onMoveBegin(detector: MoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_START, "MOVE START") ) } override fun onMove(detector: MoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_PROGRESS, "MOVE PROGRESS") ) } override fun onMoveEnd(detector: MoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_END, "MOVE END") ) recalculateFocalPoint() } } ) // # --8<-- [end:addOnMoveListener] mapMetricsMap.addOnRotateListener( object : OnRotateListener { override fun onRotateBegin(detector: RotateGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_START, "ROTATE START") ) } override fun onRotate(detector: RotateGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_PROGRESS, "ROTATE PROGRESS") ) recalculateFocalPoint() } override fun onRotateEnd(detector: RotateGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_END, "ROTATE END") ) } } ) mapMetricsMap.addOnScaleListener( object : OnScaleListener { override fun onScaleBegin(detector: StandardScaleGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_START, "SCALE START") ) if (focalPointLatLng != null) { gestureAlertsAdapter!!.addAlert( GestureAlert( GestureAlert.TYPE_OTHER, "INCREASING MOVE THRESHOLD" ) ) gesturesManager!!.moveGestureDetector.moveThreshold = ResourceUtils.convertDpToPx(this@GestureDetectorActivity, 175f) gestureAlertsAdapter!!.addAlert( GestureAlert( GestureAlert.TYPE_OTHER, "MANUALLY INTERRUPTING MOVE" ) ) gesturesManager!!.moveGestureDetector.interrupt() } recalculateFocalPoint() } override fun onScale(detector: StandardScaleGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_PROGRESS, "SCALE PROGRESS") ) } override fun onScaleEnd(detector: StandardScaleGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_END, "SCALE END") ) if (focalPointLatLng != null) { gestureAlertsAdapter!!.addAlert( GestureAlert( GestureAlert.TYPE_OTHER, "REVERTING MOVE THRESHOLD" ) ) gesturesManager!!.moveGestureDetector.moveThreshold = 0f } } } ) mapMetricsMap.addOnShoveListener( object : OnShoveListener { override fun onShoveBegin(detector: ShoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_START, "SHOVE START") ) } override fun onShove(detector: ShoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_PROGRESS, "SHOVE PROGRESS") ) } override fun onShoveEnd(detector: ShoveGestureDetector) { gestureAlertsAdapter!!.addAlert( GestureAlert(GestureAlert.TYPE_END, "SHOVE END") ) } } ) } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_gestures, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { val uiSettings = mapMetricsMap.uiSettings when (item.itemId) { R.id.menu_gesture_focus_point -> { fixedFocalPointEnabled(focalPointLatLng == null) return true } R.id.menu_gesture_animation -> { uiSettings.isScaleVelocityAnimationEnabled = !uiSettings.isScaleVelocityAnimationEnabled uiSettings.isRotateVelocityAnimationEnabled = !uiSettings.isRotateVelocityAnimationEnabled uiSettings.isFlingVelocityAnimationEnabled = !uiSettings.isFlingVelocityAnimationEnabled return true } R.id.menu_gesture_rotate -> { uiSettings.isRotateGesturesEnabled = !uiSettings.isRotateGesturesEnabled return true } R.id.menu_gesture_tilt -> { uiSettings.isTiltGesturesEnabled = !uiSettings.isTiltGesturesEnabled return true } R.id.menu_gesture_zoom -> { uiSettings.isZoomGesturesEnabled = !uiSettings.isZoomGesturesEnabled return true } R.id.menu_gesture_scroll -> { uiSettings.isScrollGesturesEnabled = !uiSettings.isScrollGesturesEnabled return true } R.id.menu_gesture_double_tap -> { uiSettings.isDoubleTapGesturesEnabled = !uiSettings.isDoubleTapGesturesEnabled return true } R.id.menu_gesture_quick_zoom -> { uiSettings.isQuickZoomGesturesEnabled = !uiSettings.isQuickZoomGesturesEnabled return true } R.id.menu_gesture_scroll_horizontal -> { uiSettings.isHorizontalScrollGesturesEnabled = !uiSettings.isHorizontalScrollGesturesEnabled return true } } return super.onOptionsItemSelected(item) } private fun fixedFocalPointEnabled(enabled: Boolean) { if (enabled) { focalPointLatLng = LatLng(51.50325, -0.12968) marker = mapMetricsMap.addMarker(MarkerOptions().position(focalPointLatLng)) mapMetricsMap.easeCamera( CameraUpdateFactory.newLatLngZoom(focalPointLatLng!!, 16.0), object : CancelableCallback { override fun onCancel() { recalculateFocalPoint() } override fun onFinish() { recalculateFocalPoint() } } ) } else { if (marker != null) { mapMetricsMap.removeMarker(marker!!) marker = null } focalPointLatLng = null mapMetricsMap.uiSettings.focalPoint = null } } private fun recalculateFocalPoint() { if (focalPointLatLng != null) { mapMetricsMap.uiSettings.focalPoint = mapMetricsMap.projection.toScreenLocation(focalPointLatLng!!) } } private class GestureAlertsAdapter : RecyclerView.Adapter() { private var isUpdating = false private val updateHandler = Handler(Looper.getMainLooper()) private val alerts: MutableList = ArrayList() class ViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { var alertMessageTv: TextView init { val typeface = FontCache.get("Roboto-Regular.ttf", view.context) alertMessageTv = view.findViewById(R.id.alert_message) alertMessageTv.typeface = typeface } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_gesture_alert, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val alert = alerts[position] holder.alertMessageTv.text = alert.message holder.alertMessageTv.setTextColor( ContextCompat.getColor(holder.alertMessageTv.context, alert.color) ) } override fun getItemCount(): Int { return alerts.size } fun addAlert(alert: GestureAlert) { for (gestureAlert in alerts) { if (gestureAlert.alertType != GestureAlert.TYPE_PROGRESS) { break } if (alert.alertType == GestureAlert.TYPE_PROGRESS && gestureAlert == alert) { return } } if (itemCount >= MAX_NUMBER_OF_ALERTS) { alerts.removeAt(itemCount - 1) } alerts.add(0, alert) if (!isUpdating) { isUpdating = true updateHandler.postDelayed(updateRunnable, 250) } } @SuppressLint("NotifyDataSetChanged") private val updateRunnable = Runnable { notifyDataSetChanged() isUpdating = false } fun cancelUpdates() { updateHandler.removeCallbacksAndMessages(null) } } private class GestureAlert( @field:Type @param:Type val alertType: Int, val message: String? ) { @Retention(AnnotationRetention.SOURCE) @IntDef(TYPE_NONE, TYPE_START, TYPE_PROGRESS, TYPE_END, TYPE_OTHER) annotation class Type @ColorInt var color = 0 override fun equals(other: Any?): Boolean { if (this === other) { return true } if (other == null || javaClass != other.javaClass) { return false } val that = other as GestureAlert if (alertType != that.alertType) { return false } return if (message != null) message == that.message else that.message == null } override fun hashCode(): Int { var result = alertType result = 31 * result + (message?.hashCode() ?: 0) return result } companion object { const val TYPE_NONE = 0 const val TYPE_START = 1 const val TYPE_END = 2 const val TYPE_PROGRESS = 3 const val TYPE_OTHER = 4 } init { when (alertType) { TYPE_NONE -> color = android.R.color.black TYPE_END -> color = android.R.color.holo_red_dark TYPE_OTHER -> color = android.R.color.holo_purple TYPE_PROGRESS -> color = android.R.color.holo_orange_dark TYPE_START -> color = android.R.color.holo_green_dark } } } companion object { private const val MAX_NUMBER_OF_ALERTS = 30 } } ``` --- # LatLngBounds API https://docs.mapatlas.xyz/overview/sdk/android-native/camera/lat-lng-bounds # LatLngBounds API [//]: # ({{ activity_source_note("LatLngBoundsActivity.kt") }}) [//]: # (This example demonstrates setting the camera to some bounds defined by some features. It sets these bounds when the map is initialized and when the [bottom sheet](https://m2.material.io/components/sheets-bottom) is opened or closed.) [//]: # () [//]: # (
) [//]: # ( ) [//]: # (
) Here you can see how the feature collection is loaded and how `MapMetricsMap.getCameraForLatLngBounds` is used to set the bounds during map initialization: ```kotlin val featureCollection: FeatureCollection = fromJson(GeoParseUtil.loadStringFromAssets(this, "points-sf.geojson")) bounds = createBounds(featureCollection) map.getCameraForLatLngBounds(bounds, createPadding(peekHeight))?.let { map.cameraPosition = it } ``` The `createBounds` function uses the `LatLngBounds` API to include all points within the bounds: ```kotlin private fun createBounds(featureCollection: FeatureCollection): LatLngBounds { val boundsBuilder = LatLngBounds.Builder() featureCollection.features()?.let { for (feature in it) { val point = feature.geometry() as Point boundsBuilder.include(LatLng(point.latitude(), point.longitude())) } } return boundsBuilder.build() } ``` --- # Max/Min Zoom https://docs.mapatlas.xyz/overview/sdk/android-native/camera/max-min-zoom # Max/Min Zoom [//]: # ({{ activity_source_note("MaxMinZoomActivity.kt") }}) This example shows how to configure a maximum and a minimum zoom level. ```kotlin mapMetricsMap.setMinZoomPreference(3.0) mapMetricsMap.setMaxZoomPreference(5.0) ``` ## Bonus: Add Click Listener As a bonus, this example also shows how you can define a click listener to the map. ```kotlin mapMetricsMap.addOnMapClickListener { if (this::mapMetricsMap.isInitialized) { mapMetricsMap.setStyle(Style.Builder().fromUri(TestStyles.AMERICANA)) } true } ``` You can remove a click listener again with `MapMetricsMap.removeOnMapClickListener`. To use this API you need to assign the click listener to a variable, since you need to pass the listener to that method. [//]: # (
) [//]: # ( ) [//]: # ( {{ openmaptiles_caption }}) [//]: # (
) --- # Scroll by Method https://docs.mapatlas.xyz/overview/sdk/android-native/camera/move-map-pixels # Scroll by Method [//]: # ({{ activity_source_note("ScrollByActivity.kt") }}) This example shows how you can move the map by x/y pixels. ```kotlin mapMetricsMap.scrollBy( (seekBarX.progress * MULTIPLIER_PER_PIXEL).toFloat(), (seekBarY.progress * MULTIPLIER_PER_PIXEL).toFloat() ) ``` [//]: # (
) [//]: # ( ![Screenshot of Example Activity to move the map by some pixels](https://github.com/user-attachments/assets/f8ae0ec7-a165-4fb3-ab9f-bfb5579c7dd8){ width="300" }) [//]: # (
) --- # Animate Camera Orbiting a Point https://docs.mapatlas.xyz/overview/sdk/android-native/camera/orbit-animation # Animate Camera Orbiting a Point This tutorial shows how to create a smooth orbiting camera animation that rotates around a central point — great for showcasing landmarks or creating cinematic map experiences. ## Prerequisites - Completed the [Getting Started Guide](../getting-started) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Orbit Animation Rotate the camera 360° around a point: ```kotlin import android.animation.ValueAnimator import android.os.Bundle import android.view.animation.LinearInterpolator import android.widget.Button import androidx.appcompat.app.AppCompatActivity import org.maplibre.android.camera.CameraPosition import org.maplibre.android.camera.CameraUpdateFactory import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.MapView import org.maplibre.android.maps.MapMetricsMap import org.maplibre.android.maps.Style class OrbitActivity : AppCompatActivity() { private lateinit var mapView: MapView private lateinit var map: MapMetricsMap private var orbitAnimator: ValueAnimator? = null private val center = LatLng(48.8584, 2.2945) // Eiffel Tower override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_orbit) mapView = findViewById(R.id.mapView) mapView.onCreate(savedInstanceState) mapView.getMapAsync { mapMetricsMap -> map = mapMetricsMap map.setStyle( Style.Builder().fromUri( "https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY" ) ) // Set initial camera with tilt map.cameraPosition = CameraPosition.Builder() .target(center) .zoom(16.0) .tilt(55.0) .bearing(0.0) .build() // Start orbit button findViewById ))} {selected && (

{selected.latitude}, {selected.longitude}

)}
); } ``` **Options:** `client` (required), `country`, `proximity`, `minLength` (default `2` — below it, no request fires and `suggestions` is cleared), `debounceMs` (extra debounce stacked on top of the client's own — leave unset unless you need a delay different from `client.debounceMs`). **Session lifecycle** matches the core `Session`: one session per `client`, reused across every keystroke; `select()` retrieves and closes it, and the next `setQuery()` reopens one automatically. `suggestCount` and `isOpen` on the hook's return value mirror `Session.suggestCount` / `Session.isOpen`. **Safety guarantees:** out-of-order responses are discarded (a slow response for an earlier keystroke never overwrites a later one); no `setState` fires after unmount; React 18 ``-safe (no duplicate session, no duplicate request on double-invoked effects). ## Routing & isochrones `mapatlas.routing` and the top-level `mapatlas.isochrone()` wrap the gateway's Valhalla-backed endpoints — directions, matrix, map matching, and route optimization. `costing` is a strict union (`'auto' | 'bicycle' | 'bus' | 'truck' | 'taxi' | 'motor_scooter' | 'pedestrian' | 'bikeshare'`), not a bare string, and this client always hits `/optimization/`, not `/optimize/` (the latter 404s). See the [README](https://github.com/MapMetrics/geocoder-sdk/tree/main/NPM#routing--isochrones) for the full request/response shapes. ## The OSM tier `tier: 'osm'` talks to the free, OpenStreetMap-only endpoints (`/osm-geocode/`, `/osm-reverse/`, `/osm-autocomplete/`) via the `osm-geocode` scope. It has no session/retrieve flow — `search()`, `reverse()`, and `autocomplete()` are the only three operations, results already carry coordinates, and `createSession()` throws `TierUnsupportedError`. Rate-limited to 10,000 requests/key/day plus a global monthly cap; hitting it throws `QuotaExceededError`. ### Self-hosting The free tier's engine is open source: [MapMetrics/atlas-osm-geocoder](https://github.com/MapMetrics/atlas-osm-geocoder), deployable on Cloudflare Workers with a prebuilt planet bundle. Point this package at your own instance with `MapAtlas.selfHosted()`: ```ts const mapatlas = MapAtlas.selfHosted({ baseUrl: 'https://my-worker.workers.dev' }); await mapatlas.geocoding.search('Nieuwezijds Voorburgwal 147'); await mapatlas.geocoding.autocomplete('Nieuwezijds'); await mapatlas.geocoding.reverse(52.37, 4.89); ``` A self-hosted instance takes no token and uses different paths (`/search`, `/reverse`, `/autocomplete` — no `/osm-` prefix, no `token` parameter); `selfHosted()` never sends a credential to it, even if you pass one by mistake. `createSession()` and session-based retrieval are unavailable on a self-hosted client, same as on `tier: 'osm'` — there's no session concept on this engine. ## Errors All errors extend `MapAtlasError`: - `TokenNotFoundError` — key was never provisioned. - `TokenInactiveError` — key exists but is deactivated. - `ScopeError` — key lacks the scope this operation requires. - `OriginRequiredError` — key is origin-restricted, request sent no `Origin` header. Native/server clients can never satisfy this — see [Choosing a key for native apps](./index.md#choosing-a-key-for-native-apps). - `OriginNotAllowedError` — request's `Origin` isn't on the key's allow-list. - `QuotaExceededError` — OSM tier cap exhausted; carries `selfHostUrl`. - `TierUnsupportedError` — thrown by `createSession()` on the `osm` tier and on `selfHosted()` clients, and by `autocomplete()` on the `v2` tier. - `NetworkError` — the request never completed, or the response wasn't parseable JSON. ```ts import { MapAtlasError, ScopeError } from '@mapmetrics/geocoder'; try { await mapatlas.geocoding.search('Nieuwezijds Voorburgwal 147', { country: 'nl' }); } catch (e) { if (e instanceof ScopeError) { // ... } else if (e instanceof MapAtlasError) { // catches every other documented failure mode } else { throw e; } } ``` Prefer `e.code` / `e.name` (plain strings) over `instanceof` if your app might load this package as both ESM and CJS in the same dependency tree — that produces two distinct class objects for the same error, and `instanceof` won't match across them. ::: tip Not every suggestion can be resolved The gateway returns some rows — injected locality entries, such as the city itself when you type "Amsterdam" — with no retrieve handle. They are still returned so you can show them in a list, but they cannot be turned into coordinates. `suggestion.isRetrievable` tells you which is which, and `ord` is `null` (never `NaN`) on those rows: ```ts const results = await session.suggest('Amsterdam', { country: 'nl' }); const usable = results.filter((s) => s.isRetrievable); ``` Calling `retrieve()` on a non-retrievable suggestion throws `NotRetrievableError`. `retrieveBatch()` rejects the whole call if any item is non-retrievable, rather than silently returning fewer places than you asked for. All four SDKs behave identically here. ::: ## Endpoint reference This SDK is a typed wrapper — for the full parameter list, response shape, and gateway-level gotchas each call is protecting you from, see: - [Autocomplete (v2)](../../geocoder/v2/autocomplete.md) - [Retrieve (v2)](../../geocoder/v2/retrieve.md) - [Forward Geocode (v2)](../../geocoder/v2/forwardgeocode.md) - [Reverse Geocode (v2)](../../geocoder/v2/reversegeocode.md) - [Sessions & Billing](./sessions.md) - [API Keys & Security](../../api-keys.md) --- # Kotlin SDK https://docs.mapatlas.xyz/overview/sdk/geocoding/kotlin # Kotlin SDK `mapatlas-geocoder` is a pure Kotlin/JVM client for the [v2 geocoding API](../../geocoder/v2/autocomplete.md) — autocomplete, place retrieval, and forward/reverse geocoding. Because it's plain Kotlin/JVM (not an Android library module), the exact same artifact runs on Android and on a server-side JVM. ::: warning Not published yet Not on Maven Central. Consume it as a Gradle composite build or a local Maven artifact today — see below. ::: ## Install **As a composite build** (`settings.gradle.kts` in your app): ```kotlin includeBuild("../Geocoder-SDK/Android") { dependencySubstitution { substitute(module("net.mapmetrics:mapatlas-geocoder")).using(project(":")) } } ``` ```kotlin // app/build.gradle.kts dependencies { implementation("net.mapmetrics:mapatlas-geocoder:1.0.0") } ``` **As a local Maven artifact:** ```bash cd Geocoder-SDK/Android gradle publishToMavenLocal ``` ```kotlin repositories { mavenLocal() } dependencies { implementation("net.mapmetrics:mapatlas-geocoder:1.0.0") } ``` **Why plain Kotlin/JVM, not an Android library module?** It uses the `kotlin("jvm")` Gradle plugin, not the Android Gradle Plugin — no Android SDK is required to build or unit test it. Android compatibility comes from targeting JVM 11 bytecode and relying only on `java.net.http`, which Android's core library desugaring supports back to `minSdk 21`. Enable desugaring in your app module: ```kotlin android { compileOptions { isCoreLibraryDesugaringEnabled = true } } dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2") } ``` ## Quickstart ```kotlin import net.mapmetrics.geocoder.MapAtlas import net.mapmetrics.geocoder.SearchOptions import net.mapmetrics.geocoder.GeocodeSearchResult val mapatlas = MapAtlas(token = "YOUR_API_KEY") // One session covers a whole search — every keystroke, then the pick. val session = mapatlas.geocoding.createSession() // As the user types. Debounced for you; suggestions carry no coordinates. val results = session.suggest("Nieuwezijds") // When they pick one, hand back the suggestion — not an id. val place = session.retrieve(results[0]) println("${place.latitude}, ${place.longitude}") // Resolving several at once is both fewer round trips and cheaper billing — // see Sessions & Billing. val places = session.retrieveBatch(results.take(3)) // One-shot lookups, when there's no user typing to debounce. val fwd = mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147", SearchOptions(country = "nl")) val rev = mapatlas.geocoding.reverse(latitude = 52.37, longitude = 4.89) check(fwd is GeocodeSearchResult.Pelias) // always true on the default v2 tier mapatlas.close() // releases the HTTP engine's threads and the coroutine scope backing Session debouncing ``` ## The reactive layer: `GeocodeSearchController` `GeocodeSearchController` exposes a single `StateFlow` — own it (and a `MapAtlas`) in a `ViewModel`, collect it in a Compose screen, and you have a working, correctly-billed, debounced search box. It debounces, discards stale out-of-order responses, and turns failures into typed state rather than thrown exceptions. ```kotlin class AddressSearchViewModel : ViewModel() { private val mapatlas = MapAtlas(token = "YOUR_API_KEY") val controller = GeocodeSearchController( client = mapatlas, scope = viewModelScope, country = "nl", minLength = 2, ) fun onQueryChanged(text: String) = controller.setQuery(text) suspend fun onSuggestionPicked(suggestion: Suggestion): RetrievedPlace = controller.select(suggestion) override fun onCleared() { controller.close() mapatlas.close() } } ``` ```kotlin @Composable fun AddressSearchScreen(viewModel: AddressSearchViewModel = viewModel()) { val state by viewModel.controller.state.collectAsState() val scope = rememberCoroutineScope() Column { TextField( value = state.query, onValueChange = viewModel::onQueryChanged, placeholder = { Text("Search an address…") }, ) if (state.isLoading) CircularProgressIndicator() state.error?.let { Text("${it::class.simpleName}: ${it.message}", color = Color.Red) } LazyColumn { items(state.suggestions) { suggestion -> Text( text = suggestion.placeName ?: suggestion.text.orEmpty(), modifier = Modifier.clickable { scope.launch { val place = viewModel.onSuggestionPicked(suggestion) // navigate / use place.latitude, place.longitude } }, ) } } state.selected?.let { place -> Text("Selected: ${place.latitude}, ${place.longitude}") } } } ``` `GeocodeSearchState` carries `query`, `suggestions`, `isLoading`, `error` (a typed `MapAtlasException?`), and `selected`. Out-of-order responses are discarded automatically: type "a" then quickly "ab", and a slow response for "a" arriving after "ab" is dropped rather than flashing stale results — the controller lets the older request finish (it doesn't race to cancel it) and simply ignores its result once superseded. ## The OSM tier `tier = MapAtlasTier.OSM` talks to the free, OpenStreetMap-only endpoints via the `osm-geocode` scope. No session/retrieve flow — `search()`, `reverse()`, and `autocomplete()` are the only three operations, returning `GeocodeSearchResult.Osm` / `OsmResponse` (a permissive wrapper around the raw JSON). Calling `createSession()` on this tier throws `MapAtlasException.TierUnsupported`. Rate-limited to 10,000 requests/key/day plus a global monthly cap; exhausting it throws `MapAtlasException.QuotaExceeded` with a `selfHostUrl`. ### Self-hosting The free tier's engine is open source: [MapMetrics/atlas-osm-geocoder](https://github.com/MapMetrics/atlas-osm-geocoder), deployable on Cloudflare Workers with a prebuilt planet bundle. Point this package at your own instance with `MapAtlas.selfHosted()`: ```kotlin val mapatlas = MapAtlas.selfHosted(baseUrl = "https://my-worker.workers.dev") mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147") mapatlas.geocoding.autocomplete("Nieuwezijds") mapatlas.geocoding.reverse(latitude = 52.37, longitude = 4.89) ``` A self-hosted instance takes no token and uses different paths (`/search`, `/reverse`, `/autocomplete`, no `token` parameter) — this package never sends a credential to it. `createSession()` (and therefore `Session.retrieve()`/`retrieveBatch()`) throw `TierUnsupported` on a self-hosted client, exactly as on the hosted `osm` tier — this engine has no session concept either. `QuotaExceeded` cannot occur self-hosted (no quota). ## Choosing a key for an Android app Origin restriction is a **browser-only** feature — it works by checking the `Origin` header a browser sends, and native apps don't send one. **This is the error an Android developer will hit** if they reuse a browser-restricted key: an origin-restricted key can never work from Android. Use an unrestricted key for native/server clients. See [API Keys & Security](../../api-keys.md) for the full model. ## Errors All errors extend the sealed `MapAtlasException`. Kotlin's `when` is exhaustive over a sealed class, so the compiler flags anything you miss: - `TokenNotFound` — key was never provisioned. - `TokenInactive` — key exists but is deactivated. - `ScopeNotAllowed` — key is valid but not scoped for this operation. - `OriginRequired` — key is origin-restricted, no `Origin` header sent. See [Choosing a key for an Android app](#choosing-a-key-for-an-android-app). - `OriginNotAllowed` — request's `Origin` isn't on the key's allow-list. - `QuotaExceeded` — OSM tier cap exhausted; carries `selfHostUrl`. - `TierUnsupported` — thrown by `createSession()` on the `osm` tier and on `MapAtlas.selfHosted()` clients, and by `autocomplete()` on the `v2` tier. - `NotRetrievable` — thrown by `retrieve()`/`retrieveBatch()` when a suggestion has no `ord` (see below). - `NetworkError` — the request never completed. - `DecodingError` — a response was received but couldn't be parsed. - `Unknown` — any other non-2xx response this package doesn't have a specific subclass for; still carries `status`/`code` where available. ```kotlin try { mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147", SearchOptions(country = "nl")) } catch (e: MapAtlasException.ScopeNotAllowed) { // ... } catch (e: MapAtlasException) { // catches every other documented failure mode } ``` Every exception carries `status: Int?` and `code: String?` alongside `message`. ### Suggestions without coordinates, or an `ord` `Suggestion` deliberately carries **no coordinates** — only `retrieve()`/ `retrieveBatch()` return them. Separately, `ord` is nullable: the gateway returns injected locality rows among ordinary results for some queries (two rows for `q=Amsterdam` at last check) that carry no `ord` at all. Check `suggestion.isRetrievable` before calling `retrieve()` on a row you're not sure about: ```kotlin val suggestions = session.suggest("Amsterdam") val retrievable = suggestions.filter { it.isRetrievable } val place = session.retrieve(retrievable.first()) ``` Calling `retrieve()` on a non-retrievable suggestion throws `MapAtlasException.NotRetrievable` with a message explaining why, rather than crashing on a null or silently 404ing. `retrieveBatch()` rejects the **whole call** if any item in the list is non-retrievable, rather than silently dropping the offending item(s). ## Not implemented yet This package covers geocoding only — routing, matrix, map matching, optimization, and isochrones are **not** implemented here. The TypeScript and Dart SDKs cover those endpoints; see [JavaScript](./javascript.md) or [Flutter](./flutter.md) if you need routing from a shared backend or a cross-platform Dart layer. ## Endpoint reference This SDK is a typed wrapper — for the full parameter list, response shape, and gateway-level gotchas each call is protecting you from, see: - [Autocomplete (v2)](../../geocoder/v2/autocomplete.md) - [Retrieve (v2)](../../geocoder/v2/retrieve.md) - [Forward Geocode (v2)](../../geocoder/v2/forwardgeocode.md) - [Reverse Geocode (v2)](../../geocoder/v2/reversegeocode.md) - [Sessions & Billing](./sessions.md) - [API Keys & Security](../../api-keys.md) --- # Sessions & Billing https://docs.mapatlas.xyz/overview/sdk/geocoding/sessions # Sessions & Billing All four geocoding SDKs are built around one billing model: the [v2 autocomplete API](../../geocoder/v2/autocomplete.md) bills by **session**, not by request. This page is the shared reference the per-language pages link back to — see [Sessions (v2 Autocomplete Billing)](../../geocoder/v2/sessions.md) for the underlying HTTP-level rules this section builds on. ## The rule A session starts on the first `suggest()` call on a fresh session object, and ends — closing the session — on either `retrieve()` or `retrieveBatch()`. The next `suggest()` call after that automatically opens a new session. You never see or manage the `session_token` yourself; every SDK mints, reuses, and rotates it for you. ## What a session costs Measured against the gateway (one token, counting session starts): | sequence | sessions billed | |---|---| | 3 `suggest()` calls on one session | **1** | | `suggest()` → `retrieve()` → `suggest()` | **2** | | `suggest()` → `retrieveBatch()` → `suggest()` | **2** | Two consequences follow directly from this table: **Resolving picks one at a time is expensive.** Three sequential `retrieve()` calls on three different suggestions cost **three sessions**, because each `retrieve()` both closes the session it belongs to and — if none is open — starts one. One `retrieveBatch()` of the same three suggestions costs **one session**. If you're resolving more than one result at a time — plotting a set of markers, say — always prefer the batch call. **Omitting a stable session token costs roughly 5x.** If nothing carries a consistent token across a search, the gateway mints a fresh session for every single keystroke instead of one session covering the whole search. For a typical multi-character search this multiplies cost by roughly 5x. This is exactly the mistake the SDKs exist to prevent — a hand-rolled `fetch`/`URLSession` call that constructs a new session (or omits `session_token` entirely) per keystroke will hit this without any error or warning; the gateway returns HTTP 200 every time. ## How each SDK prevents it Every SDK ties one session object to the lifetime of one search-box interaction: - **JavaScript/TypeScript** — `client.geocoding.createSession()` returns a `Session` that owns the token; `select()`/`retrieve()`/`retrieveBatch()` close it and the next `suggest()` reopens one automatically. The `useAutocomplete` hook owns one `Session` per component instance. - **Dart/Flutter** — `mapatlas.geocoding.createSession()` returns a `GeocodeSession` with the same lifecycle. `GeocodeSearchController` creates exactly one `GeocodeSession` in its constructor and reuses it for the controller's whole lifetime. - **Swift** — `mapatlas.geocoding.createSession()` returns a `Session`. `GeocodeSearchController` creates one for its lifetime as an `@Observable` controller. - **Kotlin** — `mapatlas.geocoding.createSession()` returns a `Session`. `GeocodeSearchController` owns one internally and exposes it only through `StateFlow`. In all four, constructing a **new** session object per keystroke — e.g. calling `createSession()` inside a text-change handler instead of once, up front — reintroduces the roughly-5x cost the SDK is meant to prevent. The reactive controllers exist specifically so you never have to think about this: bind the controller to the lifetime of the search field, not the keystroke. ## Retrievability Not every suggestion can be resolved. The gateway injects locality rows (e.g. two of the fifteen results for `q=Amsterdam`) alongside ordinary results; those rows are shown in the suggestion list but carry no `ord`, so they can't be retrieved. Every SDK exposes an `isRetrievable` check on the suggestion — use it to disable or filter those rows before the user can tap one. See the error-handling section of each language page for what happens if you call `retrieve()` on a non-retrievable suggestion anyway. ## See Also - [Autocomplete (v2)](../../geocoder/v2/autocomplete.md) - [Retrieve (v2)](../../geocoder/v2/retrieve.md) - [Sessions (v2 HTTP billing model)](../../geocoder/v2/sessions.md) - [API Keys & Security](../../api-keys.md) --- # Swift SDK https://docs.mapatlas.xyz/overview/sdk/geocoding/swift # Swift SDK `MapAtlasGeocoder` is a Swift client for the [v2 geocoding API](../../geocoder/v2/autocomplete.md) — autocomplete, place retrieval, and forward/reverse geocoding — for iOS and macOS. Foundation only, no third-party dependencies, `async`/`await` throughout. ::: warning Not published yet Not on the Swift Package Index yet. Add it as a git-based Swift Package today — see below; the coordinates don't change once it's indexed. ::: ## Install **Platforms:** macOS 12+, iOS 15+. ### Xcode File → Add Package Dependencies… and enter: ``` https://github.com/MapMetrics/geocoder-sdk ``` Select the `Swift` package directory and add the **MapAtlasGeocoder** product to your target. ### Package.swift ```swift dependencies: [ .package(url: "https://github.com/MapMetrics/geocoder-sdk", branch: "main"), ], targets: [ .target( name: "YourTarget", dependencies: [ .product(name: "MapAtlasGeocoder", package: "geocoder-sdk"), ] ), ] ``` ## Quickstart ```swift import MapAtlasGeocoder let mapatlas = MapAtlas(token: "YOUR_API_KEY") // One session covers a whole search — every keystroke, then the pick. let session = try mapatlas.geocoding.createSession() // As the user types. Debounced for you; suggestions carry no coordinates. let results = try await session.suggest("Nieuwezijds") // When they pick one, hand back the suggestion — not an id. let place = try await session.retrieve(results[0]) print(place.latitude, place.longitude) // Resolving several at once is both fewer round trips and cheaper billing — // see Sessions & Billing. let places = try await session.retrieveBatch(Array(results.prefix(3))) // One-shot lookups, when there's no user typing to debounce. Both return GeoJSON. let forward = try await mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147", country: "nl") let reverse = try await mapatlas.geocoding.reverse(lat: 52.37, lon: 4.89) ``` ## The reactive layer: `GeocodeSearchController` `GeocodeSearchController` is a headless, `@Observable`, `@MainActor` controller — bind your own markup to it. Requires macOS 14 / iOS 17 (the rest of the package works down to macOS 12 / iOS 15 — `@Observable` alone needs the newer OS). ```swift import SwiftUI import MapAtlasGeocoder struct AddressField: View { @State private var controller: GeocodeSearchController var onSelect: (RetrievedPlace) -> Void init(client: MapAtlas, onSelect: @escaping (RetrievedPlace) -> Void) { _controller = State(initialValue: try! GeocodeSearchController(client: client, country: "nl")) self.onSelect = onSelect } var body: some View { VStack(alignment: .leading, spacing: 8) { TextField("Search an address…", text: $controller.query) .textFieldStyle(.roundedBorder) if controller.isLoading { ProgressView() } if let error = controller.error { Text(error.message).foregroundStyle(.red).font(.caption) } List(controller.suggestions, id: \.text) { suggestion in Button(suggestion.placeName ?? suggestion.text ?? "") { Task { if let place = await controller.select(suggestion) { onSelect(place) } } } // Suggestions can be non-retrievable (e.g. a bare "Amsterdam" // locality row) — still shown, but selecting one surfaces // `.notRetrievable` into `controller.error` instead of a crash. .disabled(!suggestion.isRetrievable) } } } } ``` **Guarantees:** one `Session` is created for the controller's lifetime and reused across every keystroke; `select(_:)` retrieves and closes it, and the next `query` change reopens it automatically. `minLength` (default `2`) is enforced locally — no request, no session activity, below it. Out-of-order responses are discarded: each search is tagged with a sequence number, and a slow response for an earlier keystroke arriving after a newer one is never applied. Errors land in `controller.error`, never thrown into a view. ## The OSM tier `tier: .osm` talks to the free, OpenStreetMap-only endpoints (`/osm-geocode/`, `/osm-reverse/`, `/osm-autocomplete/`) via the `osm-geocode` scope. No session/retrieve flow — `search()`, `reverse()`, and `autocomplete(_:)` are the only three operations. Calling `createSession()` on this tier throws `.tierUnsupported`. Rate-limited to 10,000 requests/key/day plus a global monthly cap; exhausting it throws `.quotaExceeded` with a `selfHostURL`. ```swift let mapatlas = MapAtlas(token: "YOUR_KEY", tier: .osm) let result = try await mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147") if case .osm(let raw) = result { // raw: OsmResponse (= JSONValue) — narrow it yourself at the call site. } ``` `autocomplete(_:)` on this tier is the opposite of v2 autocomplete: no session, no `retrieve()` step, results already carry coordinates. It's only available with `tier: .osm` — on the default `.v2` tier it throws `.tierUnsupported` and points you at `createSession()` + `suggest(_:)` instead. ### Self-hosting The free tier's engine is open source: [MapMetrics/atlas-osm-geocoder](https://github.com/MapMetrics/atlas-osm-geocoder), deployable on Cloudflare Workers with a prebuilt planet bundle. Point this package at your own instance with `MapAtlas.selfHosted(baseURL:)`: ```swift let mapatlas = MapAtlas.selfHosted(baseURL: URL(string: "https://my-worker.workers.dev")!) try await mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147") try await mapatlas.geocoding.autocomplete("Nieuwezijds") try await mapatlas.geocoding.reverse(lat: 52.37, lon: 4.89) ``` A self-hosted instance takes no token and uses different paths (`/search`, `/reverse`, `/autocomplete`, no `token` parameter) — there is no `token`/`getToken` option on this constructor at all, since a self-hosted instance has no auth and this package never sends one. `createSession()` (and session-based retrieval) throw `.tierUnsupported` here too — this engine has no session concept at all. ## Choosing a key for a Swift app Origin restriction is a **browser-only** feature — it's enforced by checking the `Origin` header a browser sends, and native apps never send one. **A native app can never satisfy an origin-restricted key** — this is the error you'll hit if you reuse a browser-restricted key in an iOS app. Use an unrestricted key for iOS/macOS instead. See [API Keys & Security](../../api-keys.md) for the full model. ## Errors `MapAtlasError` is a Swift `enum` with one case per failure mode. Switch on it, or read `.message` / `.status` / `.code` / `.name`: - `.tokenNotFound` — key was never provisioned. - `.tokenInactive` — key exists but is deactivated. - `.scopeNotAllowed` — key is valid but not scoped for this operation. - `.originRequired` — key is origin-restricted, no `Origin` header sent. See [Choosing a key for a Swift app](#choosing-a-key-for-a-swift-app). - `.originNotAllowed` — request's `Origin` isn't on the key's allow-list. - `.quotaExceeded` — OSM tier cap exhausted; carries `selfHostURL`. - `.tierUnsupported` — thrown by `createSession()` on the `.osm` tier and on `MapAtlas.selfHosted(baseURL:)` clients, and by `autocomplete(_:)` on the `.v2` tier. - `.notRetrievable` — thrown by `retrieve(_:)`/`retrieveBatch(_:)` when a `Suggestion` has no `ord` (`isRetrievable == false`). - `.network` — the request never completed, or the response wasn't parseable JSON. - `.decoding` — the response was valid JSON but didn't match the expected shape. - `.unknown` — any other non-2xx response, not covered above. ```swift import MapAtlasGeocoder do { _ = try await mapatlas.geocoding.search("Nieuwezijds Voorburgwal 147", country: "nl") } catch let error as MapAtlasError { switch error { case .originRequired: // this key can't be used from a native app — swap it for an unrestricted one break case .quotaExceeded(_, _, _, let selfHostURL): print("quota exceeded, self-host at:", selfHostURL as Any) default: print(error.name, error.message) } } ``` ## Not implemented yet This package covers geocoding only — routing, matrix, map matching, optimization, and isochrones are **not** implemented here. The TypeScript and Dart SDKs cover those endpoints; see [JavaScript](./javascript.md) or [Flutter](./flutter.md) if you need routing from a shared backend or a cross-platform Dart layer. ## Endpoint reference This SDK is a typed wrapper — for the full parameter list, response shape, and gateway-level gotchas each call is protecting you from, see: - [Autocomplete (v2)](../../geocoder/v2/autocomplete.md) - [Retrieve (v2)](../../geocoder/v2/retrieve.md) - [Forward Geocode (v2)](../../geocoder/v2/forwardgeocode.md) - [Reverse Geocode (v2)](../../geocoder/v2/reversegeocode.md) - [Sessions & Billing](./sessions.md) - [API Keys & Security](../../api-keys.md) --- # SDKs & Softwares https://docs.mapatlas.xyz/overview/sdk/ # SDKs & Softwares ## Integrating Maps in your project MapMetrics exposes Vector Tile APIs to work with maps. To display maps, you need to use a Maps SDK. There are already great OpenSource Maps SDKs, and we'd rather spend our nights optimizing rendering efficiency to give you the fastest maps on the market than putting our name on a brand new Maps SDK which would pretty much do the same as the others. ### Vector Tiles Vector tiles were created after the raster tiles. These are also squares, which together form a map. This time, these squares only contain the data, so you have to associate a style to it to finally have a map. This allows for more personalization and better interactions with the map. We would recommend using MapMetrics GL for vector tiles. # Attribution Your map must display the following links: © MapMetrics and © OSM contributors. This is a part or our General Terms and Conditions (5.6 Attribution Requirements). This is our attribution template: ```html © MapMetrics | © OSM contributors ``` --- # Action Journal https://docs.mapatlas.xyz/overview/sdk/ios-native/ActionJournalExample # Action Journal Learn about the ``MLNMapView`` methods for logging and viewing map actions. The Action Journal provides functionality for persistent logging of top level map events. Its primary use case is to assist in debugging problematic sessions and crashes by offering additional insight into the actions performed by the map at the time of failure. Data is stored in human readable format, which is useful for analyzing individual cases, but can also be easily translated and aggregated into a database, allowing for efficient analysis of multiple cases and helping to identify recurring patterns (Google BigQuery, AWS Glue + S3 + Athena, etc). We are always interested in improving observability, so if you have a special use case, feel free to [open an issue or pull request]({"contact/admin/forURLs"}) to extend the types of observability methods. ## Logging implementation details The logging is implemented using rolling files with a size based policy: - A new file is created when the current log size exceeds ``MLNActionJournalOptions/logFileSize``. - When the maximum number of files exceeds ``MLNActionJournalOptions/logFileCount``: - The oldest one is deleted. - The remaining files are renamed sequentially to maintain the naming convention `action_journal.0.log` through `action_journal.{logFileCount - 1}.log`. - Each file contains one event per line. - All files are stored in an umbrella "action_journal" directory at ``MLNActionJournalOptions/path``. See also: ``MLNSettings``, ``MLNActionJournalOptions``. ## Event format Events are stored as JSON objects with the following format: | Field | Type | Required | Description | | :---- | :--: | :------: | :---------- | | name | string | true | event name | | time | string | true | event time ([ISO 8601]({"contact/admin/forURLs"}) with milliseconds) | | styleName | string | false | currently loaded style name | | styleURL | string | false | currently loaded style URL | | clientName | string | false | | | clientVersion | string | false | | | event | object | false | event specific data - consists of encoded values of the parameters passed to their ``MLNMapViewDelegate`` counterparts ``` { "name" : "onTileAction", "time" : "2025-04-17T13:13:13.974Z", "styleName" : "Streets", "styleURL" : "maptiler://maps/streets", "clientName" : "App", "clientVersion" : "1.0", "event" : { "action" : "RequestedFromNetwork", "tileX" : 0, "tileY" : 0, "tileZ" : 0, "overscaledZ" : 0, "sourceID" : "openmaptiles" } } ``` ## Usage Enabling the action journal. ```swift let options = MLNMapOptions() options.actionJournalOptions.enabled = true options.styleURL = AMERICANA_STYLE mapView = MLNMapView(frame: view.bounds, options: options) ``` ```swift @objc func printActionJournal() { print("ActionJournalLog files: \(mapView.getActionJournalLogFiles())") print("ActionJournalLog : \(mapView.getActionJournalLog())") // print only the newest events on each call mapView.clearActionJournalLog() } ``` ## Alternative The implementation is kept close to the core events to minimize additional locking and avoid platform-specific conversions and calls. As a result customization options and extensibility is limited. For greater flexibility, consider using the ``MLNMapViewDelegate`` interface. It provides hooks for most Action Journal events and allows for more customizable querying and storage of map data. However, this comes at the cost of added complexity. See [Observe Low-Level Events](./ObserverExample.md) to learn about the map events that you can listen for, which mirror the events available in the action journal. --- # Animated Line https://docs.mapatlas.xyz/overview/sdk/ios-native/AnimatedLineExample # Animated Line Add an animated line to a map > This example uses UIKit. Demonstrates using `MLNPolyline.polylineWithCoordinates:count:` to update an `MLNPolyline`. ```swift class AnimatedLineExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! var timer: Timer? var polylineSource: MLNShapeSource? var currentIndex = 1 var allCoordinates: [CLLocationCoordinate2D]! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.setCenter( CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736), zoomLevel: 11, animated: false ) view.addSubview(mapView) mapView.delegate = self allCoordinates = coordinates } // Wait until the map is loaded before adding to the map. func mapViewDidFinishLoadingMap(_ mapView: MLNMapView) { addPolyline(to: mapView.style!) animatePolyline() } func addPolyline(to style: MLNStyle) { // Add an empty MLNShapeSource, we'll keep a reference to this and add points to this later. let source = MLNShapeSource(identifier: "polyline", shape: nil, options: nil) style.addSource(source) polylineSource = source // Add a layer to style our polyline. let layer = MLNLineStyleLayer(identifier: "polyline", source: source) layer.lineJoin = NSExpression(forConstantValue: "round") layer.lineCap = NSExpression(forConstantValue: "round") layer.lineColor = NSExpression(forConstantValue: UIColor.blue) // The line width should gradually increase based on the zoom level. layer.lineWidth = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [14: 5, 18: 20])) style.addLayer(layer) } func animatePolyline() { currentIndex = 1 // Start a timer that will simulate adding points to our polyline. This could also represent coordinates being added to our polyline from another source, such as a CLLocationManagerDelegate. timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(tick), userInfo: nil, repeats: true) } @objc func tick() { if currentIndex > allCoordinates.count { timer?.invalidate() timer = nil return } // Create a subarray of locations up to the current index. let coordinates = Array(allCoordinates[0 ..< currentIndex]) // Update our MLNShapeSource with the current locations. updatePolylineWithCoordinates(coordinates: coordinates) currentIndex += 1 } func updatePolylineWithCoordinates(coordinates: [CLLocationCoordinate2D]) { var mutableCoordinates = coordinates let polyline = MLNPolylineFeature(coordinates: &mutableCoordinates, count: UInt(mutableCoordinates.count)) // Updating the MLNShapeSource's shape will have the map redraw our polyline with the current coordinates. polylineSource?.shape = polyline } let coordinates = [ (-122.63748, 45.52214), (-122.64855, 45.52218), (-122.6545, 45.52219), (-122.65497, 45.52196), (-122.65631, 45.52104), (-122.6578, 45.51935), (-122.65867, 45.51848), (-122.65872, 45.51293), (-122.66576, 45.51295), (-122.66745, 45.51252), (-122.66813, 45.51244), (-122.67359, 45.51385), (-122.67415, 45.51406), (-122.67481, 45.51484), (-122.676, 45.51532), (-122.68106, 45.51668), (-122.68503, 45.50934), (-122.68546, 45.50858), (-122.6852, 45.50783), (-122.68424, 45.50714), (-122.68433, 45.50585), (-122.68429, 45.50521), (-122.68456, 45.50445), (-122.68538, 45.50371), (-122.68653, 45.50311), (-122.68731, 45.50292), (-122.68742, 45.50253), (-122.6867, 45.50239), (-122.68545, 45.5026), (-122.68407, 45.50294), (-122.68357, 45.50271), (-122.68236, 45.50055), (-122.68233, 45.49994), (-122.68267, 45.49955), (-122.68257, 45.49919), (-122.68376, 45.49842), (-122.68428, 45.49821), (-122.68573, 45.49798), (-122.68923, 45.49805), (-122.68926, 45.49857), (-122.68814, 45.49911), (-122.68865, 45.49921), (-122.6897, 45.49905), (-122.69346, 45.49917), (-122.69404, 45.49902), (-122.69438, 45.49796), (-122.69504, 45.49697), (-122.69624, 45.49661), (-122.69781, 45.4955), (-122.69803, 45.49517), (-122.69711, 45.49508), (-122.69688, 45.4948), (-122.69744, 45.49368), (-122.69702, 45.49311), (-122.69665, 45.49294), (-122.69788, 45.49212), (-122.69771, 45.49264), (-122.69835, 45.49332), (-122.7007, 45.49334), (-122.70167, 45.49358), (-122.70215, 45.49401), (-122.70229, 45.49439), (-122.70185, 45.49566), (-122.70215, 45.49635), (-122.70346, 45.49674), (-122.70517, 45.49758), (-122.70614, 45.49736), (-122.70663, 45.49736), (-122.70807, 45.49767), (-122.70807, 45.49798), (-122.70717, 45.49798), (-122.70713, 45.4984), (-122.70774, 45.49893), ].map { CLLocationCoordinate2D(latitude: $0.1, longitude: $0.0) } } ``` --- # Custom Annotation View https://docs.mapatlas.xyz/overview/sdk/ios-native/AnnotationViewExample # Custom Annotation View Add a custom annotation view This examples shows how you can implement and use a custom ``MLNAnnotationView``. You need to implement ``MLNMapViewDelegate/mapView:viewForAnnotation:`` of ``MLNMapViewDelegate`` which will be called when you add an ``MLNAnnotation`` to the example. In this case, three ``MLNPointAnnotation``s are added to the map. When one is selected selected ``MLNAnnotationView/setSelected:animated:`` will be called. ```swift class AnnotationViewExample: UIViewController, MLNMapViewDelegate { override func viewDidLoad() { super.viewDidLoad() let mapView = MLNMapView(frame: view.bounds) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.attributionButton.isHidden = true mapView.tintColor = .lightGray mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 0, longitude: 66) mapView.zoomLevel = 2 mapView.delegate = self view.addSubview(mapView) // Specify coordinates for our annotations. let coordinates = [ CLLocationCoordinate2D(latitude: 0, longitude: 33), CLLocationCoordinate2D(latitude: 0, longitude: 66), CLLocationCoordinate2D(latitude: 0, longitude: 99), ] // Fill an array with point annotations and add it to the map. var pointAnnotations = [MLNPointAnnotation]() for coordinate in coordinates { let point = MLNPointAnnotation() point.coordinate = coordinate point.title = "\(coordinate.latitude), \(coordinate.longitude)" pointAnnotations.append(point) } mapView.addAnnotations(pointAnnotations) } // MARK: - MLNMapViewDelegate methods // This delegate method is where you tell the map to load a view for a specific annotation. To load a static MLNAnnotationImage, you would use `-mapView:imageForAnnotation:`. func mapView(_ mapView: MLNMapView, viewFor annotation: MLNAnnotation) -> MLNAnnotationView? { // This example is only concerned with point annotations. guard annotation is MLNPointAnnotation else { return nil } // Use the point annotation’s longitude value (as a string) as the reuse identifier for its view. let reuseIdentifier = "\(annotation.coordinate.longitude)" // For better performance, always try to reuse existing annotations. var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) // If there’s no reusable annotation view available, initialize a new one. if annotationView == nil { annotationView = CustomAnnotationView(reuseIdentifier: reuseIdentifier) annotationView!.bounds = CGRect(x: 0, y: 0, width: 40, height: 40) // Set the annotation view’s background color to a value determined by its longitude. let hue = CGFloat(annotation.coordinate.longitude) / 100 annotationView!.backgroundColor = UIColor(hue: hue, saturation: 0.5, brightness: 1, alpha: 1) } return annotationView } func mapView(_: MLNMapView, annotationCanShowCallout _: MLNAnnotation) -> Bool { true } } // // MLNAnnotationView subclass class CustomAnnotationView: MLNAnnotationView { override func layoutSubviews() { super.layoutSubviews() // Use CALayer’s corner radius to turn this view into a circle. layer.cornerRadius = bounds.width / 2 layer.borderWidth = 2 layer.borderColor = UIColor.white.cgColor } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Animate the border width in/out, creating an iris effect. let animation = CABasicAnimation(keyPath: "borderWidth") animation.duration = 0.1 layer.borderWidth = selected ? bounds.width / 4 : 2 layer.add(animation, forKey: "borderWidth") } } ``` ![](/overview/sdk/ios-native/img/AnnotationViewExample.png) --- # Blocking Gestures https://docs.mapatlas.xyz/overview/sdk/ios-native/BlockingGesturesExample # Blocking Gestures Constrain the map to a certain area. > Note: This example uses SwiftUI. To constrain the map to a certain area, you need to implement the ``MLNMapViewDelegate/mapView:shouldChangeFromCamera:toCamera:`` method of ``MLNMapViewDelegate``. By returning a boolean you can either allow or disallow a camera change. ```swift // Denver, Colorado private let center = CLLocationCoordinate2D(latitude: 39.748947, longitude: -104.995882) // Colorado’s bounds private let colorado = MLNCoordinateBounds( sw: CLLocationCoordinate2D(latitude: 36.986207, longitude: -109.049896), ne: CLLocationCoordinate2D(latitude: 40.989329, longitude: -102.062592) ) struct BlockingGesturesExample: UIViewRepresentable { class Coordinator: NSObject, MLNMapViewDelegate { func mapView(_ mapView: MLNMapView, shouldChangeFrom _: MLNMapCamera, to newCamera: MLNMapCamera) -> Bool { // Get the current camera to restore it after. let currentCamera = mapView.camera // From the new camera obtain the center to test if it’s inside the boundaries. let newCameraCenter = newCamera.centerCoordinate // Set the map’s visible bounds to newCamera. mapView.camera = newCamera let newVisibleCoordinates = mapView.visibleCoordinateBounds // Revert the camera. mapView.camera = currentCamera // Test if the newCameraCenter and newVisibleCoordinates are inside self.colorado. let inside = MLNCoordinateInCoordinateBounds(newCameraCenter, colorado) let intersects = MLNCoordinateInCoordinateBounds(newVisibleCoordinates.ne, colorado) && MLNCoordinateInCoordinateBounds(newVisibleCoordinates.sw, colorado) return inside && intersects } } func makeUIView(context: Context) -> MLNMapView { let mapView = MLNMapView(frame: .zero, styleURL: VERSATILES_COLORFUL_STYLE) mapView.setCenter(center, zoomLevel: 10, direction: 0, animated: false) mapView.delegate = context.coordinator return mapView } func updateUIView(_: MLNMapView, context _: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } } ``` The style used in this example can be found here: . --- # Fill Extrustion Layer https://docs.mapatlas.xyz/overview/sdk/ios-native/BuildingLightExample # Fill Extrustion Layer Add a fill extrustion layer and adjust the light dynamically with a slider. > Note: This example uses UIKit. This examples adds a ``MLNFillExtrusionStyleLayer``. The to be rendered height of the buildings is read from the vector data. The global ``MLNStyle/light`` property is adjusted as the user changes a slider, which affects the fill extrustion layer. ```swift class BuildingLightExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! var light: MLNLight! var slider: UISlider! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.delegate = self // Center the map on the Flatiron Building in New York, NY. mapView.camera = MLNMapCamera(lookingAtCenter: CLLocationCoordinate2D(latitude: 40.7411, longitude: -73.9897), altitude: 1200, pitch: 45, heading: 0) view.addSubview(mapView) addSlider() } // Add a slider to the map view. This will be used to adjust the map's light object. func addSlider() { slider = UISlider() slider.translatesAutoresizingMaskIntoConstraints = false slider.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin] slider.minimumValue = -180 slider.maximumValue = 180 slider.value = 0 slider.isContinuous = true slider.addTarget(self, action: #selector(shiftLight), for: .valueChanged) view.addSubview(slider) NSLayoutConstraint.activate([ slider.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0), slider.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40.0), slider.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -75.0), ]) } func mapView(_: MLNMapView, didFinishLoading style: MLNStyle) { // Add a MLNFillExtrusionStyleLayer. addFillExtrusionLayer(style: style) // Create an MLNLight object. light = MLNLight() // Create an MLNSphericalPosition and set the radial, azimuthal, and polar values. // Radial : Distance from the center of the base of an object to its light. Takes a CGFloat. // Azimuthal : Position of the light relative to its anchor. Takes a CLLocationDirection. // Polar : The height of the light. Takes a CLLocationDirection. let position = MLNSphericalPositionMake(5, 180, 80) light.position = NSExpression(forConstantValue: NSValue(mlnSphericalPosition: position)) // Set the light anchor to the map and add the light object to the map view's style. The light anchor can be the viewport (or rotates with the viewport) or the map (rotates with the map). To make the viewport the anchor, replace `map` with `viewport`. light.anchor = NSExpression(forConstantValue: "map") style.light = light } @objc func shiftLight() { // Use the slider's value to change the light's polar value. let position = MLNSphericalPositionMake(5, 180, CLLocationDirection(slider.value)) light.position = NSExpression(forConstantValue: NSValue(mlnSphericalPosition: position)) mapView.style?.light = light } func addFillExtrusionLayer(style: MLNStyle) { // Access the OpenMapTiles source and use it to create a ``MLNFillExtrusionStyleLayer``. The source identifier is `openmaptiles`. Use the `sources` property on a style to verify source identifiers. guard let source = style.source(withIdentifier: "openmaptiles") else { print("Could not find source openmaptiles") return } let layer = MLNFillExtrusionStyleLayer(identifier: "extrusion-layer", source: source) layer.sourceLayerIdentifier = "building" layer.fillExtrusionBase = NSExpression(forKeyPath: "render_min_height") layer.fillExtrusionHeight = NSExpression(forKeyPath: "render_height") layer.fillExtrusionOpacity = NSExpression(forConstantValue: 0.8) layer.fillExtrusionColor = NSExpression(forConstantValue: UIColor.white) // Access the map's layer with the identifier "poi" and insert the fill extrusion layer below it. let symbolLayer = style.layer(withIdentifier: "poi")! style.insertLayer(layer, below: symbolLayer) } } ``` ![](/overview/sdk/ios-native/img/BuildingLightExample.png) --- # Custom Style Layers (Metal API) https://docs.mapatlas.xyz/overview/sdk/ios-native/CustomStyleLayerExample # Custom Style Layers (Metal API) Creating a Custom Style Layer with Metal Custom style layers allow you to draw directly with Metal, enabling you to render specialized shapes, custom geometry, or apply advanced visual effects that go beyond what is possible with standard style layers. Below you can find an example of how to create a custom style layer with ``MLNCustomStyleLayer``. In this implementation, a SwiftUI view wraps an ``MLNMapView`` and appends a subclassed custom style layer once the map loads. The layer's ``MLNCustomStyleLayer/didMoveToMapView:`` method handles initialization, including compiling Metal shaders and creating a [`MTLRenderPipelineState`]({"contact/admin/forURLs"}) for subsequent draw operations. The ``MLNCustomStyleLayer/willMoveFromMapView:`` method provides a place to release or invalidate resources when the layer is removed from the map, while the ``MLNCustomStyleLayer/drawInMapView:withContext:`` method encodes the drawing commands using a [`MTLRenderCommandEncoder`]({"contact/admin/forURLs"}) and the map's projection matrix. By projecting latitude/longitude coordinates into a normalized 0–1 space and then transforming them into tile coordinates, the layer ensures that rendered geometry aligns correctly with the base map. ```swift struct CustomStyleLayerExample: UIViewRepresentable { func makeCoordinator() -> CustomStyleLayerExample.Coordinator { Coordinator(self) } final class Coordinator: NSObject, MLNMapViewDelegate { var control: CustomStyleLayerExample init(_ control: CustomStyleLayerExample) { self.control = control } func mapViewDidFinishLoadingMap(_ mapView: MLNMapView) { let mapOverlay = CustomStyleLayer(identifier: "test-overlay") let style = mapView.style! style.layers.append(mapOverlay) } } func makeUIView(context: Context) -> MLNMapView { let mapView = MLNMapView() mapView.delegate = context.coordinator return mapView } func updateUIView(_: MLNMapView, context _: Context) {} } class CustomStyleLayer: MLNCustomStyleLayer { private var pipelineState: MTLRenderPipelineState? private var depthStencilStateWithoutStencil: MTLDepthStencilState? override func didMove(to mapView: MLNMapView) { #if MLN_RENDER_BACKEND_METAL let resource = mapView.backendResource() let shaderSource = """ #include using namespace metal; typedef struct { vector_float2 position; vector_float4 color; } Vertex; struct RasterizerData { float4 position [[position]]; float4 color; }; struct Uniforms { float4x4 matrix; }; vertex RasterizerData vertexShader(uint vertexID [[vertex_id]], constant Vertex *vertices [[buffer(0)]], constant Uniforms &uniforms [[buffer(1)]]) { RasterizerData out; const float4 position = uniforms.matrix * float4(float2(vertices[vertexID].position.xy), 1, 1); out.position = position; out.color = vertices[vertexID].color; return out; } fragment float4 fragmentShader(RasterizerData in [[stage_in]]) { return in.color; } """ var error: NSError? let device = resource.device let library = try? device?.makeLibrary(source: shaderSource, options: nil) assert(library != nil, "Error compiling shaders: \(String(describing: error))") let vertexFunction = library?.makeFunction(name: "vertexShader") let fragmentFunction = library?.makeFunction(name: "fragmentShader") // Configure a pipeline descriptor that is used to create a pipeline state. let pipelineStateDescriptor = MTLRenderPipelineDescriptor() pipelineStateDescriptor.label = "Simple Pipeline" pipelineStateDescriptor.vertexFunction = vertexFunction pipelineStateDescriptor.fragmentFunction = fragmentFunction pipelineStateDescriptor.colorAttachments[0].pixelFormat = resource.mtkView.colorPixelFormat pipelineStateDescriptor.depthAttachmentPixelFormat = .depth32Float_stencil8 pipelineStateDescriptor.stencilAttachmentPixelFormat = .depth32Float_stencil8 do { pipelineState = try device?.makeRenderPipelineState(descriptor: pipelineStateDescriptor) } catch { assertionFailure("Failed to create pipeline state: \(error)") } // Notice that we don't configure the stencilTest property, leaving stencil testing disabled let depthStencilDescriptor = MTLDepthStencilDescriptor() depthStencilDescriptor.depthCompareFunction = .always // Or another value as needed depthStencilDescriptor.isDepthWriteEnabled = false depthStencilStateWithoutStencil = device!.makeDepthStencilState(descriptor: depthStencilDescriptor) #endif } override func willMove(from _: MLNMapView) {} override func draw(in _: MLNMapView, with context: MLNStyleLayerDrawingContext) { #if MLN_RENDER_BACKEND_METAL guard let renderEncoder else { return } // Project to 0..1. let p1 = project(CLLocationCoordinate2D(latitude: 25.0, longitude: 12.5)) let p2 = project(CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)) let p3 = project(CLLocationCoordinate2D(latitude: 0.0, longitude: 25.0)) // Multiply by the world size so it becomes the tile coordinate system. let worldSize = 512.0 * pow(2.0, context.zoomLevel) let p1Tile = CGPoint(x: p1.x * worldSize, y: p1.y * worldSize) let p2Tile = CGPoint(x: p2.x * worldSize, y: p2.y * worldSize) let p3Tile = CGPoint(x: p3.x * worldSize, y: p3.y * worldSize) // Then build a triangle from tile coordinates struct Vertex { var position: vector_float2; var color: vector_float4 } let triangleVertices: [Vertex] = [ Vertex(position: vector_float2(Float(p1Tile.x), Float(p1Tile.y)), color: vector_float4(1, 0, 0, 1)), Vertex(position: vector_float2(Float(p2Tile.x), Float(p2Tile.y)), color: vector_float4(0, 1, 0, 1)), Vertex(position: vector_float2(Float(p3Tile.x), Float(p3Tile.y)), color: vector_float4(0, 0, 1, 1)), ] // Use the camera's full projection matrix *unchanged*. var matrix = convertMatrix(context.projectionMatrix) // Encode renderEncoder.setRenderPipelineState(pipelineState!) renderEncoder.setDepthStencilState(depthStencilStateWithoutStencil) renderEncoder.setVertexBytes(triangleVertices, length: MemoryLayout.size * triangleVertices.count, index: 0) renderEncoder.setVertexBytes(&matrix, length: MemoryLayout.size, index: 1) // Draw the triangle. renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) #endif } func project(_ coordinate: CLLocationCoordinate2D) -> CGPoint { // We project the coordinates into the space 0 to 1 and then scale these when drawing based on the current zoom level let worldSize = 1.0 let x = (180.0 + coordinate.longitude) / 360.0 * worldSize let yi = log(tan((45.0 + coordinate.latitude / 2.0) * Double.pi / 180.0)) let y = (180.0 - yi * (180.0 / Double.pi)) / 360.0 * worldSize return CGPoint(x: x, y: y) } struct MLNMatrix4f { var m00, m01, m02, m03: Float var m10, m11, m12, m13: Float var m20, m21, m22, m23: Float var m30, m31, m32, m33: Float } func convertMatrix(_ mat: MLNMatrix4) -> MLNMatrix4f { MLNMatrix4f( m00: Float(mat.m00), m01: Float(mat.m01), m02: Float(mat.m02), m03: Float(mat.m03), m10: Float(mat.m10), m11: Float(mat.m11), m12: Float(mat.m12), m13: Float(mat.m13), m20: Float(mat.m20), m21: Float(mat.m21), m22: Float(mat.m22), m23: Float(mat.m23), m30: Float(mat.m30), m31: Float(mat.m31), m32: Float(mat.m32), m33: Float(mat.m33) ) } } ``` ![](/overview/sdk/ios-native/img/CustomStyleLayerExample.png) --- # Customizing Fonts https://docs.mapatlas.xyz/overview/sdk/ios-native/Customizing_Fonts # Customizing Fonts Using custom fonts MapMetrics Native iOS can render text that is part of an ``MLNSymbolStyleLayer`` in a font of your choice. The font customization options discussed in this document do not apply to user interface elements such as the scale bar or annotation callout views. ## Server-side fonts By default, the map renders characters using glyphs downloaded from the server. You apply fonts when building a style with [Maputnik]({"contact/admin/forURLs"}), in the `text-font` layout property in style JSON, or in the ``MLNSymbolStyleLayer/textFontNames`` property at runtime. The values in these properties must be font display names, not font family names or PostScript names. Each font name in the list must match a font that is present on the server; otherwise, the text will not load, even if one of the fonts is available. Each font name must be included in the `{fontstack}` portion of the JSON stylesheet’s [`glyphs`]({"contact/admin/forURLs"}) property. [Martin]({"contact/admin/forURLs"}) can serve fonts directly. You can also generate fonts with [font-maker]({"contact/admin/forURLs"}). ## Client-side fonts By default, Chinese hanzi, Japanese kana, and Korean hangul characters (CJK) are rendered on the client side. Client-side text rendering uses less bandwidth than server-side text rendering, especially when viewing regions of the map that feature a wide variety of CJK characters. First, the map attempts to apply a font that you specify the same way as you would specify a server-side font: in [Maputnik]({"contact/admin/forURLs"}), in the `text-font` layout property in style JSON, or in the `MLNSymbolStyleLayer.textFontNames` property at runtime. Instead of downloading the glyphs, the map tries to find a [system font]({"contact/admin/forURLs"}) or a font [bundled with your application]({"contact/admin/forURLs"}) that matches one of these fonts based on its family name (for example, “PingFang TC”), display name (“PingFang TC Ultralight”), or PostScript name (“PingFangTC-Ultralight”). If the symbol layer does not specify an available font that contains the required glyphs, then the map tries to find a matching font in the `MLNIdeographicFontFamilyName` [Info.plist key](doc:Info.plist_Keys). Like the ``MLNSymbolStyleLayer/textFontNames`` property, this key can contain a family name, display name, or PostScript name. This key is a global fallback that applies to all layers uniformly. It can either be a single string or an array of strings, which the map tries to apply in order from most preferred to least preferred. Each character is rendered in the first font you specify that has a glyph for the character. If the entire list of fonts is exhausted, the map uses the system’s font cascade list, which may vary based on the device model and system language. To disable client-side rendering of CJK characters, set the `MLNIdeographicFontFamilyName` key to the Boolean value `NO`. The map will revert to server-side font rendering. --- # Vector Tile Sources https://docs.mapatlas.xyz/overview/sdk/ios-native/DDSCircleLayerExample # Vector Tile Sources Add and style a vector tile source > This example uses UIKit This example shows how a vector data source can be added and a style for it can be configured dynamically. The tiles are [tiles around Innsbruck, Austria]({"contact/admin/forURLs"}) that use the OpenMapTiles schema. We are interested in the [POIs]({"contact/admin/forURLs"}) that are in the `poi` layer, and filter this further with an `NSPredicate` to only show POIs with a `class` of shop. Each POI has a `rank` which is normally used to reduce label density. In this example, we use it to demonstrate how a numeric attribute can be used for styling with the [step]({"contact/admin/forURLs"}) expression. POIs with a rank between 0 and 10 get a red color, between 10 and 20 green, etc. ```swift class DDSCircleLayerExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.tintColor = .darkGray // Set the map’s center coordinate and zoom level. let innsbruck = CLLocationCoordinate2D(latitude: 47.26497, longitude: 11.4088) mapView.setCenter(innsbruck, animated: false) mapView.zoomLevel = 14 mapView.delegate = self view.addSubview(mapView) } // Wait until the style is loaded before modifying the map style. func mapView(_: MLNMapView, didFinishLoading style: MLNStyle) { let source = MLNVectorTileSource(identifier: "demotiles", configurationURL: URL(string: "{"contact/admin/forURLs"}")!) style.addSource(source) let layer = MLNCircleStyleLayer(identifier: "poi-shop-style", source: source) layer.sourceLayerIdentifier = "poi" layer.predicate = NSPredicate(format: "class == %@", "shop") // Style the circle layer color based on the rank layer.circleColor = NSExpression(mglJSONObject: ["step", ["get", "rank"], 0, "red", 10, "green", 20, "blue", 30, "purple", 40, "yellow"] as [Any]) layer.circleRadius = NSExpression(forConstantValue: 3) style.addLayer(layer) } } ``` ![](/overview/sdk/ios-native/img/DDSCircleLayerExample.png) --- # Styles in Examples https://docs.mapatlas.xyz/overview/sdk/ios-native/ExampleStyles # Styles in Examples The following styles are used in the examples: ```swift let AMERICANA_STYLE = URL(string: "{"contact/admin/forURLs"}") let VERSATILES_COLORFUL_STYLE = URL(string: "{"contact/admin/forURLs"}") ``` --- # Information for Style Authors https://docs.mapatlas.xyz/overview/sdk/ios-native/For_Style_Authors # Information for Style Authors ## Designing for iOS When designing your style, consider the context in which your application shows the style. There are a number of considerations specific to iOS that may not be obvious when designing your style with Maputnik. A map view is essentially a graphical user interface element, so many of same issues in user interface design also apply when designing a map style. ### Color Ensure sufficient contrast in your application’s user interface when your map style is present. Standard user interface elements such as toolbars, sidebars, and sheets often overlap the map view with a translucent, blurred background, so make sure the contents of these elements remain legible with the map view underneath. The user location annotation view, the attribution button, any buttons in callout views, and any items in the navigation bar are influenced by your application’s tint color, so choose a tint color that contrasts well with your map style. If you intend your style to be used in the dark, consider the impact that Night Shift may have on your style’s colors. ### Typography and graphics Choose font and icon sizes appropriate to iOS devices. iPhones and iPads have smaller screens than the typical browser window, especially when multitasking is enabled. Your user's viewing distance may be shorter than on a desktop computer. Some of your users may use the Larger Dynamic Type and Accessibility Text features to increase the size of all text on the device. You can use the [runtime styling API](Manipulating-the-style-at-runtime) to adjust your style’s font and icon sizes accordingly. Design sprite images and choose font weights that look crisp on both standard-resolution displays and Retina displays. This SDK supports the same resolutions as iOS. Standard-resolution displays are limited to older devices that your application may or may not support, depending on its minimum deployment target. Icon and text labels should be legible regardless of the map’s orientation. By default, this SDK makes it easy for your users to rotate or tilt the map using multitouch gestures. If you do not intend your design to accommodate rotation and tilting, disable these gestures using the `MLNMapView.rotateEnabled` and `MLNMapView.pitchEnabled` properties, respectively, or the corresponding inspectables in Interface Builder. ### Interactivity Pay attention to whether elements of your style appear to be interactive. A text label may look like a tappable button merely due to matching your application’s tint color or the default blue tint color. You can make an icon or text label interactive by installing a gesture recognizer and performing feature querying (e.g., ``MLNMapView/visibleFeaturesAtPoint:``) to get details about the selected feature. Make sure your users can easily distinguish any interactive elements from the surrounding map, such as pins, the user location annotation view, or a route line. Avoid relying on hover effects to indicate interactive elements. Leave enough room between interactive elements to accommodate imprecise tapping gestures. For more information about user interface design, consult Apple’s [_iOS Human Interface Guidelines_]({"contact/admin/forURLs"}). To learn more about designing maps for mobile devices, see [Nathaniel Slaughter's blog post]({"contact/admin/forURLs"}) on the subject. ## Applying your style You set an `MLNMapView` object’s style either in code, by setting the `MLNMapView.styleURL` property, or in Interface Builder, by setting the “Style URL” inspectable. The URL must point to a local or remote style JSON file. The style JSON file format is defined by the [MapMetrics Style Spec]({"contact/admin/forURLs"}). ## Manipulating the style at runtime The _runtime styling API_ enables you to modify every aspect of a style dynamically as a user interacts with your application. The style itself is represented at runtime by an ``MLNStyle`` object, which provides access to various ``MLNSource`` and ``MLNStyleLayer`` objects that represent content sources and style layers, respectively. To avoid conflicts with Objective-C keywords or Cocoa terminology, this SDK uses the following terms for concepts defined in the style specification: In the style specification | In the SDK ---------------------------|--------- bounds | coordinate bounds filter | predicate function type | interpolation mode id | identifier image | style image layer | style layer property | attribute SDF icon | template image source | content source ## Specifying the map’s content Each source defined by a style JSON file is represented at runtime by a content source object that you can use to initialize new style layers. The content source object is a member of one of the following subclasses of ``MLNSource``: In style JSON | In the SDK --------------|----------- `vector` | ``MLNVectorTileSource`` `raster` | ``MLNRasterTileSource`` `raster-dem` | ``MLNRasterDEMSource`` `geojson` | ``MLNShapeSource`` `image` | ``MLNImageSource`` `canvas` and `video` sources are not supported. ### Tile sources Raster and vector tile sources may be defined in TileJSON configuration files. This SDK supports the properties defined in the style specification, which are a subset of the keys defined in version 2.1.0 of the [TileJSON]({"contact/admin/forURLs"}) specification. As an alternative to authoring a custom TileJSON file, you may supply various tile source options when creating a raster or vector tile source. These options are detailed in the `MLNTileSourceOption` documentation: In style JSON | In TileJSON | In the SDK --------------|---------------|----------- `url` | — | `configurationURL` parameter in `-[MLNTileSource initWithIdentifier:configurationURL:]` `tiles` | `tiles` | `tileURLTemplates` parameter in `-[MLNTileSource initWithIdentifier:tileURLTemplates:options:]` `minzoom` | `minzoom` | `MLNTileSourceOptionMinimumZoomLevel` `maxzoom` | `maxzoom` | `MLNTileSourceOptionMaximumZoomLevel` `bounds` | `bounds` | `MLNTileSourceOptionCoordinateBounds` `tileSize` | — | `MLNTileSourceOptionTileSize` `attribution` | `attribution` | `MLNTileSourceOptionAttributionHTMLString` (but consider specifying `MLNTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MLNTileSourceOptionTileCoordinateSystem` `encoding` | – | `MLNTileSourceOptionDEMEncoding` ### Shape sources Shape sources also accept various options. These options are detailed in the `MLNShapeSourceOption` documentation: In style JSON | In the SDK -----------------|----------- `data` | `url` parameter in `-[MLNShapeSource initWithIdentifier:URL:options:]` `maxzoom` | `MLNShapeSourceOptionMaximumZoomLevel` `buffer` | `MLNShapeSourceOptionBuffer` `tolerance` | `MLNShapeSourceOptionSimplificationTolerance` `cluster` | `MLNShapeSourceOptionClustered` `clusterRadius` | `MLNShapeSourceOptionClusterRadius` `clusterMinPoints` | `MLNShapeSourceOptionClusterMinPoints` `clusterMaxZoom` | `MLNShapeSourceOptionMaximumZoomLevelForClustering` `lineMetrics` | `MLNShapeSourceOptionLineDistanceMetrics` To create a shape source from local GeoJSON data, first [convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects), then use the `-[MLNShapeSource initWithIdentifier:shape:options:]` method. ### Image sources Image sources accept a non-axis aligned quadrilateral as their geographic coordinates. These coordinates, in `MLNCoordinateQuad`, are described in counterclockwise order, in contrast to the clockwise order defined in the style specification. ## Configuring the map content’s appearance Each layer defined by the style JSON file is represented at runtime by a style layer object, which you can use to refine the map’s appearance. The style layer object is a member of one of the following subclasses of `MLNStyleLayer`: In style JSON | In the SDK --------------|----------- `background` | `MLNBackgroundStyleLayer` `circle` | `MLNCircleStyleLayer` `fill` | `MLNFillStyleLayer` `fill-extrusion` | `MLNFillExtrusionStyleLayer` `heatmap` | `MLNHeatmapStyleLayer` `hillshade` | `MLNHillshadeStyleLayer` `line` | `MLNLineStyleLayer` `raster` | `MLNRasterStyleLayer` `symbol` | `MLNSymbolStyleLayer` You configure layout and paint attributes by setting properties on these style layer objects. The property names generally correspond to the style JSON properties, except for the use of camelCase instead of kebab-case. Properties whose names differ from the style specification are listed below: ### Circle style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- `circle-pitch-scale` | `MLNCircleStyleLayer.circleScaleAlignment` | `MLNCircleStyleLayer.circleScaleAlignment` `circle-translate` | `MLNCircleStyleLayer.circleTranslation` | `MLNCircleStyleLayer.circleTranslation` `circle-translate-anchor` | `MLNCircleStyleLayer.circleTranslationAnchor` | `MLNCircleStyleLayer.circleTranslationAnchor` ### Fill style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- `fill-antialias` | `MLNFillStyleLayer.fillAntialiased` | `MLNFillStyleLayer.isFillAntialiased` `fill-translate` | `MLNFillStyleLayer.fillTranslation` | `MLNFillStyleLayer.fillTranslation` `fill-translate-anchor` | `MLNFillStyleLayer.fillTranslationAnchor` | `MLNFillStyleLayer.fillTranslationAnchor` ### Fill extrusion style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- `fill-extrusion-vertical-gradient` | `MLNFillExtrusionStyleLayer.fillExtrusionHasVerticalGradient` | `MLNFillExtrusionStyleLayer.fillExtrusionHasVerticalGradient` `fill-extrusion-translate` | `MLNFillExtrusionStyleLayer.fillExtrusionTranslation` | `MLNFillExtrusionStyleLayer.fillExtrusionTranslation` `fill-extrusion-translate-anchor` | `MLNFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` | `MLNFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` ### Line style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- `line-dasharray` | `MLNLineStyleLayer.lineDashPattern` | `MLNLineStyleLayer.lineDashPattern` `line-translate` | `MLNLineStyleLayer.lineTranslation` | `MLNLineStyleLayer.lineTranslation` `line-translate-anchor` | `MLNLineStyleLayer.lineTranslationAnchor` | `MLNLineStyleLayer.lineTranslationAnchor` ### Raster style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- `raster-brightness-max` | `MLNRasterStyleLayer.maximumRasterBrightness` | `MLNRasterStyleLayer.maximumRasterBrightness` `raster-brightness-min` | `MLNRasterStyleLayer.minimumRasterBrightness` | `MLNRasterStyleLayer.minimumRasterBrightness` `raster-hue-rotate` | `MLNRasterStyleLayer.rasterHueRotation` | `MLNRasterStyleLayer.rasterHueRotation` `raster-resampling` | `MLNRasterStyleLayer.rasterResamplingMode` | `MLNRasterStyleLayer.rasterResamplingMode` ### Symbol style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- `icon-allow-overlap` | `MLNSymbolStyleLayer.iconAllowsOverlap` | `MLNSymbolStyleLayer.iconAllowsOverlap` `icon-ignore-placement` | `MLNSymbolStyleLayer.iconIgnoresPlacement` | `MLNSymbolStyleLayer.iconIgnoresPlacement` `icon-image` | `MLNSymbolStyleLayer.iconImageName` | `MLNSymbolStyleLayer.iconImageName` `icon-optional` | `MLNSymbolStyleLayer.iconOptional` | `MLNSymbolStyleLayer.isIconOptional` `icon-rotate` | `MLNSymbolStyleLayer.iconRotation` | `MLNSymbolStyleLayer.iconRotation` `icon-size` | `MLNSymbolStyleLayer.iconScale` | `MLNSymbolStyleLayer.iconScale` `icon-keep-upright` | `MLNSymbolStyleLayer.keepsIconUpright` | `MLNSymbolStyleLayer.keepsIconUpright` `text-keep-upright` | `MLNSymbolStyleLayer.keepsTextUpright` | `MLNSymbolStyleLayer.keepsTextUpright` `text-max-angle` | `MLNSymbolStyleLayer.maximumTextAngle` | `MLNSymbolStyleLayer.maximumTextAngle` `text-max-width` | `MLNSymbolStyleLayer.maximumTextWidth` | `MLNSymbolStyleLayer.maximumTextWidth` `symbol-avoid-edges` | `MLNSymbolStyleLayer.symbolAvoidsEdges` | `MLNSymbolStyleLayer.symbolAvoidsEdges` `text-field` | `MLNSymbolStyleLayer.text` | `MLNSymbolStyleLayer.text` `text-allow-overlap` | `MLNSymbolStyleLayer.textAllowsOverlap` | `MLNSymbolStyleLayer.textAllowsOverlap` `text-font` | `MLNSymbolStyleLayer.textFontNames` | `MLNSymbolStyleLayer.textFontNames` `text-size` | `MLNSymbolStyleLayer.textFontSize` | `MLNSymbolStyleLayer.textFontSize` `text-ignore-placement` | `MLNSymbolStyleLayer.textIgnoresPlacement` | `MLNSymbolStyleLayer.textIgnoresPlacement` `text-justify` | `MLNSymbolStyleLayer.textJustification` | `MLNSymbolStyleLayer.textJustification` `text-optional` | `MLNSymbolStyleLayer.textOptional` | `MLNSymbolStyleLayer.isTextOptional` `text-rotate` | `MLNSymbolStyleLayer.textRotation` | `MLNSymbolStyleLayer.textRotation` `text-writing-mode` | `MLNSymbolStyleLayer.textWritingModes` | `MLNSymbolStyleLayer.textWritingModes` `icon-translate` | `MLNSymbolStyleLayer.iconTranslation` | `MLNSymbolStyleLayer.iconTranslation` `icon-translate-anchor` | `MLNSymbolStyleLayer.iconTranslationAnchor` | `MLNSymbolStyleLayer.iconTranslationAnchor` `text-translate` | `MLNSymbolStyleLayer.textTranslation` | `MLNSymbolStyleLayer.textTranslation` `text-translate-anchor` | `MLNSymbolStyleLayer.textTranslationAnchor` | `MLNSymbolStyleLayer.textTranslationAnchor` ## Setting attribute values Each property representing a layout or paint attribute is set to an `NSExpression` object. `NSExpression` objects play the same role as [expressions in the MapMetrics Style Spec]({"contact/admin/forURLs"}), but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. ### Constant values in expressions In contrast to the JSON type that the style specification defines for each layout or paint property, the style value object often contains a more specific Foundation or Cocoa type. General rules for attribute types are listed below. Pay close attention to the SDK documentation for the attribute you want to get or set. In style JSON | In Objective-C | In Swift --------------|-----------------------|--------- Color | `UIColor` | `UIColor` Enum | `NSString` | `String` String | `NSString` | `String` Boolean | `NSNumber.boolValue` | `NSNumber.boolValue` Number | `NSNumber.floatValue` | `NSNumber.floatValue` Array (`-dasharray`) | `NSArray` | `[Float]` Array (`-font`) | `NSArray` | `[String]` Array (`-offset`, `-translate`) | `NSValue.CGVectorValue` | `NSValue.cgVectorValue` Array (`-padding`) | `NSValue.UIEdgeInsetsValue` | `NSValue.uiEdgeInsetsValue` For padding attributes, note that the arguments to `UIEdgeInsetsMake()` in Objective-C and `UIEdgeInsets(top:left:bottom:right:)` in Swift are specified in counterclockwise order, in contrast to the clockwise order defined by the style specification ## Filtering sources You can filter a shape or vector tile source by setting the `MLNVectorStyleLayer.predicate` property to an `NSPredicate` object. Below is a table of style JSON operators and the corresponding operators used in the predicate format string: In style JSON | In the format string --------------------------|--------------------- `["has", key]` | `key != nil` `["!has", key]` | `key == nil` `["==", key, value]` | `key == value` `["!=", key, value]` | `key != value` `[">", key, value]` | `key > value` `[">=", key, value]` | `key >= value` `["<", key, value]` | `key < value` `["<=", key, value]` | `key <= value` `["in", key, v0, …, vn]` | `key IN {v0, …, vn}` `["!in", key, v0, …, vn]` | `NOT key IN {v0, …, vn}` `["all", f0, …, fn]` | `p0 AND … AND pn` `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` ## Specifying the text format The following format attributes are defined as `NSString` constans that you can use to update the formatting of `MLNSymbolStyleLayer.text` property. In style JSON | In Objective-C | In Swift --------------|-----------------------|--------- `text-font` | `MLNFontNamesAttribute` | `.fontNamesAttribute` `font-scale` | `MLNFontScaleAttribute` | `.fontScaleAttribute` `text-color` | `MLNFontColorAttribute` | `.fontColorAttribute` See for a full description of the supported operators and operand types. --- # Working with GeoJSON Data https://docs.mapatlas.xyz/overview/sdk/ios-native/GeoJSON # Working with GeoJSON Data ## Adding a GeoJSON file to the map MapMetrics iOS offers several ways to work with [GeoJSON]({"contact/admin/forURLs"}) files. GeoJSON is a standard file format for representing geographic data. You can host a GeoJSON file or bundle it with your application, you can use a GeoJSON file as the basis of an ``MLNShapeSource`` object. Pass the file’s URL into the ``MLNShapeSource/initWithIdentifier:URL:options:`` initializer and add the shape source to the map using the ``MLNStyle/addSource:`` method. The URL may be a local file URL, an HTTP URL, or an HTTPS URL. Once you’ve added the GeoJSON file to the map via an ``MLNShapeSource`` object, you can configure the appearance of its data and control what data is visible using ``MLNStyleLayer`` objects. You can also [access the data programmatically](#Extracting-GeoJSON-data-from-the-map). ## Converting GeoJSON data into shape objects If you have GeoJSON data in the form of source code (also known as “GeoJSON text”), you can convert it into an ``MLNShape``, ``MLNFeature``, or ``MLNShapeCollectionFeature`` object that the ``MLNShapeSource`` class understands natively. First, create an `NSData` object out of the source code string or file contents, then pass that data object into the ``MLNShape/shapeWithData:encoding:error:`` method. Finally, you can pass the resulting shape or feature object into the ``MLNShapeSource/initWithIdentifier:shape:options:`` initializer and add it to the map, or you can use the object and its properties to power non-map-related functionality in your application. To include multiple shapes in the source, create and pass an ``MLNShapeCollection`` or ``MLNShapeCollectionFeature`` object to ``MLNShapeSource/initWithIdentifier:shape:options:``. Alternatively, use the ``MLNShapeSource/initWithIdentifier:features:options:`` or ``MLNShapeSource/initWithIdentifier:shapes:options:`` method to create a shape source with an array. ``MLNShapeSource/initWithIdentifier:features:options:`` accepts only ``MLNFeature`` instances, such as ``MLNPointFeature`` objects, whose attributes you can use when applying a predicate to ``MLNVectorStyleLayer`` or configuring a style layer’s appearance. ## Extracting GeoJSON data from the map Any ``MLNShape``, ``MLNFeature``, or ``MLNShapeCollectionFeature`` object has an ``MLNShape/geoJSONDataUsingEncoding:`` method that you can use to create a GeoJSON source code representation of the object. You can extract a feature object from the map using a method such as ``MLNMapView/visibleFeaturesAtPoint:``. ## About GeoJSON deserialization The process of converting GeoJSON text into ``MLNShape``, ``MLNFeature``, or ``MLNShapeCollectionFeature`` objects is known as “GeoJSON deserialization”. GeoJSON geometries, features, and feature collections are known in this SDK as shapes, features, and shape collection features, respectively. Each GeoJSON object type corresponds to a type provided by either this SDK or the Core Location framework: GeoJSON object type | SDK type --------------------|--------- `Position` (longitude, latitude) | `CLLocationCoordinate2D` (latitude, longitude) `Point` | ``MLNPointAnnotation`` `MultiPoint` | ``MLNPointCollection`` `LineString` | ``MLNPolyline`` `MultiLineString` | ``MLNMultiPolyline`` `Polygon` | ``MLNPolygon`` `MultiPolygon` | ``MLNMultiPolygon`` `GeometryCollection` | ``MLNShapeCollection`` `Feature` | ``MLNFeature`` `FeatureCollection` | ``MLNShapeCollectionFeature`` A `Feature` object in GeoJSON corresponds to an instance of an ``MLNShape`` subclass conforming to the ``MLNFeature`` protocol. There is a distinct ``MLNFeature``-conforming class for each type of geometry that a GeoJSON feature can contain. This allows features to be used as raw shapes where convenient. For example, some features can be added to a map view as annotations. Note that identifiers and attributes will not be available for feature querying when a feature is used as an annotation. In contrast to the GeoJSON standard, it is possible for ``MLNShape`` subclasses other than ``MLNPointAnnotation`` to straddle the antimeridian. The following GeoJSON data types correspond straightforwardly to Foundation data types when they occur as feature identifiers or property values: GeoJSON data type | Objective-C representation | Swift representation -------------------|----------------------------|--------------------- `null` | `NSNull` | `NSNull` `true`, `false` | `NSNumber.boolValue` | `Bool` Integer | `NSNumber.unsignedLongLongValue`, `NSNumber.longLongValue` | `UInt64`, `Int64` Floating-point number | `NSNumber.doubleValue` | `Double` String | `NSString` | `String` --- # User Interactions https://docs.mapatlas.xyz/overview/sdk/ios-native/GestureRecognizers # User Interactions Learn how to work with gesture recognizers MapMetrics Native iOS provides a set of built-in gesture recognizers. You can customize or supplement these gestures according to your use case. You see what gesture recognizers are on your ``MLNMapView`` by accessing the `gestureRecognizers` property on your map. ## Configuring user interaction Several properties on an ``MLNMapView`` provide ways to enable or disable a set of gesture recognizers. Boolean values are set to `true` by default. - ``MLNMapView/zoomEnabled`` - Allows the user to zoom in or out by pinching two fingers, double-tapping, tapping with two fingers, or double-tapping then dragging vertically. Accepts Boolean values. - ``MLNMapView/scrollEnabled`` - Allows the user to scroll by dragging or swiping one finger. Accepts Boolean values. - ``MLNMapView/rotateEnabled`` - Allows the user to rotate by moving two fingers in a circular motion. Accepts Boolean values. - ``MLNMapView/pitchEnabled`` - Allows the user to tilt the map by vertically dragging two fingers. Accepts Boolean values. - ``MLNMapView/decelerationRate`` - Determines the rate of deceleration after the user lifts their finger. You can set the value using the ``MLNMapViewDecelerationRateNormal``, ``MLNMapViewDecelerationRateFast``, or ``MLNMapViewDecelerationRateImmediate`` constants. ## Individual gestures |Gesture | Description | Related Property | |:-------:|----------------| -----------| |Pinch | Zooms in or out on the map's anchor point | ``MLNMapView/zoomEnabled`` | |Rotation | Changes the ``MLNMapView`` direction based on the user rotating two fingers in a circular motion | ``MLNMapView/rotateEnabled`` | |Single tap | Selects/deselects the annotation that you tap. | | |Double tap | Zooms in on the map's anchor point | ``MLNMapView/zoomEnabled`` | |Two-finger tap | Zooms out with the map's anchor point centered | ``MLNMapView/zoomEnabled`` | |Pan | Scrolls across mapView (_note: if_ `MLNUserTrackingModeFollow` _is being used, it will be disabled once the user pans_)| ``MLNMapView/scrollEnabled`` | |Two-finger drag | Adjusts the pitch of the ``MLNMapView`` | `pitchEnabled` | |One-finger zoom | Tap twice; on second tap, hold your finger on the map and pan up to zoom in, or down to zoom out | ``MLNMapView/zoomEnabled`` | @Video( source: "rotation.mp4", poster: "rotation.png", alt: "A short video showing the gesture for rotation with two fingers.") { Rotation with two fingers. } @Video( source: "quickzoom.mp4", poster: "quickzoom.png", alt: "A short video showing rotation with one finger.") { One finger zoom with a double tap. } ## Adding custom gesture recognizers You can add [`UIGestureRecognizer`s]({"contact/admin/forURLs"}) to your map programmatically or via storyboard. Adding custom responses to gesture recognizers can enhance your user's experience, but try to use standard gestures where possible. The gesture recognizers that you add will take priority over the built-in gesture recognizer. You can also set up your own gesture recognizer to work simultaneously with built-in gesture recognizers by using [`gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:`]({"contact/admin/forURLs"}), allowing you to enhance already existing gesture recognizers. You can also add gesture recognizers that are only called when the default gesture recognizer fails (and vice versa), such as when a user taps on a part of the map that is not an annotation. The documentation for ``MLNMapView`` includes an example of how to create a fallback gesture recognizer. If you would like to disable a specific set of gesture recognizers, such as zoom, you can set the Boolean value for the appropriate property to `false`. You can then add your own gesture recognizers to perform those actions. --- # Prerequisite https://docs.mapatlas.xyz/overview/sdk/ios-native/GettingStarted # Prerequisite For all of our examples, you will need: - an API key, - knowledge in Swift/iOS development, - one style (default) ## Get the library There are several ways to get this library: - from the github repository (https://github.com/MapMetrics/MapMetrics-iOS) - from swift package https://github.com/MapMetrics/MapMetrics-iOS - from cocoapods.org ## Installation (CocoaPods) Add this to your Podfile: ```ruby target 'YourApp' do pod 'MapMetrics-iOS', '~> 0.0.3' # Use the latest version # OR pod 'MapMetrics-iOS', :git => 'https://github.com/MapMetrics/MapMetrics-iOS', :tag => '0.0.1' end ``` Run: ```bash pod install ``` ## Required Build Settings (Sandbox Fix) To prevent `rsync.samba deny(1)` errors, users must add these settings: ### Option A: Automatic Fix (via Podfile) Add to your Podfile: ```ruby post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| # Disable sandbox restrictions config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO' end end end ``` Then run: ```bash pod install ``` ### Option B: Manual Fix (Xcode Settings) 1. Open your project in Xcode. 2. Go to **Target → Build Settings**. 3. Search for `ENABLE_USER_SCRIPT_SANDBOXING`. 4. Set to **NO** for all configurations (Debug/Release). --- ## Verify Installation Import in your code: ```swift import MapMetrics ``` Clean Build (if issues persist): ```bash rm -rf ~/Library/Developer/Xcode/DerivedData/* ``` --- ## Troubleshooting | Issue | Solution | |---------------------------|-----------| | Sandbox deny(1) errors | Ensure `ENABLE_USER_SCRIPT_SANDBOXING=NO` is set. | | `pod install` fails | Delete `Pods/` and `Podfile.lock`, then retry. | | Version conflicts | Run `pod update MapMetrics`. | --- ## Example Podfile (Complete) ```ruby platform :ios, '12.0' target 'YourApp' do use_frameworks! pod 'MapMetrics-iOS', '~> 0.0.3' post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO' end end end end ``` --- ## Example Usage Here's how to integrate **MapMetrics** into your project: ```swift class ViewController: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView( frame: view.bounds, styleURL: URL(string: "") ) mapView.delegate = self view.addSubview(mapView) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.delegate = self // This is a better starting position let center = CLLocationCoordinate2D(latitude: 20.0, longitude: 0.0) // More centered globally mapView.setCenter(center, zoomLevel: 2, animated: false) // Lower zoom to see more area view.addSubview(mapView) } } ``` --- --- # Missing Images https://docs.mapatlas.xyz/overview/sdk/ios-native/IMAGES-NEEDED # Missing Images The following images are referenced in the documentation but not yet downloaded. These images are part of the MapMetrics Native iOS documentation and can be found in the official repository or documentation site. ## Image List 1. **AddPackageDependencies.png** - Referenced in: GettingStarted.md 2. **AnnotationViewExample.png** - Referenced in: AnnotationViewExample.md 3. **BuildingLightExample.png** - Referenced in: BuildingLightExample.md 4. **CustomStyleLayerExample.png** - Referenced in: CustomStyleLayerExample.md 5. **DDSCircleLayerExample.png** - Referenced in: DDSCircleLayerExample.md 6. **DemotilesScreenshot.png** - Referenced in: (various) 7. **ImpreciseLocation.png** - Referenced in: LocationPrivacyExample.md 8. **LineStyleLayerExample.png** - Referenced in: LineStyleLayerExample.md 9. **MultipleImagesExample.png** - Referenced in: MultipleImagesExample.md 10. **PluginLayer.png** - Referenced in: PluginLayers.md 11. **PreciseLocationRequestPopup.png** - Referenced in: LocationPrivacyExample.md 12. **RenderingStatistics.png** - Referenced in: RenderingStatisticsHud.md 13. **WebAPIDataExample.png** - Referenced in: WebAPIDataExample.md 14. **pmtiles-demo.png** - Referenced in: PMTiles.md 15. **polyline.gif** - Referenced in: LineOnUserTap.md ## Where to Find Images ### Option 1: GitHub Repository (Recommended) Visit the MapMetrics Native repository and navigate to the iOS documentation images: ``` {"contact/admin/forURLs"} ``` Look for the example app or documentation guides folders which contain screenshot images. ### Option 2: Generate from Examples Many of these images are screenshots from the example applications. You can: 1. Clone the MapMetrics Native repository 2. Build and run the iOS example app 3. Navigate to each example 4. Take screenshots ### Option 3: Documentation Website The rendered documentation site may have the images: ``` {"contact/admin/forURLs"} ``` However, this is a JavaScript-rendered site and images are loaded dynamically. ### Option 4: Download from Releases Check the releases page for documentation bundles that may include images: ``` {"contact/admin/forURLs"} ``` ## Currently Available - screenshot.png (located in img/ directory) ## Notes - All image references in the markdown files use relative paths: `![](ImageName.png)` - Images should be placed in the `img/` directory - Some documents reference videos (e.g., gltfplugin.mp4 in PluginLayers.md) - The markdown files are already properly formatted and ready to use once images are added --- # Info.plist Keys https://docs.mapatlas.xyz/overview/sdk/ios-native/Info.plist_Keys # Info.plist Keys MapMetrics Native for iOS supports custom `Info.plist` keys in your application in order to configure various settings. ## MLNApiKey If it is required by the tileserver you use, set the API key to be used by all instances of ``MLNMapView`` in the current application. ## MLNAccuracyAuthorizationDescription Set the accuracy authorization description string as an element of `NSLocationTemporaryUsageDescriptionDictionary` to be used by the map to request authorization when the `MLNLocationManager.accuracyAuthorization` is set to `CLAccuracyAuthorizationReducedAccuracy`. Requesting accuracy authorization is available for devices running iOS 14.0 and above. Example: ```xml NSLocationTemporaryUsageDescriptionDictionary MLNAccuracyAuthorizationDescription We require your precise location to help you navigate the map. ``` Remove `MLNAccuracyAuthorizationDescription` if you want to control when to request for accuracy authorization. ## MLNIdeographicFontFamilyName This key configures a global fallback font or fonts for [client-side text rendering](doc:Customizing_Fonts) of Chinese hanzi, Japanese kana, and Korean hangul characters (CJK) that appear in text labels. If the fonts you specify in the `MLNSymbolStyleLayer.textFontNames` property are all unavailable or lack a glyph for rendering a given CJK character, the map uses the contents of this key to choose a [system font]({"contact/admin/forURLs"}) or a font [bundled with your application]({"contact/admin/forURLs"}). This key specifies a fallback for all style layers in all map views and map snapshots. If you do not specify this key or none of the font names matches, the map applies a font from the system’s font cascade list, which may vary based on the device model and system language. This key can either be set to a single string or an array of strings, which the map tries to apply in order from most preferred to least preferred. Each string can be a family name (for example, “PingFang TC”), display name (“PingFang TC Ultralight”), or PostScript name (“PingFangTC-Ultralight”). To disable client-side rendering of CJK characters in favor of [server-side rendering](customizing-fonts.html#server-side-fonts), set this key to the Boolean value `NO`. ## MLNOfflineStorageDatabasePath This key customizes the file path at which `MLNOfflineStorage` keeps the offline map database, which contains any offline packs as well as the ambient cache. Most applications should not need to customize this path; however, you could customize it to implement a migration path between different versions of your application. The key is interpreted as either an absolute file path or a file path relative to the main bundle’s resource folder, resolving any tilde or symbolic link. The path must be writable. If a database does not exist at the path you specify, one will be created automatically. An offline map database can consume a significant amount of the user’s bandwidth and iCloud storage due to iCloud backups. To exclude the database from backups, set the containing directory’s `NSURLIsExcludedFromBackupKey` resource property to the Boolean value `YES` using the [`NSURL/setResourceValue:forKey:error:`]({"contact/admin/forURLs"}) method. The entire directory will be affected, not just the database file. If the user restores the application from a backup, your application will need to restore any offline packs that had been previously downloaded. At runtime, you can obtain the value of this key using the ``MLNOfflineStorage/databasePath`` and ``MLNOfflineStorage/databaseURL`` properties. ## MLNCollisionBehaviorPre4_0 If this key is set to YES (`true`), collision detection is performed only between symbol style layers based on the same source, as in versions 2.0–3.7 of the MapMetrics Native iOS. In other words, symbols in an `MLNSymbolStyleLayer` based on one source (for example, an `MLNShapeSource`) may overlap with symbols in another layer that is based on a different source. This is the case regardless of the ``MLNSymbolStyleLayer/iconAllowsOverlap``, ``MLNSymbolStyleLayer/iconIgnoresPlacement``, ``MLNSymbolStyleLayer/textAllowsOverlap``, and ``MLNSymbolStyleLayer/textIgnoresPlacement`` properties. Beginning in version 4.0, the SDK also performs collision detection between style layers based on different sources by default. For the default behavior, omit the `MLNCollisionBehaviorPre4_0` key or set it to NO (`false`). This property may also be set using `[[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:@"MLNCollisionBehaviorPre4_0"]`; it will override any value specified in the `Info.plist`. --- # Add Line on User Tap https://docs.mapatlas.xyz/overview/sdk/ios-native/LineOnUserTap # Add Line on User Tap Demonstrating adding ``MLNPolyline`` annotations and responding to user input. > Note: This example uses SwiftUI. This example draws a line from the tapped location to the center of the map. Handling the tap is done by the `Coordinator` class. It converts the location on the view to a geographic coordinate. It removes existing annotations before adding the new line. ```swift struct LineTapMap: UIViewRepresentable { func makeUIView(context: Context) -> MLNMapView { let mapView = MLNMapView() // Add a single tap gesture recognizer let singleTap = UITapGestureRecognizer( target: context.coordinator, action: #selector(Coordinator.handleMapTap(sender:)) ) for recognizer in mapView.gestureRecognizers! where recognizer is UITapGestureRecognizer { singleTap.require(toFail: recognizer) } mapView.addGestureRecognizer(singleTap) return mapView } func updateUIView(_: MLNMapView, context _: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject { var parent: LineTapMap init(_ parent: LineTapMap) { self.parent = parent } @objc func handleMapTap(sender: UITapGestureRecognizer) { guard let mapView = sender.view as? MLNMapView else { return } // Convert tap location (CGPoint) to geographic coordinate (CLLocationCoordinate2D). let tapPoint: CGPoint = sender.location(in: mapView) let tapCoordinate: CLLocationCoordinate2D = mapView.convert(tapPoint, toCoordinateFrom: nil) print("You tapped at: \(tapCoordinate.latitude), \(tapCoordinate.longitude)") // Create an array of coordinates for our polyline, starting at the center of the map and ending at the tap coordinate. var coordinates: [CLLocationCoordinate2D] = [mapView.centerCoordinate, tapCoordinate] // Remove any existing polyline(s) from the map. if let existingAnnotations = mapView.annotations { mapView.removeAnnotations(existingAnnotations) } // Add a polyline with the new coordinates. let polyline = MLNPolyline(coordinates: &coordinates, count: UInt(coordinates.count)) mapView.addAnnotation(polyline) } } } ``` ![](/overview/sdk/ios-native/img/polyline.gif) --- # Using GeoJSON with a line style layer https://docs.mapatlas.xyz/overview/sdk/ios-native/LineStyleLayerExample # Using GeoJSON with a line style layer Adding an ``MLNLineStyleLayer`` to the map using a GeoJSON file. > Note: This example uses UIKit. ```swift class LineStyleLayerExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds, styleURL: VERSATILES_COLORFUL_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.setCenter( CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736), zoomLevel: 11, animated: false ) view.addSubview(mapView) mapView.delegate = self } // Wait until the map is loaded before adding to the map. func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) { loadGeoJson() } func loadGeoJson() { DispatchQueue.global().async { // Get the path for example.geojson in the app’s bundle. guard let jsonUrl = Bundle.main.url(forResource: "example", withExtension: "geojson") else { preconditionFailure("Failed to load local GeoJSON file") } guard let jsonData = try? Data(contentsOf: jsonUrl) else { preconditionFailure("Failed to parse GeoJSON file") } DispatchQueue.main.async { self.drawPolyline(geoJson: jsonData) } } } func drawPolyline(geoJson: Data) { // Add our GeoJSON data to the map as an MLNGeoJSONSource. // We can then reference this data from an MLNStyleLayer. // MLNMapView.style is optional, so you must guard against it not being set. guard let style = mapView.style else { return } guard let shapeFromGeoJSON = try? MLNShape(data: geoJson, encoding: String.Encoding.utf8.rawValue) else { fatalError("Could not generate MLNShape") } let source = MLNShapeSource(identifier: "polyline", shape: shapeFromGeoJSON, options: nil) style.addSource(source) // Create new layer for the line. let layer = MLNLineStyleLayer(identifier: "polyline", source: source) // Set the line join and cap to a rounded end. layer.lineJoin = NSExpression(forConstantValue: "round") layer.lineCap = NSExpression(forConstantValue: "round") // Set the line color to a constant blue color. layer.lineColor = NSExpression(forConstantValue: UIColor(red: 59 / 255, green: 178 / 255, blue: 208 / 255, alpha: 1)) // Use `NSExpression` to smoothly adjust the line width from 2pt to 20pt between zoom levels 14 and 18. The `interpolationBase` parameter allows the values to interpolate along an exponential curve. layer.lineWidth = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [14: 2, 18: 20])) // We can also add a second layer that will draw a stroke around the original line. let casingLayer = MLNLineStyleLayer(identifier: "polyline-case", source: source) // Copy these attributes from the main line layer. casingLayer.lineJoin = layer.lineJoin casingLayer.lineCap = layer.lineCap // Line gap width represents the space before the outline begins, so should match the main line’s line width exactly. casingLayer.lineGapWidth = layer.lineWidth // Stroke color slightly darker than the line color. casingLayer.lineColor = NSExpression(forConstantValue: UIColor(red: 41 / 255, green: 145 / 255, blue: 171 / 255, alpha: 1)) // Use `NSExpression` to gradually increase the stroke width between zoom levels 14 and 18. casingLayer.lineWidth = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [14: 1, 18: 4])) // Just for fun, let’s add another copy of the line with a dash pattern. let dashedLayer = MLNLineStyleLayer(identifier: "polyline-dash", source: source) dashedLayer.lineJoin = layer.lineJoin dashedLayer.lineCap = layer.lineCap dashedLayer.lineColor = NSExpression(forConstantValue: UIColor.white) dashedLayer.lineOpacity = NSExpression(forConstantValue: 0.5) dashedLayer.lineWidth = layer.lineWidth // Dash pattern in the format [dash, gap, dash, gap, ...]. You’ll want to adjust these values based on the line cap style. dashedLayer.lineDashPattern = NSExpression(forConstantValue: [0, 1.5]) style.addLayer(layer) style.addLayer(dashedLayer) style.insertLayer(casingLayer, below: layer) } } ``` ![](/overview/sdk/ios-native/img/LineStyleLayerExample.png) --- # User Location & Location Privacy https://docs.mapatlas.xyz/overview/sdk/ios-native/LocationPrivacyExample # User Location & Location Privacy Requesting precise location with ``MLNLocationManager``. > Note: This example uses SwiftUI. This example shows how to request a precise location with ``MLNLocationManager``. Let's prepare your `Info.plist`: First, provide a description why your app needs to access location: ```plist NSLocationWhenInUseUsageDescription Dummy Location When In Use Description ``` Second, add a description why your app needs precise location: ```plist NSLocationTemporaryUsageDescriptionDictionary MLNAccuracyAuthorizationDescription Dummy Precise Location Description ``` Third and finally, specify your app only needs reduced location access by default (until you request precise accuracy in the example): ```plist NSLocationDefaultAccuracyReduced ``` The `Coordinator` defined below is also the ``MLNMapViewDelegate``. When the location manager authorization changes it will call the relevant method. If precise location has not been granted, a button is shown at the bottom of the map. ![](/overview/sdk/ios-native/img/ImpreciseLocation.png) When the button is pressed a pop-up with the description we set in `Info.plist` will be shown: ![](/overview/sdk/ios-native/img/PreciseLocationRequestPopup.png) ```swift enum LocationAccuracyState { case unknown case reducedAccuracy case fullAccuracy } @MainActor class PrivacyExampleViewModel: NSObject, ObservableObject { @Published var locationAccuracy: LocationAccuracyState = .unknown @Published var showTemporaryLocationAuthorization = false } class PrivacyExampleCoordinator: NSObject, MLNMapViewDelegate { @ObservedObject private var mapViewModel: PrivacyExampleViewModel private var pannedToUserLocation = false init(mapViewModel: PrivacyExampleViewModel) { self.mapViewModel = mapViewModel super.init() } @MainActor func mapView(_: MLNMapView, didChangeLocationManagerAuthorization manager: MLNLocationManager) { guard let accuracySetting = manager.accuracyAuthorization else { return } switch accuracySetting() { case .fullAccuracy: mapViewModel.locationAccuracy = .fullAccuracy case .reducedAccuracy: mapViewModel.locationAccuracy = .reducedAccuracy @unknown default: mapViewModel.locationAccuracy = .unknown } } // when a location is available for the first time, we fly to it func mapView(_ mapView: MLNMapView, didUpdate _: MLNUserLocation?) { guard !pannedToUserLocation else { return } guard let userLocation = mapView.userLocation else { print("User location is currently not available.") return } mapView.fly(to: MLNMapCamera(lookingAtCenter: userLocation.coordinate, altitude: 100_000, pitch: 0, heading: 0)) pannedToUserLocation = true } } struct PrivacyExampleRepresentable: UIViewRepresentable { @ObservedObject var mapViewModel: PrivacyExampleViewModel func makeCoordinator() -> PrivacyExampleCoordinator { PrivacyExampleCoordinator(mapViewModel: mapViewModel) } func makeUIView(context: Context) -> MLNMapView { let mapView = MLNMapView() mapView.delegate = context.coordinator mapView.showsUserLocation = true return mapView } func updateUIView(_ mapView: MLNMapView, context _: Context) { if mapViewModel.showTemporaryLocationAuthorization { let purposeKey = "MLNAccuracyAuthorizationDescription" mapView.locationManager.requestTemporaryFullAccuracyAuthorization?(withPurposeKey: purposeKey) DispatchQueue.main.async { mapViewModel.showTemporaryLocationAuthorization = false } } } } struct LocationPrivacyExampleView: View { @StateObject private var viewModel = PrivacyExampleViewModel() var body: some View { VStack { PrivacyExampleRepresentable(mapViewModel: viewModel) .edgesIgnoringSafeArea(.all) if viewModel.locationAccuracy == LocationAccuracyState.reducedAccuracy { Button("Request Precise Location") { viewModel.showTemporaryLocationAuthorization.toggle() } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } } } } ``` --- # Manage Offline Regions https://docs.mapatlas.xyz/overview/sdk/ios-native/ManageOfflineRegionsExample # Manage Offline Regions Query, delete and download offline regions > Note: This example uses UIKit. This example is similar to , but shows how offline regions can be managed. - ``MLNOfflineStorage/addPackForRegion:withContext:completionHandler:`` is used to kick off downloads for offline regions, as before. - ``MLNOfflineStorage/packs`` returns an array of packs that have been downloaded. In this example they are shown in an `UITableView`. - ``MLNOfflineStorage/resetDatabaseWithCompletionHandler:`` can be used to reset the (offline) database. Note that this includes the ambient cache at the time of writing. In this example, this method is used on view initialization. When selecting one of the packs in the table view, the map moves to the bounds of the corresponding region. ```swift class ManageOfflineRegionsExample: UIViewController, MLNMapViewDelegate { let jsonDecoder = JSONDecoder() struct UserData: Codable { var name: String } lazy var mapView: MLNMapView = { let mapView = MLNMapView(frame: CGRect.zero, styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.tintColor = .gray mapView.delegate = self mapView.translatesAutoresizingMaskIntoConstraints = false return mapView }() lazy var downloadButton: UIButton = { let downloadButton = UIButton(frame: CGRect.zero) downloadButton.backgroundColor = UIColor.systemBlue downloadButton.setTitleColor(UIColor.white, for: .normal) downloadButton.setTitle("Download Region", for: .normal) downloadButton.addTarget(self, action: #selector(startOfflinePackDownload), for: .touchUpInside) downloadButton.layer.cornerRadius = view.bounds.width / 30 downloadButton.translatesAutoresizingMaskIntoConstraints = false return downloadButton }() lazy var tableView: UITableView = { let tableView = UITableView(frame: CGRect.zero) tableView.delegate = self tableView.dataSource = self tableView.translatesAutoresizingMaskIntoConstraints = false return tableView }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(mapView) view.addSubview(tableView) mapView.addSubview(downloadButton) let centerCoordinate = CLLocationCoordinate2D(latitude: 22.27933, longitude: 114.16281) mapView.setCenter(centerCoordinate, zoomLevel: 13, animated: false) // Set up constraints for map view, table view, and download button. installConstraints() } func setupOfflinePackHandler() { NotificationCenter.default.addObserver(self, selector: #selector(offlinePackProgressDidChange), name: NSNotification.Name.MLNOfflinePackProgressChanged, object: nil) } func installConstraints() { NSLayoutConstraint.activate([ mapView.topAnchor.constraint(equalTo: view.topAnchor), mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), mapView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5), tableView.topAnchor.constraint(equalTo: mapView.bottomAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), downloadButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 100), downloadButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 5), downloadButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.45), downloadButton.heightAnchor.constraint(equalTo: downloadButton.widthAnchor, multiplier: 0.2), ]) } /* For the purposes of this example, remove any offline packs that exist before the example is re-loaded. */ override func viewWillAppear(_: Bool) { MLNOfflineStorage.shared.resetDatabase { error in if let error { // Handle the error here if packs can't be removed. print(error) } else { MLNOfflineStorage.shared.reloadPacks() } } } @objc func startOfflinePackDownload(selector _: NSNotification) { // Setup offline pack notification handlers. setupOfflinePackHandler() /** Create a region that includes the current map camera, to be captured in an offline map. Note: Because tile count grows exponentially as zoom level increases, you should be conservative with your `toZoomLevel` setting. */ let region = MLNTilePyramidOfflineRegion(styleURL: mapView.styleURL, bounds: mapView.visibleCoordinateBounds, fromZoomLevel: mapView.zoomLevel, toZoomLevel: mapView.zoomLevel + 2) // Store some data for identification purposes alongside the offline pack. let userInfo = UserData(name: "\(region.bounds)") let jsonEncoder = JSONEncoder() let optionalContext = try? jsonEncoder.encode(userInfo) guard let context = optionalContext else { print("Error: failed to get context") return } // Create and register an offline pack with the shared offline storage object. MLNOfflineStorage.shared.addPack(for: region, withContext: context) { pack, error in guard error == nil else { // Handle the error if the offline pack couldn’t be created. print("Error: \(error?.localizedDescription ?? "unknown error")") return } // Begin downloading the map for offline use. pack!.resume() } } // MARK: - MLNOfflinePack notification handlers @objc func offlinePackProgressDidChange(notification: NSNotification) { /** Get the offline pack this notification is referring to, along with its associated metadata. */ if let pack = notification.object as? MLNOfflinePack, let userInfo = try? jsonDecoder.decode(UserData.self, from: pack.context) { // At this point, the offline pack has finished downloading. if pack.state == .complete { let byteCount = ByteCountFormatter.string(fromByteCount: Int64(pack.progress.countOfBytesCompleted), countStyle: ByteCountFormatter.CountStyle.memory) let packName = userInfo.name print(""" Offline pack “\(packName)” completed download: - Bytes: \(byteCount) - Resource count: \(pack.progress.countOfResourcesCompleted)") """) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.MLNOfflinePackProgressChanged, object: nil) } } // Reload the table to update the progress percentage for each offline pack. tableView.reloadData() } } private extension MLNOfflinePackProgress { var percentCompleted: Float { guard countOfResourcesExpected != 0 else { return 0 } let percentage = Float(countOfResourcesCompleted) / Float(countOfResourcesExpected) * 100 return percentage } var formattedCountOfBytesCompleted: String { ByteCountFormatter.string(fromByteCount: Int64(countOfBytesCompleted), countStyle: .memory) } } extension ManageOfflineRegionsExample: UITableViewDelegate, UITableViewDataSource { // Create the table view which will display the downloaded regions. func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { if let packs = MLNOfflineStorage.shared.packs { return packs.count } else { return 0 } } func tableView(_: UITableView, viewForHeaderInSection _: Int) -> UIView? { let label = UILabel() label.backgroundColor = UIColor.systemBlue label.textColor = UIColor.white label.font = UIFont.preferredFont(forTextStyle: .headline) label.textAlignment = .center if MLNOfflineStorage.shared.packs != nil { label.text = "Offline maps" } else { label.text = "No offline maps" } return label } func tableView(_: UITableView, heightForHeaderInSection _: Int) -> CGFloat { 50.0 } func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell") if let packs = MLNOfflineStorage.shared.packs { let pack = packs[indexPath.row] cell.textLabel?.text = "Region \(indexPath.row + 1): size: \(pack.progress.formattedCountOfBytesCompleted)" cell.detailTextLabel?.text = "Percent completion: \(pack.progress.percentCompleted)%" } return cell } func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { guard let packs = MLNOfflineStorage.shared.packs else { return } if let selectedRegion = packs[indexPath.row].region as? MLNTilePyramidOfflineRegion { mapView.setVisibleCoordinateBounds(selectedRegion.bounds, animated: true) } } } ``` --- # Adding Multiple Images https://docs.mapatlas.xyz/overview/sdk/ios-native/MultipleImagesExample # Adding Multiple Images Adding images to the map and assigning them to POI types > This example uses UIKit. This example uses POIs in national parks and adds them to the map with data and icons provided by the National Park Service. Both the data and the icons are in the public domain. ## Preparation We've done all the work for you and generated [`pois-nps.mbtiles`]({"contact/admin/forURLs"}), which you can download directly. You can skip the rest of this section unless you're curious how we built the MBTiles from the source data. - Data, Shapefile download: [{"contact/admin/forURLs"}]({"contact/admin/forURLs"}) - Icons: [{"contact/admin/forURLs"}]({"contact/admin/forURLs"}) Convert the data first to GeoJSON format with [`ogr2ogr`]({"contact/admin/forURLs"}) and then to GeoJSON format using [tippecanoe]({"contact/admin/forURLs"}). ``` ogr2ogr -f GeoJSON pois.json -t_srs EPSG:4326 nps-pois.shp tippecanoe -o pois-nps.mbtiles pois.json ``` The resulting `.mbtiles` file can be hosted with a tile server such as [Martin]({"contact/admin/forURLs"}) or embedded in the app bundle as a resource. Since the file is quite small in this case we will use that last option in this example. Martin comes with a [`mbtiles` binary]({"contact/admin/forURLs"}) that allows us to inspect what the MBTiles file contains from the command line. ``` mbtiles meta-all pois-nps.mbtiles ``` The important thing to note is that there is a `pois` layer whose features include a `POITYPE` attribute. While we could add icons for all kinds of `POITYPE` values included in the dataset, we will only add icons for restrooms, trailheads and viewpoints, and leave the rest as an exercise to the reader. The icons need to be extracted with a tool like [Inkscape]({"contact/admin/forURLs"}) because the National Park Service includes all icons in a big vector file. This is outside the scope of this example. ## Adding Icons on Style Load The `.mbtiles` file needs to be added to the assets of the app. When the style loads we can add a ``MLNVectorTileSource`` with as URL `mbtiles://\(Bundle.main.bundlePath)/pois-nps.mbtiles"`. The images need to be added to an imageset so they can be loaded as an `UIImage` and added to the style with ``MLNStyle/setImage:forName:`` as is shown below in the example. Note that you should set ``MLNVectorStyleLayer/sourceLayerIdentifier`` to match the layer name in the MBTiles file. Lastly a [`match` expression]({"contact/admin/forURLs"}) is used to select the correct image based on `POITYPE` attribute present in the feature. ```swift class MultipleImagesExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.tintColor = .darkGray let glacierPoint = CLLocationCoordinate2D(latitude: 37.72836462778902, longitude: -119.57352616838511) mapView.setCenter(glacierPoint, animated: false) mapView.zoomLevel = 12 mapView.delegate = self view.addSubview(mapView) } // Wait until the style is loaded before modifying the map style. func mapView(_: MLNMapView, didFinishLoading style: MLNStyle) { let source = MLNVectorTileSource(identifier: "pois-nps", configurationURL: URL(string: "mbtiles://\(Bundle.main.bundlePath)/pois-nps.mbtiles")!) style.addSource(source) let imagesToAdd = [ ["nps-restrooms", "restrooms"], ["nps-trailhead", "trailhead"], ["nps-viewpoint", "viewpoint"], ] for imageInfo in imagesToAdd { if let imageName = imageInfo.first, let imageKey = imageInfo.last { if let image = UIImage(named: imageName) { style.setImage(image, forName: imageKey) } else { print("Failed to load \(imageName)") return } } } let imageLayer = MLNSymbolStyleLayer(identifier: "npc-poi-images", source: source) imageLayer.sourceLayerIdentifier = "pois" imageLayer.iconImageName = NSExpression(mglJSONObject: [ "match", ["get", "POITYPE"], "Restroom", "restrooms", "Trailhead", "trailhead", "Viewpoint", "viewpoint", "", ]) style.addLayer(imageLayer) } } ``` ![](/overview/sdk/ios-native/img/MultipleImagesExample.png) --- # Observe Low-Level Events https://docs.mapatlas.xyz/overview/sdk/ios-native/ObserverExample # Observe Low-Level Events Learn about the ``MLNMapViewDelegate`` methods for observing map events. > Warning: These methods are not thread-safe. You can observe certain low-level events as they happen. Use these methods to collect metrics or investigate issues during map rendering. This feature is intended primarily for power users. We are always interested in improving observability, so if you have a special use case, feel free to [open an issue or pull request]({"contact/admin/forURLs"}) to extend the types of observability methods. ## Frame Events Observe frame rendering statistics with ``MLNMapViewDelegate/mapViewDidFinishRenderingFrame:fullyRendered:renderingStats:``. ```swift func mapViewDidFinishRenderingFrame(_: MLNMapView, fullyRendered: Bool, renderingStats: MLNRenderingStats) { } ``` See also: ``MLNMapViewDelegate/mapViewDidFinishRenderingFrame:fullyRendered:`` and ``MLNMapViewDelegate/mapViewDidFinishRenderingFrame:fullyRendered:frameEncodingTime:frameRenderingTime:`` ## Shader Events Observe shader compilation with ``MLNMapViewDelegate/mapView:shaderWillCompile:backend:defines:`` and ``MLNMapViewDelegate/mapView:shaderDidCompile:backend:defines:``. ```swift func mapView(_: MLNMapView, shaderWillCompile id: Int, backend: Int, defines: String) { print("A new shader is being compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)") } func mapView(_: MLNMapView, shaderDidCompile id: Int, backend: Int, defines: String) { print("A shader has been compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)") } ``` See also: ``MLNMapViewDelegate/mapView:shaderDidFailCompile:backend:defines:``. ## Glyph Loading Observe glyph loading events with ``MLNMapViewDelegate/mapView:glyphsWillLoad:range:`` and ``MLNMapViewDelegate/mapView:glyphsDidLoad:range:``. ```swift func mapView(_: MLNMapView, glyphsWillLoad fontStack: [String], range: NSRange) { print("Glyphs are being requested for the font stack \(fontStack), ranging from \(range.location) to \(range.location + range.length)") } func mapView(_: MLNMapView, glyphsDidLoad fontStack: [String], range: NSRange) { print("Glyphs have been loaded for the font stack \(fontStack), ranging from \(range.location) to \(range.location + range.length)") } ``` See also: ``MLNMapViewDelegate/mapView:glyphsDidError:range:``. ## Tile Events Monitor tile-related actions using the delegate method ``MLNMapViewDelegate/mapView:tileDidTriggerAction:x:y:z:wrap:overscaledZ:sourceID:`` with the ``MLNTileOperation`` type. ```swift func mapView(_: MLNMapView, tileDidTriggerAction operation: MLNTileOperation, x: Int, y: Int, z: Int, wrap: Int, overscaledZ: Int, sourceID: String) { let tileStr = String(format: "(x: %ld, y: %ld, z: %ld, wrap: %ld, overscaledZ: %ld, sourceID: %@)", x, y, z, wrap, overscaledZ, sourceID) switch operation { case MLNTileOperation.requestedFromCache: print("Requesting tile \(tileStr) from cache") case MLNTileOperation.requestedFromNetwork: print("Requesting tile \(tileStr) from network") case MLNTileOperation.loadFromCache: print("Loading tile \(tileStr), requested from the cache") case MLNTileOperation.loadFromNetwork: print("Loading tile \(tileStr), requested from the network") case MLNTileOperation.startParse: print("Parsing tile \(tileStr)") case MLNTileOperation.endParse: print("Completed parsing tile \(tileStr)") case MLNTileOperation.error: print("An error occured during proccessing for tile \(tileStr)") case MLNTileOperation.cancelled: print("Pending work on tile \(tileStr)") case MLNTileOperation.nullOp: print("An unknown tile operation was emitted for tile \(tileStr)") @unknown default: assertionFailure() } } ``` ## Sprite Loading Observe sprite loading events with ``MLNMapViewDelegate/mapView:spriteWillLoad:url:`` and ``MLNMapViewDelegate/mapView:spriteDidLoad:url:``. ```swift func mapView(_: MLNMapView, spriteWillLoad id: String, url: String) { print("The sprite \(id) has been requested from \(url)") } func mapView(_: MLNMapView, spriteDidLoad id: String, url: String) { print("The sprite \(id) has been loaded from \(url)") } ``` See also: ``MLNMapViewDelegate/mapView:spriteDidError:url:``. --- # PMTiles https://docs.mapatlas.xyz/overview/sdk/ios-native/PMTiles # PMTiles Working with PMTiles Starting MapMetrics iOS 6.10.0, using [PMTiles](https://protomaps.com/docs/pmtiles) as a data source is supported. You can prefix your vector tile source with `pmtiles://` to load a PMTiles file. The rest of the URL continue with be `https://` to load a remote PMTiles file, `asset://` to load an asset or `file://` to load a local PMTiles file. > Note: PMTiles sources currently do not support caching or offline pack downloads. Oliver Wipfli has made a style available that combines a [Protomaps](https://protomaps.com) basemap together with Foursquare's POI dataset. It is available in the [wipfli/foursquare-os-places-pmtiles](https://github.com/wipfli/foursquare-os-places-pmtiles) repository on GitHub. The style to use is ``` https://raw.githubusercontent.com/wipfli/foursquare-os-places-pmtiles/main/style.json ``` The neat thing about this style is that it only uses PMTiles vector sources. PMTiles can be hosted with a relatively simple file server (or file hosting service) instead of a more complex specialized tile server. ![PMTiles Demo](/overview/sdk/ios-native/img/pmtiles-demo.png) --- # POI Along a Route https://docs.mapatlas.xyz/overview/sdk/ios-native/POIAlongRouteExample # POI Along a Route Use an `NSPredicate` to show POI and road labels along a route. > This example uses UIKit. This example adds a dynamically styled GeoJSON route to the map, similar to . However, two existing layers: the `poi` layer and the `road_label` part of the Americana style are adjusted as well. The contents of these layers are shown or hidden, based on whether they lay inside a polygon around the route. In this example, both the route and the area of the polygon along the route are hardcoded. The route is styled with three `MLNLineStyleLayer`s. We make use of the [interpolate expression]({"contact/admin/forURLs"}) to set the widths of these line layers at various zoom levels. ```swift class POIAlongRouteExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.setCenter( CLLocationCoordinate2D(latitude: 45.52214, longitude: -122.63748), zoomLevel: 18, animated: false ) view.addSubview(mapView) mapView.delegate = self } // Wait until the map is loaded before adding to the map. func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) { loadGeoJson() restrictPOIVisibleShape() setCamera() } func loadGeoJson() { DispatchQueue.global().async { // Get the path for example.geojson in the app’s bundle. guard let jsonUrl = Bundle.main.url(forResource: "example", withExtension: "geojson") else { preconditionFailure("Failed to load local GeoJSON file") } guard let jsonData = try? Data(contentsOf: jsonUrl) else { preconditionFailure("Failed to parse GeoJSON file") } DispatchQueue.main.async { self.drawPolyline(geoJson: jsonData) } } } func setCamera() { let camera = mapView.camera camera.heading = 249.37706203842038 camera.pitch = 60 camera.centerCoordinate.latitude = 45.52199780570582 camera.centerCoordinate.longitude = -122.6418837958432 mapView.setCamera(camera, animated: false) mapView.setZoomLevel(15.062187320447523, animated: false) } func drawPolyline(geoJson: Data) { // Add our GeoJSON data to the map as an MLNGeoJSONSource. // We can then reference this data from an MLNStyleLayer. // MLNMapView.style is optional, so you must guard against it not being set. guard let style = mapView.style else { return } guard let shapeFromGeoJSON = try? MLNShape(data: geoJson, encoding: String.Encoding.utf8.rawValue) else { fatalError("Could not generate MLNShape") } let source = MLNShapeSource(identifier: "polyline", shape: shapeFromGeoJSON, options: nil) style.addSource(source) // Create new layer for the line. let layer = MLNLineStyleLayer(identifier: "polyline", source: source) // Set the line join and cap to a rounded end. layer.lineJoin = NSExpression(forConstantValue: "round") layer.lineCap = NSExpression(forConstantValue: "round") // Set the line color to a constant blue color. layer.lineColor = NSExpression(forConstantValue: UIColor(red: 59 / 255, green: 178 / 255, blue: 208 / 255, alpha: 1)) // Use expression to smoothly adjust the line width from 2pt to 20pt between zoom levels 14 and 18. layer.lineWidth = NSExpression(mglJSONObject: ["interpolate", ["linear"], ["zoom"], 14, 2, 18, 20]) // We can also add a second layer that will draw a stroke around the original line. let casingLayer = MLNLineStyleLayer(identifier: "polyline-case", source: source) // Copy these attributes from the main line layer. casingLayer.lineJoin = layer.lineJoin casingLayer.lineCap = layer.lineCap // Line gap width represents the space before the outline begins, so should match the main line’s line width exactly. casingLayer.lineGapWidth = layer.lineWidth // Stroke color slightly darker than the line color. casingLayer.lineColor = NSExpression(forConstantValue: UIColor(red: 41 / 255, green: 145 / 255, blue: 171 / 255, alpha: 1)) // Use expression to gradually increase the stroke width between zoom levels 14 and 18. casingLayer.lineWidth = NSExpression(mglJSONObject: ["interpolate", ["linear"], ["zoom"], 14, 1, 18, 4]) // Just for fun, let’s add another copy of the line with a dash pattern. let dashedLayer = MLNLineStyleLayer(identifier: "polyline-dash", source: source) dashedLayer.lineJoin = layer.lineJoin dashedLayer.lineCap = layer.lineCap dashedLayer.lineColor = NSExpression(forConstantValue: UIColor.white) dashedLayer.lineOpacity = NSExpression(forConstantValue: 0.5) dashedLayer.lineWidth = layer.lineWidth // Dash pattern in the format [dash, gap, dash, gap, ...]. You’ll want to adjust these values based on the line cap style. dashedLayer.lineDashPattern = NSExpression(forConstantValue: [0, 1.5]) guard let poiLayer = mapView.style?.layer(withIdentifier: "poi") as? MLNSymbolStyleLayer else { print("Could not find poi layer") return } style.insertLayer(layer, below: poiLayer) style.insertLayer(dashedLayer, above: layer) style.insertLayer(casingLayer, below: layer) } func restrictPOIVisibleShape() { // find poi-label layer guard let poiLayer = mapView.style?.layer(withIdentifier: "poi") as? MLNSymbolStyleLayer else { print("Could not find poi layer") return } // find road-label layer guard let roadLabelLayer = mapView.style?.layer(withIdentifier: "road_label") as? MLNSymbolStyleLayer else { print("Could not find road_label layer") return } // show the POI and road that is within this polygon let polygonShape = [ [-122.63730626171188, 45.52288837762333], [-122.65455070022612, 45.52299746891552], [-122.65747018755947, 45.52177017968134], [-122.65992255691913, 45.51931552089448], [-122.66015611590598, 45.513696676587045], [-122.66696825301655, 45.51375123117057], [-122.6672018120034, 45.51222368283956], [-122.6571977020749, 45.51225096085216], [-122.6570419960839, 45.51822452705878], [-122.65392787626189, 45.52106106703124], [-122.63567134880579, 45.52114288817623], [-122.63657745074761, 45.52288036393409], [-122.6373404839605, 45.52291377640398], ] // create a polygon class let coordinates = polygonShape.map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } let bufferedRoutePolygon = MLNPolygon(coordinates: coordinates, count: UInt(coordinates.count), interiorPolygons: nil) // apply predicates to these two layers poiLayer.predicate = NSPredicate(format: "SELF IN %@", bufferedRoutePolygon) roadLabelLayer.predicate = NSPredicate(format: "SELF IN %@", bufferedRoutePolygon) } } ``` --- # Plugin Layers https://docs.mapatlas.xyz/overview/sdk/ios-native/PluginLayers # Plugin Layers Plugin Layers are a way to add layer types that render themselves into the style parsing engine at runtime. It is a way to dynamically link new layer types to the MapMetrics core -- and styling language -- without having to compile them into the library itself. Because the layers are bound at runtime, one or more different types of specialized layers can be added and different versions of layer types can be added. Currently Plugin Layers are only available on iOS/Darwin using the Metal Rendering pipeline. ## Creating a Plugin Layer Plugin Layers can be created by creating a descendant from MLNPluginLayer and add the class type to an instance of MLNMapView. The layer is self describing (e.g. the layer type, what properties are available, etc) and that information will be registered with the MapMetrics core. When the plugin layer type is found in the style, the core will automatically instantiate it, pass along initial properties and manage the rendering of the layer. ### Defining a layer's capabilities The newly created layer class should override the layerCapabilites class method. Please note that this is a class method and not an instance method, so make sure it's prefaced with a + and not a -. The object that is returned from this method defines the layer type and the propeties that the layer expects. The triangle example (platform/darwin/app/PluginLayerExampleMetalRendering.mm) defines it's layer type "plugin-layer-metal-rendering" and two paint properties (scale and fill-color). These properties can be expression based. It's important to define the type of property (single float or color) and a default value. The properties returned by layerCapabilities will correspond to any properties in the "paint": part of the style. Initialization properties can also be added to the "properties": section of the layer style. ```objc +(MLNPluginLayerCapabilities *)layerCapabilities { MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init]; tempResult.layerID = @"plugin-layer-metal-rendering"; tempResult.requiresPass3D = YES; // Define the paint properties that this layer implements and // what types they are tempResult.layerProperties = @[ // The scale property [MLNPluginLayerProperty propertyWithName:@"scale" propertyType:MLNPluginLayerPropertyTypeSingleFloat defaultValue:@(1.0)], // The fill color property [MLNPluginLayerProperty propertyWithName:@"fill-color" propertyType:MLNPluginLayerPropertyTypeColor defaultValue:[UIColor blueColor]] ]; return tempResult; } ``` For the triangle example, there are two initialization properties (offset-x and offset-y) for positioning and two paint properties, scale and fill-color. Here are three different instances of the plugin layer being added to the style. One that has a static scale (and uses the default color), one that has an expression based scale (and uses the default scale) and one that has an expression based scale and fill color. ```json { "id": "metal-rendering-layer-1", "type": "plugin-layer-metal-rendering", "properties": { "offset-x": -300, "offset-y": -300 }, "paint": { "scale": 2.5 } }, { "id": "metal-rendering-layer-2", "type": "plugin-layer-metal-rendering", "properties": { "offset-x": -300 }, "paint": { "scale": [ "interpolate", [ "linear" ], [ "zoom" ], 5, 0.5, 15, 3 ] } }, { "id": "metal-rendering-layer-3", "type": "plugin-layer-metal-rendering", "properties": { "color1": "#DDAAFF", "offset-x": 300 }, "paint": { "scale": [ "interpolate", [ "linear" ], [ "zoom" ], 5, 3, 15, 0.5 ], "fill-color": [ "interpolate", [ "linear" ], [ "zoom" ], 1, "#ff0000", 22, "#00ff00" ] } } ``` ![](/overview/sdk/ios-native/img/PluginLayer.png) ## Example 2: GLTF Plugin Layer In [this repository]({"contact/admin/forURLs"}) you can find an example of a plugin layer that allows adding GLTF models. The following JSON is added to the style. ``` { "id": "model-layer", "type": "hudhud::gltf-model-layer", "properties": { "model-source-resource":"models.json" }, "paint": { "scale": 1.0 } } ``` @Video( source: "gltfplugin.mp4", poster: "gltfplugin.png", alt: "GLTF Plugin in action.") { GLTF Plugin showing 3D models of the Arc de Triomphe and the Eifel Tower. } --- # Predicates and expressions https://docs.mapatlas.xyz/overview/sdk/ios-native/Predicates_and_Expressions # Predicates and expressions Using `NSPredicate` with MapMetrics iOS Style layers use predicates and expressions to determine what to display and how to format it. _Predicates_ are represented by the same `NSPredicate` class that filters results from Core Data or items in an `NSArray` in Objective-C. Predicates are based on _expressions_, represented by the `NSExpression` class. Somewhat unusually, style layers also use expressions on their own. This document discusses the specific subset of the predicate and expression syntax supported by this SDK. For a more general introduction to predicates and expressions, consult the _[Predicate Programming Guide]({"contact/admin/forURLs"})_ in Apple developer documentation. For additional detail on how this SDK has extended the `NSExpression` class, see the [`NSExpression+MLNAdditions.h`]({"contact/admin/forURLs"}) header. ## Using predicates to filter vector data Most style layer classes display `MLNFeature` objects that you can show or hide based on the feature’s attributes. Use the `MLNVectorStyleLayer.predicate` property to include only the features in the source layer that satisfy a condition that you define. ### Operators The following comparison operators are supported: `NSPredicateOperatorType` | Format string syntax ----------------------------------------------|--------------------- `NSEqualToPredicateOperatorType` | `key = value`
`key == value` `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value`
`key => value` `NSLessThanOrEqualToPredicateOperatorType` | `key <= value`
`key =< value` `NSGreaterThanPredicateOperatorType` | `key > value` `NSLessThanPredicateOperatorType` | `key < value` `NSNotEqualToPredicateOperatorType` | `key != value`
`key <> value` `NSBetweenPredicateOperatorType` | `key BETWEEN { 32, 212 }` To test whether a feature has or lacks a specific attribute, compare the attribute to `NULL` or `NIL`. Predicates created using the `+[NSPredicate predicateWithValue:]` method are also supported. String operators and custom operators are not supported. The following compound operators are supported: `NSCompoundPredicateType` | Format string syntax --------------------------|--------------------- `NSAndPredicateType` | `predicate1 AND predicate2`
`predicate1 && predicate2` `NSOrPredicateType` | `predicate1 OR predicate2`
predicate1 || predicate2 `NSNotPredicateType` | `NOT predicate`
`!predicate` The following aggregate operators are supported: `NSPredicateOperatorType` | Format string syntax ----------------------------------|--------------------- `NSInPredicateOperatorType` | `key IN { 'iOS', 'macOS', 'tvOS', 'watchOS' }` `NSContainsPredicateOperatorType` | `{ 'iOS', 'macOS', 'tvOS', 'watchOS' } CONTAINS key` You can use the `IN` and `CONTAINS` operators to test whether a value appears in a collection, whether a string is a substring of a larger string, or whether the evaluated feature (`SELF`) lies within a given `MLNShape` or `MLNFeature`. For example, to show one delicious local chain of sandwich shops, but not similarly named steakhouses and pizzerias: ```objc MLNPolygon *cincinnati = [MLNPolygon polygonWithCoordinates:cincinnatiCoordinates count:sizeof(cincinnatiCoordinates) / sizeof(cincinnatiCoordinates[0])]; deliLayer.predicate = [NSPredicate predicateWithFormat:@"class = 'food_and_drink' AND name CONTAINS 'Izzy' AND SELF IN %@", cincinnati]; ``` ```swift let cincinnati = MLNPolygon(coordinates: &cincinnatiCoordinates, count: UInt(cincinnatiCoordinates.count)) deliLayer.predicate = NSPredicate(format: "class = 'food_and_drink' AND name CONTAINS 'Izzy' AND SELF IN %@", cincinnati) ``` The following combinations of comparison operators and modifiers are supported: `NSComparisonPredicateModifier` | `NSPredicateOperatorType` | Format string syntax --------------------------------|-------------------------------------|--------------------- `NSAllPredicateModifier` | `NSNotEqualToPredicateOperatorType` | `ALL haystack != needle` `NSAnyPredicateModifier` | `NSEqualToPredicateOperatorType` | `ANY haystack = needle`
`SOME haystack = needle` The following comparison predicate options are supported for comparison and aggregate operators that are used in the predicate: `NSComparisonPredicateOptions` | Format string syntax ----------------------------------------|--------------------- `NSCaseInsensitivePredicateOption` | `'QUEBEC' =[c] 'Quebec'` `NSDiacriticInsensitivePredicateOption` | `'Québec' =[d] 'Quebec'` Other comparison predicate options are unsupported, namely `l` (for locale sensitivity) and `n` (for normalization). A comparison is locale-sensitive as long as it is case- or diacritic-insensitive. Comparison predicate options are not supported in conjunction with comparison modifiers like `ALL` and `ANY`. ### Operands Operands in predicates can be [variables](#variables), [key paths](#key-paths), or almost anything else that can appear [inside an expression](#using-expressions-to-configure-layout-and-paint-attributes). Automatic type casting is not performed. Therefore, a feature only matches a predicate if its value for the attribute in question is of the same type as the value specified in the predicate. Use the `CAST()` operator to convert a key path or variable into a matching type: * To cast a value to a number, use `CAST(key, 'NSNumber')`. * To cast a value to a string, use `CAST(key, 'NSString')`. * To cast a value to a color, use `CAST(key, 'UIColor')` on iOS and `CAST(key, 'NSColor')` on macOS. * To cast an `NSColor` or `UIColor` object to an array, use `CAST(noindex(color), 'NSArray')`. For details about the predicate format string syntax, consult the “Predicate Format String Syntax” chapter of the _[Predicate Programming Guide]({"contact/admin/forURLs"})_ in Apple developer documentation. ## Using expressions to configure layout and paint attributes An expression can contain subexpressions of various types. Each of the supported types of expressions is discussed below. ### Constant values A constant value can be of any of the following types: In Objective-C | In Swift ----------------------|--------- `NSColor` (macOS)
`UIColor` (iOS) | `NSColor` (macOS)
`UIColor` (iOS) `NSString` | `String` `NSString` | `String` `NSNumber.boolValue` | `NSNumber.boolValue` `NSNumber.doubleValue` | `NSNumber.doubleValue` `NSArray` | `[Float]` `NSArray` | `[String]` `NSValue.CGVectorValue` (iOS)
`NSValue` containing `CGVector` (macOS) | `NSValue.cgVectorValue` (iOS)
`NSValue` containing `CGVector` (macOS) `NSValue.UIEdgeInsetsValue` (iOS)
`NSValue.edgeInsetsValue` (macOS) | `NSValue.uiEdgeInsetsValue` (iOS)
`NSValue.edgeInsetsValue` (macOS) For literal floating-point values, use `-[NSNumber numberWithDouble:]` instead of `-[NSNumber numberWithFloat:]` to avoid precision issues. ### Key paths A key path expression refers to an attribute of the `MLNFeature` object being evaluated for display. For example, if a polygon’s `MLNFeature.attributes` dictionary contains the `floorCount` key, then the key path `floorCount` refers to the value of the `floorCount` attribute when evaluating that particular polygon. The following special attributes are also available on features that are produced as a result of clustering multiple point features together in a shape source: | Attribute | Type | Meaning | |-------------|--------|------------------------------------------------------------------------------------------------------------------------------------------| | cluster | Bool | True if the feature is a point cluster. If the attribute is false (or not present) then the feature should not be considered a cluster. | | cluster_id | Number | Identifier for the point cluster. | | point_count | Number | The number of point features in a given cluster. | Some characters may not be used directly as part of a key path in a format string. For example, if a feature’s attribute is named `ISO 3166-1:2006`, an expression format string of `lowercase(ISO 3166-1:2006)` or a predicate format string of `ISO 3166-1:2006 == 'US-OH'` would raise an exception. Instead, use a `%K` placeholder or the `+[NSExpression expressionForKeyPath:]` initializer: ```objc [NSPredicate predicateWithFormat:@"%K == 'US-OH'", @"ISO 3166-1:2006"]; [NSExpression expressionForFunction:@"lowercase:" arguments:@[[NSExpression expressionForKeyPath:@"ISO 3166-1:2006"]]] ``` ```swift NSPredicate(format: "%K == 'US-OH'", "ISO 3166-1:2006") NSExpression(forFunction: "lowercase:", arguments: [NSExpression(forKeyPath: "ISO 3166-1:2006")]) ``` ### Functions Of the [functions predefined]({"contact/admin/forURLs"}) by the [`+[NSExpression expressionForFunction:arguments:]` method]({"contact/admin/forURLs"}), the following subset is supported in layer attribute values: Initializer parameter | Format string syntax ----------------------|--------------------- `average:` | `average({1, 2, 2, 3, 4, 7, 9})` `sum:` | `sum({1, 2, 2, 3, 4, 7, 9})` `count:` | `count({1, 2, 2, 3, 4, 7, 9})` `min:` | `min({1, 2, 2, 3, 4, 7, 9})` `max:` | `max({1, 2, 2, 3, 4, 7, 9})` `add:to:` | `1 + 2` `from:subtract:` | `2 - 1` `multiply:by:` | `1 * 2` `divide:by:` | `1 / 2` `modulus:by:` | `modulus:by:(1, 2)` `sqrt:` | `sqrt(2)` `log:` | `log(10)` `ln:` | `ln(2)` `raise:toPower:` | `2 ** 2` `exp:` | `exp(0)` `ceiling:` | `ceiling(0.99999)` `abs:` | `abs(-1)` `trunc:` | `trunc(6378.1370)` `floor:` | `floor(-0.99999)` `uppercase:` | `uppercase('Elysian Fields')` `lowercase:` | `lowercase('DOWNTOWN')` `noindex:` | `noindex(0 + 2 + c)` `length:` | `length('Wapakoneta')` `castObject:toType:` | `CAST(ele, 'NSString')`
`CAST(ele, 'NSNumber')` A number of [MapMetrics-specific functions](#MapMetrics-specific-functions) are also available. The following predefined functions are **not** supported: Initializer parameter | Format string syntax ----------------------|--------------------- `median:` | `median({1, 2, 2, 3, 4, 7, 9})` `mode:` | `mode({1, 2, 2, 3, 4, 7, 9})` `stddev:` | `stddev({1, 2, 2, 3, 4, 7, 9})` `random` | `random()` `randomn:` | `randomn(10)` `now` | `now()` `bitwiseAnd:with:` | `bitwiseAnd:with:(5, 3)` `bitwiseOr:with:` | `bitwiseOr:with:(5, 3)` `bitwiseXor:with:` | `bitwiseXor:with:(5, 3)` `leftshift:by:` | `leftshift:by:(23, 1)` `rightshift:by:` | `rightshift:by:(23, 1)` `onesComplement:` | `onesComplement(255)` `distanceToLocation:fromLocation:` | `distanceToLocation:fromLocation:(there, here)` ### Conditionals Conditionals are supported via the built-in `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` method and `TERNARY()` operator. If you need to express multiple cases (“else-if”), you can either nest a conditional within a conditional or use the [`MLN_IF()`](#code-mgl_if-code) or [`MLN_MATCH()`](#code-mgl_match-code) function. ### Aggregates Aggregate expressions can contain arrays of expressions. In some cases, it is possible to use the array itself instead of wrapping the array in an aggregate expression. ### Variables The following variables are defined by this SDK for use with style layers: | Variable | Type | Meaning | | --- | --- | --- | | `$featureIdentifier` | Any GeoJSON data type | A value that uniquely identifies the feature in the containing source. This variable corresponds to the `NSExpression.featureIdentifierVariableExpression` property. | | `$geometryType` | String | The type of geometry represented by the feature. A feature’s type is one of the following strings:

* `Point` for point features, corresponding to the `MLNPointAnnotation` class
* `LineString` for polyline features, corresponding to the ``MLNPolyline`` class
* `Polygon` for polygon features, corresponding to the ``MLNPolygon`` class

This variable corresponds to the `NSExpression.geometryTypeVariableExpression` property. | | `$heatmapDensity` | Number | The [kernel density estimation]({"contact/admin/forURLs"}) of a screen point in a heatmap layer; in other words, a relative measure of how many data points are crowded around a particular pixel. This variable can only be used with the `heatmapColor` property. This variable corresponds to the `NSExpression.heatmapDensityVariableExpression` property. | | `$zoomLevel` | Number | The current zoom level. In style layout and paint properties, this variable may only appear as the target of a top-level interpolation or step expression. This variable corresponds to the `NSExpression.zoomLevelVariableExpression` property. | | `$lineProgress` | Number | A number that indicates the relative distance along a line at a given point along the line. This variable evaluates to 0 at the beginning of the line and 1 at the end of the line. It can only be used with the ``MLNLineStyleLayer/lineGradient`` property. It corresponds to the `NSExpression.lineProgressVariableExpression` property. | In addition to these variables, you can define your own variables and refer to them elsewhere in the expression. The syntax for defining a variable makes use of a [MapMetrics-specific function](#MapMetrics-specific-functions) that takes an `NSDictionary` as an argument: ```objc [NSExpression expressionWithFormat:@"MLN_LET('floorCount', 2, $floorCount + 1)"]; ``` ```swift NSExpression(format: "MLN_LET(floorCount, 2, $floorCount + 1)") ``` ## MapMetrics-specific functions > Warning: Due to a change in iOS 15.5, some of these stopped working. See [#331]({"contact/admin/forURLs"}) for more information and workarounds. For compatibility with the MapMetrics Style Spec, the following functions are defined by this SDK. When setting a style layer property, you can call these functions just like the predefined functions above, using either the `+[NSExpression expressionForFunction:arguments:]` method or a convenient format string syntax: ### mgl_does:have: **Selector:** `mgl_does:have:` **Format string syntax:** `mgl_does:have:(SELF, '🧀🍔')` or `mgl_does:have:(%@, '🧀🍔')` Returns a Boolean value indicating whether the dictionary has a value for the key or whether the evaluated object (`SELF`) has a value for the feature attribute. Compared to the [`mgl_has:`](#code-mgl_has-code) custom function, that function's target is instead passed in as the first argument to this function. Both functions are equivalent to the syntax `key != NIL` or `%@[key] != NIL` but can be used outside of a predicate. ### mgl_interpolate:withCurveType:parameters:stops: **Selector:** `mgl_interpolate:withCurveType:parameters:stops:` **Format string syntax:** `mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)` Produces continuous, smooth results by interpolating between pairs of input and output values ("stops"). Compared to the [`mgl_interpolateWithCurveType:parameters:stops:`](#code-mgl_interpolatewithcurvetype-parameters-stops-code) custom function, the input expression (that function's target) is instead passed in as the first argument to this function. ### mgl_step:from:stops: **Selector:** `mgl_step:from:stops:` **Format string syntax:** `mgl_step:from:stops:(x, 11, %@)` Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of input and output values ("stops"). Compared to the [`mgl_stepWithMinimum:stops:`](#code-mgl_stepwithminimum-stops-code) custom function, the input expression (that function's target) is instead passed in as the first argument to this function. ### mgl_join: **Selector:** `mgl_join:` **Format string syntax:** `mgl_join({'Old', 'MacDonald'})` Returns the result of concatenating together all the elements of an array in order. Compared to the [`stringByAppendingString:`](#code-stringbyappendingstring-code) custom function, this function takes only one argument, which is an aggregate expression containing the strings to concatenate. ### mgl_acos: **Selector:** `mgl_acos:` **Format string syntax:** `mgl_acos(1)` Returns the arccosine of the number. This function corresponds to the [`acos`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_asin: **Selector:** `mgl_asin:` **Format string syntax:** `mgl_asin(0)` Returns the arcsine of the number. This function corresponds to the [`asin`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_atan: **Selector:** `mgl_atan:` **Format string syntax:** `mgl_atan(20)` Returns the arctangent of the number. This function corresponds to the [`atan`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_cos: **Selector:** `mgl_cos:` **Format string syntax:** `mgl_cos(0)` Returns the cosine of the number. This function corresponds to the [`cos`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_log2: **Selector:** `mgl_log2:` **Format string syntax:** `mgl_log2(1024)` Returns the base-2 logarithm of the number. This function corresponds to the [`log2`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_round: **Selector:** `mgl_round:` **Format string syntax:** `mgl_round(1.5)` Returns the number rounded to the nearest integer. If the number is halfway between two integers, this function rounds it away from zero. This function corresponds to the [`round`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_sin: **Selector:** `mgl_sin:` **Format string syntax:** `mgl_sin(0)` Returns the sine of the number. This function corresponds to the [`sin`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_tan: **Selector:** `mgl_tan:` **Format string syntax:** `mgl_tan(0)` Returns the tangent of the number. This function corresponds to the [`tan`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_distanceFrom: **Selector:** `mgl_distanceFrom:` **Format string syntax:** `mgl_distanceFrom(%@)` with an `MLNShape` Returns the straight-line distance from the evaluated object to the given shape. This function corresponds to the [`distance`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_coalesce: **Selector:** `mgl_coalesce:` **Format string syntax:** `mgl_coalesce({x, y, z})` Returns the first non-`nil` value from an array of expressions. This function corresponds to the [`coalesce`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### mgl_attributed: **Selector:** `mgl_attributed:` **Format string syntax:** `mgl_attributed({x, y, z})` Concatenates and returns the array of `MLNAttributedExpression` objects, for use with the `MLNSymbolStyleLayer.text` property. `MLNAttributedExpression.attributes` valid attributes. Key | Value Type --- | --- `MLNFontNamesAttribute` | An `NSExpression` evaluating to an `NSString` array. `MLNFontScaleAttribute` | An `NSExpression` evaluating to an `NSNumber` value. `MLNFontColorAttribute` | An `NSExpression` evaluating to an `UIColor` (iOS) or `NSColor` (macOS). This function corresponds to the [`format`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### MLN_LET **Selector:** `MLN_LET:` **Format string syntax:** `MLN_LET('age', uppercase('old'), 'name', uppercase('MacDonald'), mgl_join({$age, $name}))` **Arguments:** Any number of variable names interspersed with their assigned `NSExpression` values, followed by an `NSExpression` that may contain references to those variables. Returns the result of evaluating an expression with the given variable values. Compared to the [`mgl_expressionWithContext:`](#code-mgl_expressionwithcontext-code) custom function, this function takes the variable names and values inline before the expression that contains references to those variables. ### MLN_MATCH **Selector:** `MLN_MATCH:` **Format string syntax:** `MLN_MATCH(x, 0, 'zero match', 1, 'one match', 2, 'two match', 'default')` **Arguments:** An input expression, then any number of argument pairs, followed by a default expression. Each argument pair consists of a constant value followed by an expression to produce as a result of matching that constant value. If the input value is an aggregate expression, then any of the constant values within that aggregate expression result in the following argument. This is shorthand for specifying an argument pair for each of the constant values within that aggregate expression. It is not possible to match the aggregate expression itself. Returns the result of matching the input expression against the given constant values. This function corresponds to the `+[NSExpression(MLNAdditions) mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]` method and the [`match`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### MLN_IF **Selector:** `MLN_IF:` **Format string syntax:** `MLN_IF(1 = 2, YES, 2 = 2, YES, NO)` **Arguments:** Alternating `NSPredicate` conditionals and resulting expressions, followed by a default expression. Returns the first expression that meets the condition; otherwise, the default value. Unlike `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` or the `TERNARY()` syntax, this function can accept multiple "if else" conditions and is supported on iOS 8._x_ and macOS 10.10._x_; however, each conditional passed into this function must be wrapped in a constant expression. This function corresponds to the `+[NSExpression(MLNAdditions) mgl_expressionForConditional:trueExpression:falseExpresssion:]` method and the [`case`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### MLN_FUNCTION **Selector:** `MLN_FUNCTION:` **Format string syntax:** `MLN_FUNCTION('typeof', mystery)` **Arguments:** Any arguments required by the expression operator. An expression exactly as defined by the [MapMetrics Style Spec]({"contact/admin/forURLs"}). ## Custom functions The following custom functions are also available with the `+[NSExpression expressionForFunction:selectorName:arguments:]` method or the `FUNCTION()` format string syntax. Some of these functions are defined as methods on their respective target classes, but you should not call them directly outside the context of an expression, because the result may differ from the evaluated expression's result or may result in undefined behavior. The MapMetrics Style Spec defines some operators for which no custom function is available. To use these operators in an `NSExpression`, call the [`MLN_FUNCTION()`](#code-mgl_function-code) function with the same arguments that the operator expects. ### boolValue **Selector:** `boolValue` **Format string syntax:** `FUNCTION(1, 'boolValue')` **Target:** An `NSExpression` that evaluates to a number or string. **Arguments:** None. A Boolean representation of the target: `FALSE` when then input is an empty string, 0, `FALSE`, `NIL`, or `NaN`, otherwise `TRUE`. ### mgl_has: **Selector:** `mgl_has:` **Format string syntax:** `FUNCTION($featureAttributes, 'mgl_has:', '🧀🍔')` **Target:** An `NSExpression` that evaluates to an `NSDictionary` or the evaluated object (`SELF`). **Arguments:** An `NSExpression` that evaluates to an `NSString` representing the key to look up in the dictionary or the feature attribute to look up in the evaluated object (see `MLNFeature.attributes`). `true` if the dictionary has a value for the key or if the evaluated object has a value for the feature attribute. This function corresponds to the [`has`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. See also the [`mgl_does:have:`](#code-mgl_does-have-code) function, which is used on its own without the `FUNCTION()` operator. You can also check whether an object has an attribute by comparing the key path to `NIL`, for example `cheeseburger != NIL` or `burger.cheese != NIL` ### mgl_expressionWithContext: **Selector:** `mgl_expressionWithContext:` **Format string syntax:** `FUNCTION($ios + $macos, 'mgl_expressionWithContext:', %@)` with a dictionary containing `ios` and `macos` keys **Target:** An `NSExpression` that may contain references to the variables defined in the context dictionary. **Arguments:** An `NSDictionary` with `NSString`s as keys and `NSExpression`s as values. Each key is a variable name and each value is the variable's value within the target expression. The target expression with variable subexpressions replaced with the values defined in the context dictionary. This function corresponds to the [`let`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. See also the [`MLN_LET`](#code-mgl_let-code) function, which is used on its own without the `FUNCTION()` operator. ### mgl_interpolateWithCurveType:parameters:stops: **Selector:** `mgl_interpolateWithCurveType:parameters:stops:` **Format string syntax:** `FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', NIL, %@)` with a dictionary containing zoom levels or other constant values as keys **Target:** An `NSExpression` that evaluates to a number and contains a variable or key path expression. **Arguments:** The first argument is one of the following strings denoting curve types: `linear`, `exponential`, or `cubic-bezier`. The second argument is an expression providing parameters for the curve: * If the curve type is `linear`, the argument is `NIL`. * If the curve type is `exponential`, the argument is an expression that evaluates to a number, specifying the base of the exponential interpolation. * If the curve type is `cubic-bezier`, the argument is an array or aggregate expression containing four expressions, each evaluating to a number. The four numbers are control points for the cubic Bézier curve. The third argument is an `NSDictionary` object representing the interpolation's stops, with numeric zoom levels as keys and expressions as values. A value interpolated along the continuous mathematical function defined by the arguments, with the target as the input to the function. The input expression is matched against the keys in the stop dictionary. The keys may be feature attribute values, zoom levels, or heatmap densities. The values may be constant values or `NSExpression` objects. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. This function corresponds to the `+[NSExpression(MLNAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` method and the [`interpolate`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. See also the [`mgl_interpolate:withCurveType:parameters:stops:`](#code-mgl_interpolate-withcurvetype-parameters-stops-code) function, which is used on its own without the `FUNCTION()` operator. ### mgl_numberWithFallbackValues: **Selector:** `mgl_numberWithFallbackValues:`, `doubleValue`, `floatValue`, or `decimalValue` **Format string syntax:** `FUNCTION(ele, 'mgl_numberWithFallbackValues:', 0)` **Target:** An `NSExpression` that evaluates to a Boolean value, number, or string. **Arguments:** Zero or more `NSExpression`s, each evaluating to a Boolean value or string. A numeric representation of the target: * If the target is `NIL` or `FALSE`, the result is 0. * If the target is true, the result is 1. * If the target is a string, it is converted to a number as specified by the **Selector:** `mgl_numberWithFallbackValues:`, `doubleValue`, `floatValue`, or `decimalValue` **Format string syntax:** `FUNCTION(ele, 'mgl_numberWithFallbackValues:', 0)` **Target:** An `NSExpression` that evaluates to a Boolean value, number, or string. **Arguments:** Zero or more `NSExpression`s, each evaluating to a Boolean value or string. A numeric representation of the target: * If the target is `NIL` or `FALSE`, the result is 0. * If the target is true, the result is 1. * If the target is a string, it is converted to a number as specified by the "[ToNumber Applied to the String Type]({"contact/admin/forURLs"})" algorithm of the ECMAScript Language Specification. * If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained. This function corresponds to the [`to-number`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. You can also cast a value to a number by passing the value and the string `NSNumber` into the `CAST()` operator. ### mgl_stepWithMinimum:stops: **Selector:** `mgl_stepWithMinimum:stops:` **Format string syntax:** `FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', 0, %@)` with a dictionary with zoom levels or other constant values as keys **Target:** An `NSExpression` that evaluates to a number and contains a variable or key path expression. **Arguments:** The first argument is an expression that evaluates to a number, specifying the minimum value in case the target is less than any of the stops in the second argument. The second argument is an `NSDictionary` object representing the interpolation's stops, with numeric zoom levels as keys and expressions as values. The output value of the stop whose key is just less than the evaluated target, or the minimum value if the target is less than the least of the stops' keys. The input expression is matched against the keys in the stop dictionary. The keys may be feature attribute values, zoom levels, or heatmap densities. The values may be constant values or `NSExpression` objects. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. This function corresponds to the `+[NSExpression(MLNAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]` method and the [`step`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. ### stringByAppendingString: **Selector:** `stringByAppendingString:` **Format string syntax:** `FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')` **Target:** An `NSExpression` that evaluates to a string. **Arguments:** One or more `NSExpression`s, each evaluating to a string. The target string with each of the argument strings appended in order. This function corresponds to the `-[NSExpression(MLNAdditions) mgl_expressionByAppendingExpression:]` method and is similar to the [`concat`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. See also the [`mgl_join:`](#code-mgl_join-code) function, which concatenates multiple expressions and is used on its own without the `FUNCTION()` operator. ### stringValue **Selector:** `stringValue` **Format string syntax:** `FUNCTION(ele, 'stringValue')` **Target:** An `NSExpression` that evaluates to a Boolean value, number, or string. **Arguments:** None. A string representation of the target: * If the target is NIL, the result is the empty string. * If the target is a Boolean value, the result is the string `true` or `false`. * If the target is a number, it is converted to a string as specified by the “[NumberToString]({"contact/admin/forURLs"})” algorithm of the ECMAScript Language Specification. * If the target is a color, it is converted to a string of the form `rgba(r,g,b,a)`, where r, g, and b are numerals ranging from 0 to 255 and a ranges from 0 to 1. * Otherwise, the target is converted to a string in the format specified by the [`JSON.stringify()`]({"contact/admin/forURLs"}) function of the ECMAScript Language Specification. This function corresponds to the [`to-string`]({"contact/admin/forURLs"}) operator in the MapMetrics Style Spec. You can also cast a value to a string by passing the value and the string `NSString` into the `CAST()` operator. --- # Rendering Statistics HUD https://docs.mapatlas.xyz/overview/sdk/ios-native/RenderingStatisticsHud # Rendering Statistics HUD Show rendering statistics on the map Enable the rendering HUD with: ```swift mapView.enableRenderingStatsView(true) ``` ![](/overview/sdk/ios-native/img/RenderingStatistics.png) --- # Making Snapshots https://docs.mapatlas.xyz/overview/sdk/ios-native/StaticSnapshotExample # Making Snapshots Use ``MLNMapSnapshotter`` to create snapshots of the map. > This example uses UIKit. This example demonstrates how snapshots can be made of the map. The result is an `UIImage` that you can let the user save to their camera roll or share with a friend. In this demo, the resulting image is shown back with an `UIImageView`. Attribution is read from the sources and added to the snapshot (when applicable). By default a MapMetrics logo is included on the image, but it is not required to show it. You can use ``MLNMapSnapshotOptions/showsLogo`` to configure whether to include the logo. Check with your tile provider if you need to show their logo. ```swift class StaticSnapshotExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! var button: UIButton! var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height / 2), styleURL: AMERICANA_STYLE) mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth] // Center map on the Giza Pyramid Complex in Egypt. let center = CLLocationCoordinate2D(latitude: 29.9773, longitude: 31.1325) mapView.setCenter(center, zoomLevel: 14, animated: false) view.addSubview(mapView) // Create a button to take a map snapshot. button = UIButton(frame: CGRect(x: mapView.bounds.width / 2 - 40, y: mapView.bounds.height - 40, width: 80, height: 30)) button.layer.cornerRadius = 15 button.backgroundColor = UIColor(red: 0.96, green: 0.65, blue: 0.14, alpha: 1.0) button.setImage(UIImage(named: "camera"), for: .normal) button.addTarget(self, action: #selector(createSnapshot), for: .touchUpInside) view.addSubview(button) // Create a UIImageView that will store the map snapshot. imageView = UIImageView(frame: CGRect(x: 0, y: view.bounds.height / 2, width: view.bounds.width, height: view.bounds.height / 2)) imageView.backgroundColor = .black imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(imageView) } @objc func createSnapshot() { // Use the map's style, camera, size, and zoom level to set the snapshot's options. let options = MLNMapSnapshotOptions(styleURL: mapView.styleURL, camera: mapView.camera, size: mapView.bounds.size) options.zoomLevel = mapView.zoomLevel // Add an activity indicator to show that the snapshot is loading. let indicator = UIActivityIndicatorView(frame: CGRect(x: imageView.center.x - 30, y: imageView.center.y - 30, width: 60, height: 60)) view.addSubview(indicator) indicator.startAnimating() // Create the map snapshot. let snapshotter: MLNMapSnapshotter? = MLNMapSnapshotter(options: options) snapshotter?.start { snapshot, error in if error != nil { print("Unable to create a map snapshot.") } else if let snapshot { // Add the map snapshot's image to the image view. indicator.stopAnimating() self.imageView.image = snapshot.image } } } } ``` --- # Tile URL Templates https://docs.mapatlas.xyz/overview/sdk/ios-native/Tile_URL_Templates # Tile URL Templates Using URL Templates when defining tile sources ``MLNTileSource`` objects, specifically ``MLNRasterTileSource`` and ``MLNVectorTileSource`` objects, can be created using an initializer that accepts an array of tile URL templates. Tile URL templates are strings that specify the URLs of the vector tiles or raster tile images to load. A template resembles an absolute URL, but with any number of placeholder strings that the source evaluates based on the tile it needs to load. For example: * `{"contact/admin/forURLs"} could be evaluated as `{"contact/admin/forURLs"} * `{"contact/admin/forURLs"} could be evaluated as `{"contact/admin/forURLs"} Tile URL templates are also used to define tilesets in TileJSON manifests or [`raster`]({"contact/admin/forURLs"}) and [`vector`]({"contact/admin/forURLs"}) sources in style JSON files. See the [TileJSON specification]({"contact/admin/forURLs"}) for information about tile URL templates in the context of a TileJSON or style JSON file. Tile sources support the following placeholder strings in tile URL templates, all of which are optional: | Placeholder string | Description | | --- | --- | | `{x}` | The index of the tile along the map’s x axis according to Spherical Mercator projection. If the value is 0, the tile’s left edge corresponds to the 180th meridian west. If the value is 2^z−1, the tile’s right edge corresponds to the 180th meridian east. | | `{y}` | The index of the tile along the map’s y axis according to Spherical Mercator projection. If the value is 0, the tile’s tile edge corresponds to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value is 2^z−1, the tile’s bottom edge corresponds to −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is inverted if the options parameter contains ``MLNTileSourceOptionTileCoordinateSystem`` with a value of ``MLNTileCoordinateSystem/MLNTileCoordinateSystemTMS``. | | `{z}` | The tile’s zoom level. At zoom level 0, each tile covers the entire world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, 1⁄16 of the world, and so on. For tiles loaded by a ``MLNRasterTileSource`` object, whether the tile zoom level matches the map’s current zoom level depends on the value of the source’s tile size as specified in the ``MLNTileSourceOptionTileSize`` key of the options parameter. | | `{bbox-epsg-3857}` | The tile's bounding box, expressed as a comma-separated list of the tile's western, southern, eastern, and northern extents according to Spherical Mercator (EPSG:3857) projection. The bounding box is typically used with map services conforming to the Web Map Service protocol. | | `{quadkey}` | A quadkey indicating both the tile's location and its zoom level. The quadkey is typically used with Bing Maps. | | `{ratio}` | A suffix indicating the resolution of the tile image. The suffix is the empty string for standard resolution displays and `@2x` for Retina displays, including displays for which `UIScreen.scale` is 3. | | `{prefix}` | Two hexadecimal digits chosen such that each visible tile has a different prefix. The prefix is typically used for domain sharding. | For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, refer to the tile URL template documentation. --- # Showing data from an API https://docs.mapatlas.xyz/overview/sdk/ios-native/WebAPIDataExample # Showing data from an API Showing data from an API with custom styling and interaction > Note: This example uses UIKit. This example loads lighthouses in the United States from [WikiData]({"contact/admin/forURLs"}). It adds points to the map and applies dynamic styling to these points. When zooming in the dots become larger circles with a custom icon and the name of the lighthouse shown next to it. When tapping a callout is shown with the name of the lighthouse that was tapped on. ```swift class WebAPIDataExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView(frame: view.bounds) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.setCenter(CLLocationCoordinate2D(latitude: 37.090240, longitude: -95.712891), zoomLevel: 2, animated: false) mapView.delegate = self mapView.attributionButton.isHidden = true view.addSubview(mapView) // Add a single tap gesture recognizer. This gesture requires the built-in MLNMapView tap gestures (such as those for zoom and annotation selection) to fail. let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(sender:))) for recognizer in mapView.gestureRecognizers! where recognizer is UITapGestureRecognizer { singleTap.require(toFail: recognizer) } mapView.addGestureRecognizer(singleTap) } func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) { fetchPoints { [weak self] features in self?.addItemsToMap(features: features) } } func addItemsToMap(features: [MLNPointFeature]) { // MLNMapView.style is optional, so you must guard against it not being set. guard let style = mapView.style else { return } // You can add custom UIImages to the map style. // These can be referenced by an MLNSymbolStyleLayer’s iconImage property. style.setImage(UIImage(named: "lighthouse")!, forName: "lighthouse") // Add the features to the map as a shape source. let source = MLNShapeSource(identifier: "us-lighthouses", features: features, options: nil) style.addSource(source) let lighthouseColor = UIColor(red: 0.08, green: 0.44, blue: 0.96, alpha: 1.0) // Use MLNCircleStyleLayer to represent the points with simple circles. // In this case, we can use style functions to gradually change properties between zoom level 2 and 7: the circle opacity from 50% to 100% and the circle radius from 2pt to 3pt. let circles = MLNCircleStyleLayer(identifier: "lighthouse-circles", source: source) circles.circleColor = NSExpression(forConstantValue: lighthouseColor) // The circles should increase in opacity from 0.5 to 1 based on zoom level. circles.circleOpacity = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [2: 0.5, 7: 1])) circles.circleRadius = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [2: 2, 7: 3])) // Use MLNSymbolStyleLayer for more complex styling of points including custom icons and text rendering. let symbols = MLNSymbolStyleLayer(identifier: "lighthouse-symbols", source: source) symbols.iconImageName = NSExpression(forConstantValue: "lighthouse") symbols.iconColor = NSExpression(forConstantValue: lighthouseColor) symbols.iconScale = NSExpression(forConstantValue: 0.5) symbols.iconHaloColor = NSExpression(forConstantValue: UIColor.white.withAlphaComponent(0.5)) symbols.iconHaloWidth = NSExpression(forConstantValue: 1) symbols.iconOpacity = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [5.9: 0, 6: 1])) // "name" references the "name" key in an MLNPointFeature’s attributes dictionary. symbols.text = NSExpression(forKeyPath: "name") symbols.textColor = symbols.iconColor symbols.textFontSize = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [10: 10, 16: 16])) symbols.textTranslation = NSExpression(forConstantValue: NSValue(cgVector: CGVector(dx: 15, dy: 0))) symbols.textOpacity = symbols.iconOpacity symbols.textHaloColor = symbols.iconHaloColor symbols.textHaloWidth = symbols.iconHaloWidth symbols.textJustification = NSExpression(forConstantValue: NSValue(mlnTextJustification: .left)) symbols.textAnchor = NSExpression(forConstantValue: NSValue(mlnTextAnchor: .left)) style.addLayer(circles) style.addLayer(symbols) } // MARK: - Feature interaction @IBAction func handleMapTap(sender: UITapGestureRecognizer) { if sender.state == .ended { // Limit feature selection to just the following layer identifiers. let layerIdentifiers: Set = ["lighthouse-symbols", "lighthouse-circles"] // Try matching the exact point first. let point = sender.location(in: sender.view!) for feature in mapView.visibleFeatures(at: point, styleLayerIdentifiers: layerIdentifiers) where feature is MLNPointFeature { guard let selectedFeature = feature as? MLNPointFeature else { fatalError("Failed to cast selected feature as MLNPointFeature") } showCallout(feature: selectedFeature) return } let touchCoordinate = mapView.convert(point, toCoordinateFrom: sender.view!) let touchLocation = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude) // Otherwise, get all features within a rect the size of a touch (44x44). let touchRect = CGRect(origin: point, size: .zero).insetBy(dx: -22.0, dy: -22.0) let possibleFeatures = mapView.visibleFeatures(in: touchRect, styleLayerIdentifiers: Set(layerIdentifiers)).filter { $0 is MLNPointFeature } // Select the closest feature to the touch center. let closestFeatures = possibleFeatures.sorted(by: { CLLocation(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude).distance(from: touchLocation) < CLLocation(latitude: $1.coordinate.latitude, longitude: $1.coordinate.longitude).distance(from: touchLocation) }) if let feature = closestFeatures.first { guard let closestFeature = feature as? MLNPointFeature else { fatalError("Failed to cast selected feature as MLNPointFeature") } showCallout(feature: closestFeature) return } // If no features were found, deselect the selected annotation, if any. mapView.deselectAnnotation(mapView.selectedAnnotations.first, animated: true) } } func showCallout(feature: MLNPointFeature) { let point = MLNPointFeature() point.title = feature.attributes["name"] as? String point.coordinate = feature.coordinate // Selecting an feature that doesn’t already exist on the map will add a new annotation view. // We’ll need to use the map’s delegate methods to add an empty annotation view and remove it when we’re done selecting it. mapView.selectAnnotation(point, animated: true, completionHandler: nil) } // MARK: - MLNMapViewDelegate func mapView(_: MLNMapView, annotationCanShowCallout _: MLNAnnotation) -> Bool { true } func mapView(_ mapView: MLNMapView, didDeselect annotation: MLNAnnotation) { mapView.removeAnnotations([annotation]) } func mapView(_: MLNMapView, viewFor _: MLNAnnotation) -> MLNAnnotationView? { // Create an empty view annotation. Set a frame to offset the callout. MLNAnnotationView(frame: CGRect(x: 0, y: 0, width: 20, height: 20)) } // MARK: - Data fetching and parsing func fetchPoints(withCompletion completion: @escaping (([MLNPointFeature]) -> Void)) { // Wikidata query for all lighthouses in the United States: {"contact/admin/forURLs"} let query = "SELECT DISTINCT ?item " + "?itemLabel ?coor ?image " + "WHERE " + "{ " + "?item wdt:P31 wd:Q39715 . " + "?item wdt:P17 wd:Q30 . " + "?item wdt:P625 ?coor . " + "OPTIONAL { ?item wdt:P18 ?image } . " + "SERVICE wikibase:label { bd:serviceParam wikibase:language \"en\" } " + "} " + "ORDER BY ?itemLabel" let characterSet = NSMutableCharacterSet() characterSet.formUnion(with: CharacterSet.urlQueryAllowed) characterSet.removeCharacters(in: "?") characterSet.removeCharacters(in: "&") characterSet.removeCharacters(in: ":") let encodedQuery = query.addingPercentEncoding(withAllowedCharacters: characterSet as CharacterSet)! let request = URLRequest(url: URL(string: "{"contact/admin/forURLs"})&format=json")!) URLSession.shared.dataTask(with: request, completionHandler: { data, _, error in guard error == nil else { preconditionFailure("Failed to load GeoJSON data: \(error!)") } guard let data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject], let results = json["results"] as? [String: AnyObject], let items = results["bindings"] as? [[String: AnyObject]] else { preconditionFailure("Failed to parse GeoJSON data") } DispatchQueue.main.async { completion(self.parseJSONItems(items: items)) } }).resume() } func parseJSONItems(items: [[String: AnyObject]]) -> [MLNPointFeature] { var features = [MLNPointFeature]() for item in items { guard let label = item["itemLabel"] as? [String: AnyObject], let title = label["value"] as? String else { continue } guard let coor = item["coor"] as? [String: AnyObject], let point = coor["value"] as? String else { continue } let parsedPoint = point.replacingOccurrences(of: "Point(", with: "").replacingOccurrences(of: ")", with: "") let pointComponents = parsedPoint.components(separatedBy: " ") let coordinate = CLLocationCoordinate2D(latitude: Double(pointComponents[1])!, longitude: Double(pointComponents[0])!) let feature = MLNPointFeature() feature.coordinate = coordinate feature.title = title // A feature’s attributes can used by runtime styling for things like text labels. feature.attributes = [ "name": title, ] features.append(feature) } return features } } ``` ![](/overview/sdk/ios-native/img/WebAPIDataExample.png) --- # mapmetrics-ios-Map-add markers https://docs.mapatlas.xyz/overview/sdk/ios-native/mapmetrics-ios-Map-add markers ## 📍 MapMetrics iOS Map Features Guide This guide covers how to: Add interactive markers ## 🔧 Prerequisites Xcode installed (version 13 or newer recommended) A new or existing iOS project Add MapMetrics SDK to your project (via SPM or manual integration) ### Example via Swift Package Manager: : https://github.com/MapMetrics/MapMetrics-iOS ## Setting Up the Map In your ViewController.swift: ```swift class ViewController: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! var selectedAnnotation: MLNPointAnnotation? var isMarkerSelected = false override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView( frame: view.bounds, styleURL: URL(string: "") ) mapView.delegate = self view.addSubview(mapView) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.delegate = self // This is a better starting position let center = CLLocationCoordinate2D(latitude: 20.0, longitude: 0.0) // More centered globally mapView.setCenter(center, zoomLevel: 2, animated: false) // Lower zoom to see more area view.addSubview(mapView) } } ``` ## Add Tap-to-Drop Marker with Editable Title ```swift override func viewDidLoad() { ... let tapGesture = UITapGestureRecognizer(target: self, action: #selector(mapTapped(_:))) mapView.addGestureRecognizer(tapGesture) } @objc func mapTapped(_ sender: UITapGestureRecognizer) { let location = sender.location(in: mapView) let coordinate = mapView.convert(location, toCoordinateFrom: mapView) let marker = MLNPointAnnotation() marker.coordinate = coordinate marker.title = "Tap to add a name" mapView.addAnnotation(marker) } ``` ### Add support for selecting and editing markers: ```swift func mapView(_ mapView: MLNMapView, didSelect annotation: MLNAnnotation) { guard let point = annotation as? MLNPointAnnotation else { return } selectedAnnotation = point } // Handle tap gesture on the map @objc func mapTapped(_ sender: UITapGestureRecognizer) { // Only add a marker if no marker is currently selected guard !isMarkerSelected else { return } let location = sender.location(in: mapView) let coordinates = mapView.convert(location, toCoordinateFrom: mapView) addMarker(at: coordinates) } // Add marker to the map at specific coordinates func addMarker(at coordinates: CLLocationCoordinate2D) { let marker = MLNPointAnnotation() marker.coordinate = coordinates marker.title = "Tap to add a name" // Default title // Add the marker to the map mapView.addAnnotation(marker) } // Handle deselecting a marker (hide info view) func mapView(_ mapView: MLNMapView, didDeselect annotation: MLNAnnotation) { if annotation === selectedAnnotation { isMarkerSelected = false selectedAnnotation = nil } } // MARK: - UITextFieldDelegate extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { // When the user presses "done", update the marker's title with the text entered if let selectedAnnotation = selectedAnnotation, let newName = textField.text, !newName.isEmpty { selectedAnnotation.title = newName } return true } } ``` --- # mapmetrics-ios-Map-add-Clusters https://docs.mapatlas.xyz/overview/sdk/ios-native/mapmetrics-ios-Map-add-Clusters ## 📍 MapMetrics iOS Map Features Guide This guide covers how to: Add Clusters ## 🔧 Prerequisites Xcode installed (version 13 or newer recommended) A new or existing iOS project Add MapMetrics SDK to your project (via SPM or manual integration) ### Example via Swift Package Manager: : https://github.com/MapMetrics/MapMetrics-iOS ## 1️⃣ Setting Up the Map In your ViewController.swift: ```swift class ViewController: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! var selectedAnnotation: MLNPointAnnotation? var isMarkerSelected = false override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView( frame: view.bounds, styleURL: URL(string: "") ) mapView.delegate = self view.addSubview(mapView) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.delegate = self // This is a better starting position let center = CLLocationCoordinate2D(latitude: 20.0, longitude: 0.0) // More centered globally mapView.setCenter(center, zoomLevel: 2, animated: false) // Lower zoom to see more area view.addSubview(mapView) } } ``` ## Displaying Clusters from GeoJSON ###Step 1: Create the source ```swift let url = URL(string: "https://cdn.mapmetrics-atlas.net/Images/heatmap.geojson")! let source = MLNShapeSource( identifier: "clusteredEarthquakes", url: url, options: [ .clustered: true, .clusterRadius: 30 ] ) style.addSource(source) ``` ## Step 2: Add the cluster layers ```swift private func addClusterLayers(source: MLNShapeSource, to style: MLNStyle) throws { // Cluster layer let clusterLayer = MLNCircleStyleLayer(identifier: "clusters", source: source) clusterLayer.circleColor = NSExpression(format: "mgl_step:from:stops:(point_count, %@, %@)", UIColor.systemTeal, [100: UIColor.systemYellow, 750: UIColor.systemPink]) clusterLayer.circleRadius = NSExpression(format: "mgl_step:from:stops:(point_count, 20, %@)", [100: 30, 750: 40]) clusterLayer.predicate = NSPredicate(format: "point_count >= 0") style.addLayer(clusterLayer) // Count layer let countLayer = MLNSymbolStyleLayer(identifier: "cluster-count", source: source) countLayer.text = NSExpression(format: "CAST(point_count_abbreviated, 'NSString')") countLayer.textFontNames = NSExpression(forConstantValue: ["Noto Sans Medium"]) countLayer.textFontSize = NSExpression(forConstantValue: 12) countLayer.predicate = NSPredicate(format: "point_count >= 0") style.addLayer(countLayer) // Unclustered point layer let pointLayer = MLNCircleStyleLayer(identifier: "unclustered-point", source: source) pointLayer.circleColor = NSExpression(forConstantValue: UIColor.systemBlue) pointLayer.circleRadius = NSExpression(forConstantValue: 4) pointLayer.circleStrokeWidth = NSExpression(forConstantValue: 1) pointLayer.circleStrokeColor = NSExpression(forConstantValue: UIColor.white) pointLayer.predicate = NSPredicate(format: "point_count == nil") style.addLayer(pointLayer) } ``` ### Complete Setup ```swift func setupClusters() { print("🟢 Starting cluster setup") guard let style = mapView.style else { print("🔴 CRITICAL ERROR: Map style is nil") return } guard let url = URL(string: "https://cdn.mapmetrics-atlas.net/Images/heatmap.geojson") else { print("🔴 ERROR: Invalid GeoJSON URL") return } let task = URLSession.shared.dataTask(with: url) { data, response, error in if let data = data { print("✅ GeoJSON Size: \(data.count) bytes") if let geoJSON = String(data: data, encoding: .utf8) { print("📦 Preview GeoJSON:\n\(geoJSON.prefix(500))") } } else { print("❌ Failed to fetch GeoJSON: \(error?.localizedDescription ?? "Unknown error")") } } task.resume() if let layer = style.layer(withIdentifier: "earthquakes-heat") as? MLNVectorStyleLayer { print("Heatmap filter: \(String(describing: layer.predicate))") } do { // Create source without custom cluster properties first let source = MLNShapeSource( identifier: "clusteredEarthquakes", url: url, options: [ .clustered: true, .clusterRadius: 30 ] ) if let shapeCollection = source.shape as? MLNShapeCollectionFeature { var minLat = 90.0 var maxLat = -90.0 var minLon = 180.0 var maxLon = -180.0 for feature in shapeCollection.shapes { if let point = feature as? MLNPointFeature { let coord = point.coordinate minLat = min(minLat, coord.latitude) maxLat = max(maxLat, coord.latitude) minLon = min(minLon, coord.longitude) maxLon = max(maxLon, coord.longitude) } // Optionally handle more geometry types like MGLPolylineFeature or MGLPolygonFeature here } let sw = CLLocationCoordinate2D(latitude: minLat, longitude: minLon) let ne = CLLocationCoordinate2D(latitude: maxLat, longitude: maxLon) let bounds = MLNCoordinateBounds(sw: sw, ne: ne) let camera = mapView.cameraThatFitsCoordinateBounds(bounds, edgePadding: .init(top: 40, left: 20, bottom: 40, right: 20)) mapView.setCamera(camera, animated: true) } style.addSource(source) print("🟢 Source added successfully") // Create and add layers... try addClusterLayers(source: source, to: style) } catch { print("🔴 ERROR: \(error.localizedDescription)") if let nsError = error as NSError? { print("User Info: \(nsError.userInfo)") } } } private func addClusterLayers(source: MLNShapeSource, to style: MLNStyle) throws { // Cluster layer let clusterLayer = MLNCircleStyleLayer(identifier: "clusters", source: source) clusterLayer.circleColor = NSExpression(format: "mgl_step:from:stops:(point_count, %@, %@)", UIColor.systemTeal, [100: UIColor.systemYellow, 750: UIColor.systemPink]) clusterLayer.circleRadius = NSExpression(format: "mgl_step:from:stops:(point_count, 20, %@)", [100: 30, 750: 40]) clusterLayer.predicate = NSPredicate(format: "point_count >= 0") style.addLayer(clusterLayer) // Count layer let countLayer = MLNSymbolStyleLayer(identifier: "cluster-count", source: source) countLayer.text = NSExpression(format: "CAST(point_count_abbreviated, 'NSString')") countLayer.textFontNames = NSExpression(forConstantValue: ["Noto Sans Medium"]) countLayer.textFontSize = NSExpression(forConstantValue: 12) countLayer.predicate = NSPredicate(format: "point_count >= 0") style.addLayer(countLayer) // Unclustered point layer let pointLayer = MLNCircleStyleLayer(identifier: "unclustered-point", source: source) pointLayer.circleColor = NSExpression(forConstantValue: UIColor.systemBlue) pointLayer.circleRadius = NSExpression(forConstantValue: 4) pointLayer.circleStrokeWidth = NSExpression(forConstantValue: 1) pointLayer.circleStrokeColor = NSExpression(forConstantValue: UIColor.white) pointLayer.predicate = NSPredicate(format: "point_count == nil") style.addLayer(pointLayer) } ``` --- # mapmetrics-ios-Map-add-Heatmap https://docs.mapatlas.xyz/overview/sdk/ios-native/mapmetrics-ios-Map-add-Heatmap ## 📍 MapMetrics iOS Map Features Guide This guide covers how to: Add HeatMap ## 🔧 Prerequisites Xcode installed (version 13 or newer recommended) A new or existing iOS project Add MapMetrics SDK to your project (via SPM or manual integration) ### Example via Swift Package Manager: : https://github.com/MapMetrics/MapMetrics-iOS ## Setting Up the Map In your ViewController.swift: ```swift class ViewController: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! var selectedAnnotation: MLNPointAnnotation? var isMarkerSelected = false override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView( frame: view.bounds, styleURL: URL(string: "") ) mapView.delegate = self view.addSubview(mapView) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.delegate = self // This is a better starting position let center = CLLocationCoordinate2D(latitude: 20.0, longitude: 0.0) // More centered globally mapView.setCenter(center, zoomLevel: 2, animated: false) // Lower zoom to see more area view.addSubview(mapView) } } ``` ## Displaying Heatmap from GeoJSON ###Step 1: Add heatmap source ```swift let heatmapSource = MLNShapeSource( identifier: "earthquakes", url: URL(string: "https://cdn.mapmetrics-atlas.net/Images/heatmap.geojson")!, options: [.clustered: false] ) style.addSource(heatmapSource) ``` ###Step 2: Add heatmap layer ```swift let heatmap = MLNHeatmapStyleLayer(identifier: "earthquakes-heat", source: heatmapSource) heatmap.heatmapWeight = ... heatmap.heatmapColor = ... heatmap.heatmapRadius = ... heatmap.heatmapOpacity = NSExpression(forConstantValue: 0.8) heatmap.isVisible = false // initially hidden style.addLayer(heatmap) ``` ### Complete Func ```swift func setupHeatmap() { guard let style = mapView.style else { print("🔴 Map style not available") return } // 1. Remove any existing source/layer to avoid duplicates if let existingSource = style.source(withIdentifier: "earthquakes") { style.removeSource(existingSource) } if let existingLayer = style.layer(withIdentifier: "earthquakes-heat") { style.removeLayer(existingLayer) } // 2. Create the source with proper options let url = URL(string: "https://cdn.mapmetrics-atlas.net/Images/heatmap.geojson")! let source: MLNShapeSource do { source = MLNShapeSource( identifier: "earthquakes", url: url, options: [.clustered: false] // Important for heatmap ) style.addSource(source) print("🟢 Heatmap source added successfully") // 3. Create the heatmap layer with proper configuration let heatmap = MLNHeatmapStyleLayer(identifier: "earthquakes-heat", source: source) // Weight based on magnitude with exponential scaling heatmap.heatmapWeight = NSExpression( forMLNInterpolating: NSExpression(forKeyPath: "mag"), curveType: .exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: [ 0: 0, 1: 0.2, 3: 0.4, 5: 1.0 ]) ) // Dynamic intensity based on zoom level heatmap.heatmapIntensity = NSExpression( forMLNInterpolating: .zoomLevelVariable, curveType: .linear, parameters: nil, stops: NSExpression(forConstantValue: [ 0: 0.5, 5: 1.5, 10: 3.0 ]) ) // Color gradient from cool to hot heatmap.heatmapColor = NSExpression( forMLNInterpolating: NSExpression(forVariable: "heatmapDensity"), curveType: .linear, parameters: nil, stops: NSExpression(forConstantValue: [ 0: UIColor.blue.withAlphaComponent(0), 0.2: UIColor.blue, 0.4: UIColor.cyan, 0.6: UIColor.green, 0.8: UIColor.yellow, 1.0: UIColor.red ]) ) // Radius that changes with zoom heatmap.heatmapRadius = NSExpression( forMLNInterpolating: .zoomLevelVariable, curveType: .linear, parameters: nil, stops: NSExpression(forConstantValue: [ 0: 5, 5: 10, 10: 20 ]) ) heatmap.heatmapOpacity = NSExpression(forConstantValue: 0.8) heatmap.isVisible = false // Start hidden // 4. Add the layer in the correct position (above base but below labels) if let waterLayer = style.layer(withIdentifier: "water") { style.insertLayer(heatmap, above: waterLayer) } else { style.addLayer(heatmap) } print("🟢 Heatmap layer added successfully") } } ``` --- # Prerequisite https://docs.mapatlas.xyz/overview/sdk/ios-native/mapmetrics-ios-add-Map-Basic-guide # Prerequisite For all of our examples, you will need: - an API key, - knowledge in Swift/iOS development, - one style (default) ## Get the library There are several ways to get this library: - from the github repository (https://github.com/MapMetrics/MapMetrics-iOS) - from swift package https://github.com/MapMetrics/MapMetrics-iOS - from cocoapods.org ## Installation (CocoaPods) Add this to your Podfile: ```ruby target 'YourApp' do pod 'MapMetrics-iOS', '~> 0.0.3' # Use the latest version # OR pod 'MapMetrics-iOS', :git => 'https://github.com/MapMetrics/MapMetrics-iOS', :tag => '0.0.1' end ``` Run: ```bash pod install ``` ## Required Build Settings (Sandbox Fix) To prevent `rsync.samba deny(1)` errors, users must add these settings: ### Option A: Automatic Fix (via Podfile) Add to your Podfile: ```ruby post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| # Disable sandbox restrictions config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO' end end end ``` Then run: ```bash pod install ``` ### Option B: Manual Fix (Xcode Settings) 1. Open your project in Xcode. 2. Go to **Target → Build Settings**. 3. Search for `ENABLE_USER_SCRIPT_SANDBOXING`. 4. Set to **NO** for all configurations (Debug/Release). --- ## Verify Installation Import in your code: ```swift import MapMetrics ``` Clean Build (if issues persist): ```bash rm -rf ~/Library/Developer/Xcode/DerivedData/* ``` --- ## Troubleshooting | Issue | Solution | |---------------------------|-----------| | Sandbox deny(1) errors | Ensure `ENABLE_USER_SCRIPT_SANDBOXING=NO` is set. | | `pod install` fails | Delete `Pods/` and `Podfile.lock`, then retry. | | Version conflicts | Run `pod update MapMetrics`. | --- ## Example Podfile (Complete) ```ruby platform :ios, '12.0' target 'YourApp' do use_frameworks! pod 'MapMetrics-iOS', '~> 0.0.3' post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO' end end end end ``` --- ## Example Usage Here's how to integrate **MapMetrics** into your project: ```swift class ViewController: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { super.viewDidLoad() mapView = MLNMapView( frame: view.bounds, styleURL: URL(string: "") ) mapView.delegate = self view.addSubview(mapView) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.delegate = self // This is a better starting position let center = CLLocationCoordinate2D(latitude: 20.0, longitude: 0.0) // More centered globally mapView.setCenter(center, zoomLevel: 2, animated: false) // Lower zoom to see more area view.addSubview(mapView) } } ``` --- --- # Mapbox migration guide https://docs.mapatlas.xyz/overview/sdk/mapbox-migration-guide # Mapbox migration guide This part of the docs is dedicated to the migration from `mapbox-gl` to MapMetrics GL. This guide might not be accurate depending on the current version of `mapbox-gl` but should be fairly straight forward. The libraries are very similar but diverge with newer features happening from v2 in both libraries where Mapbox turned proprietary. The overall migration is: uninstall `mapbox-gl`, install `@mapmetrics/mapmetrics-gl` (or use the CDN links below), then replace `mapboxgl` with `mapmetricsgl` throughout your TypeScript, JavaScript and HTML/CSS. ::: warning The global is `mapmetricsgl`, not `mapmetrics` The UMD bundle defines exactly one global, `mapmetricsgl`. Writing `new mapmetrics.Map(...)` throws `mapmetrics is not defined`. The CSS classes follow the same name — `mapmetricsgl-ctrl`, not `mapmetrics-ctrl`. ::: ```bash npm uninstall mapbox-gl npm install @mapmetrics/mapmetrics-gl ``` ```dif - var map = new mapboxgl.Map({ + var map = new mapmetricsgl.Map({ -
Shadow Length: 1.0x
Buildings: 0
Status: Loading...
``` --- # Add a 3D Model using Three.js https://docs.mapatlas.xyz/sdk/examples/3d-model-threejs --- title: "Add a 3D Model using Three.js" category: "3d" platform: ["web"] difficulty: "advanced" apis: ["Map", "addLayer", "custom layer", "triggerRepaint"] tags: ["3d", "threejs", "gltf", "custom layer", "model", "three.js", "globe", "mercator"] description: "Use a custom style layer with Three.js to render a 3D GLTF model on the map" --- # Add a 3D Model using Three.js Use a **custom style layer** with [Three.js](https://threejs.org/) to load and render a 3D GLTF model directly on the map. The model is georeferenced using the map's own projection matrix, so it stays anchored to a real-world coordinate. > **Three.js is loaded via CDN** — no build step needed for this example.
## How It Works A **custom layer** gives you full access to the map's WebGL context. Three.js shares that context so both the map and your 3D model render on the same canvas. ```javascript const customLayer = { id: '3d-model', type: 'custom', renderingMode: '3d', // required for correct depth buffer onAdd(map, gl) { this.camera = new THREE.Camera(); this.scene = new THREE.Scene(); // Load GLTF model const loader = new GLTFLoader(); loader.load('path/to/model.gltf', (gltf) => { this.scene.add(gltf.scene); }); // Share the map's WebGL canvas and context this.renderer = new THREE.WebGLRenderer({ canvas: map.getCanvas(), context: gl, antialias: true }); this.renderer.autoClear = false; }, render(gl, args) { // Georeference the model using map's projection matrix const modelMatrix = map.transform.getMatrixForModel([lng, lat], altitude); const m = new THREE.Matrix4().fromArray(args.defaultProjectionData.mainMatrix); const l = new THREE.Matrix4().fromArray(modelMatrix).scale(new THREE.Vector3(scale, scale, scale)); this.camera.projectionMatrix = m.multiply(l); this.renderer.resetState(); this.renderer.render(this.scene, this.camera); this.map.triggerRepaint(); // keep re-rendering } }; map.on('style.load', () => { map.addLayer(customLayer); }); ``` ## Import Three.js Use an importmap to load Three.js and GLTFLoader from CDN — no npm needed: ```html ``` ## Georeference the Model `map.transform.getMatrixForModel()` converts a `[lng, lat]` coordinate into the correct 4×4 matrix for the current projection (globe or Mercator): ```javascript const modelMatrix = map.transform.getMatrixForModel( [148.9819, -35.39847], // [lng, lat] 0 // altitude in meters ); ``` Scale the model up if needed — GLTF models are often designed at real-world scale (meters), but at global zoom you need a large multiplier: ```javascript .scale(new THREE.Vector3(10_000, 10_000, 10_000)) ``` ## Toggle Globe vs Mercator ```javascript const current = map.getProjection(); map.setProjection({ type: current.type === 'globe' ? 'mercator' : 'globe' }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # 3D Terrain https://docs.mapatlas.xyz/sdk/examples/3d-terrain --- title: "3D Terrain" category: "3d" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "terrain", "raster-dem", "TerrainControl", "NavigationControl"] tags: ["3D", "terrain", "elevation", "DEM", "raster-dem", "exaggeration", "mountain"] description: "Render a 3D terrain map by adding a raster-dem source and enabling the terrain property" --- # 3D Terrain Make your map look like a real landscape by enabling **3D terrain**. The map reads elevation data from a special tile source (`raster-dem`) and pushes mountains up into 3D. > **No external libraries needed.** This is built directly into MapMetrics GL — no three.js, no plugins.
Drag to pan · Hold right-click and drag to tilt · Use the terrain button (mountain icon) to toggle 3D
## How It Works 3D terrain needs two things: 1. **A `raster-dem` source** — tiles containing elevation data for every point on the map 2. **The `terrain` style property** — tells the map to use that elevation data to push the land surface up into 3D ```javascript const map = new mapmetricsgl.Map({ container: 'map', // 1. Your regular base map — here a MapMetrics vector style style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE.json&token=YOUR_TOKEN', center: [11.39085, 47.27574], zoom: 12, pitch: 70, // Tilt the camera to see the 3D effect maxPitch: 85, }); // 2. Wait for the vector style to finish loading, then add elevation map.on('load', () => { // Elevation data source (free AWS terrain tiles — no API key needed) map.addSource('terrainSource', { type: 'raster-dem', tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'], tileSize: 256, encoding: 'terrarium', maxzoom: 15, }); // 3. Enable 3D terrain map.setTerrain({ source: 'terrainSource', exaggeration: 1.5 }); }); ``` ## Exaggeration `exaggeration` controls how dramatic the 3D effect looks: | Value | Effect | |---|---| | `0` | Flat map (terrain disabled) | | `1` | Real-world scale | | `1.5` | Slightly dramatic (good default) | | `3` | Very exaggerated mountains | ## Add a Terrain Toggle Button ```javascript map.addControl(new mapmetricsgl.TerrainControl({ source: 'terrainSource', exaggeration: 1.5, }), 'top-right'); ``` This adds a mountain icon button that toggles 3D terrain on/off. ## Tips - Set `pitch: 70` or higher to see the 3D effect clearly - Set `maxPitch: 85` to allow steep camera angles - Add a `sky` layer for a realistic atmospheric background - Mountains and valleys look best at zoom 8–14 ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Create and Style Clusters https://docs.mapatlas.xyz/sdk/examples/add-a-cluster --- title: "Create and Style Clusters" category: "data-visualization" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "getClusterExpansionZoom", "easeTo"] tags: ["clustering", "geojson", "data-aggregation", "performance"] description: "Group multiple markers into clusters for better performance and visualization with large datasets" --- # Create and style clusters ##
--- # Add a Color Relief Layer https://docs.mapatlas.xyz/sdk/examples/add-a-color-relief-layer --- title: "Add a Color Relief Layer" category: "3d" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "raster-dem", "addLayer", "fill-extrusion", "color-relief"] tags: ["color relief", "terrain", "elevation", "DEM", "raster-dem", "hypsometric", "altitude color"] description: "Color the map surface by elevation using a raster-dem source and elevation-based color expressions" --- # Add a Color Relief Layer **Color relief** (also called hypsometric tinting) paints the map with colors based on elevation — lowlands are green, mid-elevations are brown, mountain peaks are white. This makes elevation immediately visible at a glance. > **No three.js or external libraries needed.** This uses a standard raster layer with a color expression.
## How Color Relief Works Color relief paints the terrain surface with colors that represent altitude: ``` 🌊 Water / low → Deep blue / dark green 🌿 Plains → Light green 🌄 Hills → Yellow / brown ⛰️ Mountains → Dark brown / grey 🏔️ Peaks → White / light grey ``` The simplest approach is to use a `hillshade` layer with carefully chosen shadow/highlight colors, combined with a semi-transparent base map. ## Adding Color Relief with Hillshade ```javascript map.addSource('dem', { type: 'raster-dem', tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'], tileSize: 256, encoding: 'terrarium', maxzoom: 15, }); map.addLayer({ id: 'color-relief', type: 'hillshade', source: 'dem', paint: { 'hillshade-shadow-color': '#2d6a4f', // low elevation → green 'hillshade-highlight-color': '#f8f9fa', // high elevation → white 'hillshade-accent-color': '#8B4513', // steep slopes → brown 'hillshade-exaggeration': 0.8, 'hillshade-illumination-anchor': 'viewport', }, }); ``` ## Tweak the Relief Opacity Adjust how strongly the color relief overlays the base map by changing its `hillshade-exaggeration` or by inserting the layer at a different position with `map.addLayer(..., 'beforeLayerId')`: ```javascript // Softer relief map.setPaintProperty('color-relief', 'hillshade-exaggeration', 0.4); // Stronger relief map.setPaintProperty('color-relief', 'hillshade-exaggeration', 1); // Hide / show the relief layer entirely map.setLayoutProperty('color-relief', 'visibility', 'none'); map.setLayoutProperty('color-relief', 'visibility', 'visible'); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a Geometry https://docs.mapatlas.xyz/sdk/examples/add-a-geometry # Add a Geometry ##
--- # Add a Heatmap Layer https://docs.mapatlas.xyz/sdk/examples/add-a-heatmap --- title: "Add a Heatmap Layer" category: "data-visualization" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer"] tags: ["heatmap", "density", "visualization", "geojson"] description: "Create heatmap visualizations to show data density and patterns on your map" --- # Add a Heatmap ##
--- # Add a Hillshade Layer https://docs.mapatlas.xyz/sdk/examples/add-a-hillshade-layer --- title: "Add a Hillshade Layer" category: "3d" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "hillshade", "raster-dem", "addLayer"] tags: ["hillshade", "terrain", "elevation", "shadow", "DEM", "raster-dem", "shading"] description: "Add a hillshade layer to show terrain shadows and elevation relief on a flat map" --- # Add a Hillshade Layer A **hillshade layer** simulates how sunlight would fall on terrain — mountains cast shadows, valleys stay dark. This makes elevation visible on an otherwise flat 2D map. > **No three.js or external libraries needed.** Hillshade is a built-in layer type in MapMetrics GL.
## What is a Hillshade Layer? Imagine shining a light from the top-left corner of the map. Mountains facing the light become bright; valleys and north-facing slopes fall into shadow. That's what a hillshade layer does — it gives a flat 2D map a sense of depth and elevation. ## Adding the Hillshade Layer You need two things: 1. A `raster-dem` source (elevation data tiles) 2. A `hillshade` layer that references it ```javascript // Step 1: Add the elevation data source (free AWS terrain tiles — no API key needed) map.addSource('dem', { type: 'raster-dem', tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'], tileSize: 256, encoding: 'terrarium', maxzoom: 15, }); // Step 2: Add the hillshade layer map.addLayer({ id: 'hillshade', type: 'hillshade', source: 'dem', paint: { 'hillshade-shadow-color': '#473B24', // shadow color 'hillshade-highlight-color': '#ffffff', // lit color 'hillshade-illumination-direction': 335, // sun angle 0–360° 'hillshade-exaggeration': 0.5, // 0 = flat, 1 = maximum contrast }, }); ``` ## Paint Properties | Property | What it does | |---|---| | `hillshade-shadow-color` | Color of shadowed slopes | | `hillshade-highlight-color` | Color of sunlit slopes | | `hillshade-accent-color` | Color of very steep slopes | | `hillshade-illumination-direction` | Direction of the sun (0° = North) | | `hillshade-exaggeration` | Shadow intensity (0–1) | ## Show/Hide at Runtime ```javascript // Show map.setLayoutProperty('hillshade', 'visibility', 'visible'); // Hide map.setLayoutProperty('hillshade', 'visibility', 'none'); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add Markers to Map https://docs.mapatlas.xyz/sdk/examples/add-a-marker --- title: "Add Markers to Map" category: "markers" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Marker", "setLngLat", "addTo"] tags: ["markers", "pins", "draggable", "custom-marker"] description: "Learn how to add interactive markers to your map, including draggable markers" --- # Adding Markers to Your Map ## The red marker is dragable
This guide demonstrates how to add markers to your MapMetrics map. Follow these steps to get started. ## Prerequisites Before you begin, ensure you have: - **Generated a style and API key:** [How to create an API key](./key-creation.md) and [How to create a map style](./style-creation.md). ## Adding a Marker To add a marker to your map, you can use the `Marker` class provided by MapMetrics-gl. Here's how to do it: ### Example: Adding a Marker ```javascript const marker = new mapmetricsgl.Marker() .setLngLat([2.349902, 48.852966]) // Set the marker's position .addTo(map); // Add the marker to the map ``` ### Customizing Markers You can customize the appearance of markers by setting properties such as color, size, and icon. For example: ```javascript const marker = new mapmetricsgl.Marker({ color: "#FF0000", // Red color scale: 1.5, // Increase the size }) .setLngLat([2.349902, 48.852966]) .addTo(map); ``` ### Creating a Draggable Marker with Custom Color You can create a marker with a custom color and make it draggable by specifying the `color` and `draggable` options in the marker constructor: ```javascript const marker = new mapmetricsgl.Marker({ color: "#FFFFFF", // White marker draggable: true // Make the marker draggable }) .setLngLat([30.5, 50.5]) .addTo(map); ``` You can also listen for drag events to get the marker's new position: ```javascript marker.on('dragend', function() { const lngLat = marker.getLngLat(); console.log('Marker dropped at', lngLat); }); ``` ## Complete Example Here's a complete example of how to add a marker to your map: --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). ``` --- # Drawing a Polyline on Your Map https://docs.mapatlas.xyz/sdk/examples/add-a-polyline # Drawing a Polyline on Your Map This guide demonstrates how to display a polyline (LineString) on your MapMetrics map. Polylines are useful for showing routes, paths, or any sequence of connected points. ## Prerequisites Before you begin, ensure you have: - **Generated a style and API key:** [How to create an API key](./key-creation.md) and [How to create a map style](./style-creation.md). ## Adding a Polyline To add a polyline, you need to: 1. Define the coordinates for your line. 2. Add a GeoJSON source to the map. 3. Add a new layer of type `line` that uses this source. ### Example: Adding a Polyline ```javascript // Define the coordinates for your polyline const lineCoordinates = [ [ 2.3398344221314176, 48.85894283778356 ], [ 2.349092133374455, 48.856611153804636 ], [ 2.3539379666036098, 48.85466006946356 ], [ 2.3610259017737576, 48.85189986905755 ], [ 2.3589284515699944, 48.84828282438352 ], [ 2.3505386507564197, 48.851566731130646 ], [ 2.3433783897170883, 48.85399382812608 ], [ 2.3386048823571173, 48.85732494614973 ], [ 2.334409981949733, 48.85789597269732 ], [ 2.3399067480006295, 48.85899042203965 ] ]; // Add the source and layer when the map is loaded map.on('load', function () { map.addSource('route', { 'type': 'geojson', 'data': { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': lineCoordinates } } }); map.addLayer({ 'id': 'route', 'type': 'line', 'source': 'route', 'layout': { 'line-join': 'round', 'line-cap': 'round' }, 'paint': { 'line-color': '#FF0000', 'line-width': 4 } }); }); ``` ## Complete Example Below is a complete example of how to add a polyline to your map: ```html
``` --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). ## Polyline Example (Vue) Below is a Vue-compatible example that displays a polyline (LineString) on the map:
--- # Add Popup to Marker https://docs.mapatlas.xyz/sdk/examples/add-a-popup --- title: "Add Popup to Marker" category: "popups" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Marker", "Popup", "setPopup", "setHTML"] tags: ["popup", "marker", "info-window", "tooltip"] description: "Display interactive popups on markers with custom HTML content" --- # Add a Popup ## Click on the marker for a popup
## Prerequisites Before you begin, ensure you have: - **Generated a style and API key:** [How to create an API key](./key-creation.md) and [How to create a map style](./style-creation.md). --- # Add an Icon to the Map https://docs.mapatlas.xyz/sdk/examples/add-an-icon-to-the-map --- title: "Add an Icon to the Map" category: "icons-images" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "loadImage", "addImage", "addSource", "addLayer", "icon-image"] tags: ["icon", "symbol", "image", "marker", "loadImage", "addImage", "sprite", "point"] description: "Load an external image and use it as a map icon in a symbol layer" --- # Add an Icon to the Map Load an image from a URL and display it as an icon on the map using a `symbol` layer.
## Load an Image with `loadImage` ```javascript map.on('load', () => { map.loadImage('https://example.com/icon.png', (error, image) => { if (error) throw error; // Register the image with a name map.addImage('my-icon', image); // Use it in a symbol layer map.addLayer({ id: 'icon-layer', type: 'symbol', source: 'my-source', layout: { 'icon-image': 'my-icon', 'icon-size': 1.0, 'icon-allow-overlap': true, } }); }); }); ``` ## Symbol Layer Icon Properties ```javascript map.addLayer({ id: 'icons', type: 'symbol', source: 'points', layout: { 'icon-image': 'my-icon', // name registered with addImage() 'icon-size': 0.5, // scale factor (1.0 = original size) 'icon-anchor': 'center', // 'center', 'top', 'bottom', 'left', 'right' 'icon-allow-overlap': true, // show icon even if it overlaps others 'icon-ignore-placement': true, // don't block other features 'icon-offset': [0, -10], // [x, y] pixel offset } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add an Animated Icon to the Map https://docs.mapatlas.xyz/sdk/examples/add-animated-icon --- title: "Add an Animated Icon to the Map" category: "icons-images" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addImage", "updateImage", "addSource", "addLayer", "requestAnimationFrame"] tags: ["icon", "animated", "pulse", "canvas", "animation", "requestAnimationFrame", "symbol", "addImage", "updateImage"] description: "Create a pulsing animated icon using Canvas and requestAnimationFrame" --- # Add an Animated Icon to the Map Create a pulsing icon animation by updating a canvas-based image on every animation frame using `map.updateImage()`.
## How It Works The animation uses three steps: 1. Create a canvas and draw an initial frame 2. Register the image with `map.addImage()` 3. On each `requestAnimationFrame`, redraw and call `map.updateImage()` to push the new frame ## Draw and Register the Animated Icon ```javascript const size = 80; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); let t = 0; function drawFrame() { ctx.clearRect(0, 0, size, size); t += 0.04; const pulse = (Math.sin(t) + 1) / 2; // oscillates 0 → 1 // Expanding ring ctx.strokeStyle = `rgba(59, 130, 246, ${1 - pulse})`; ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(size / 2, size / 2, 15 + pulse * 20, 0, Math.PI * 2); ctx.stroke(); // Core dot ctx.fillStyle = '#3b82f6'; ctx.beginPath(); ctx.arc(size / 2, size / 2, 8, 0, Math.PI * 2); ctx.fill(); // Push updated frame to the map if (map.hasImage('pulse-icon')) { map.updateImage('pulse-icon', ctx.getImageData(0, 0, size, size)); } requestAnimationFrame(drawFrame); } map.on('load', () => { // Register the initial frame map.addImage('pulse-icon', ctx.getImageData(0, 0, size, size)); // ... addSource, addLayer with 'icon-image': 'pulse-icon' drawFrame(); // start animation loop }); ``` ## Key APIs | API | Description | |-----|-------------| | `map.addImage(name, data)` | Register the initial image frame | | `map.updateImage(name, data)` | Update an existing image each frame | | `map.hasImage(name)` | Check if an image is registered before updating | | `requestAnimationFrame(fn)` | Browser animation loop (~60fps) | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add Contour Lines https://docs.mapatlas.xyz/sdk/examples/add-contour-lines --- title: "Add Contour Lines" category: "3d" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "addSource", "addLayer", "line", "GeoJSON"] tags: ["contour", "elevation", "terrain", "lines", "topographic", "altitude", "isoline"] description: "Draw contour lines on the map to show elevation levels using GeoJSON line features" --- # Add Contour Lines **Contour lines** are lines that connect points of the same elevation — like the rings you see on a topographic map. They show you how steep or flat the terrain is without needing 3D. > **No three.js or external libraries needed.** Contour lines are drawn using a standard GeoJSON `line` layer.
Contour lines with elevation labels — thin lines every 100m, thick lines every 200m
## How Contour Lines Work Contour lines are just line features in a GeoJSON source. Each line has an `elevation` property. You add two layers: - **Minor lines** (thin) — every 100m interval - **Major lines** (thick) — every 200m or 500m interval ```javascript map.addSource('contours', { type: 'geojson', data: { type: 'FeatureCollection', features: [ { type: 'Feature', properties: { elevation: 500 }, geometry: { type: 'LineString', coordinates: [[lng1, lat1], [lng2, lat2], ...] } }, // more contour lines... ] } }); // Thin lines for all contours map.addLayer({ id: 'contour-lines', type: 'line', source: 'contours', paint: { 'line-color': '#8B4513', 'line-width': 0.8, 'line-opacity': 0.6, } }); // Thick lines for major intervals (every 200m) map.addLayer({ id: 'contour-major', type: 'line', source: 'contours', filter: ['==', ['%', ['get', 'elevation'], 200], 0], paint: { 'line-color': '#5c3317', 'line-width': 2, } }); ``` ## Add Elevation Labels Place labels along the contour lines using `symbol-placement: 'line'`: ```javascript map.addLayer({ id: 'contour-labels', type: 'symbol', source: 'contours', layout: { 'text-field': ['concat', ['to-string', ['get', 'elevation']], 'm'], 'symbol-placement': 'line', // follow the line path 'text-size': 11, }, paint: { 'text-color': '#5c3317', 'text-halo-color': '#fff', 'text-halo-width': 1.5, } }); ``` ## Real Contour Data Sources In production, use a real contour data source: - **MapTiler Contours** — vector tiles with elevation data - **OpenTopoData API** — free elevation API - **Mapbox Terrain** — `contour` source layer in Mapbox terrain tiles ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add Custom Icons with Markers https://docs.mapatlas.xyz/sdk/examples/add-custom-icons-markers --- title: "Add Custom Icons with Markers" category: "icons-images" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Marker", "setLngLat", "addTo", "getElement"] tags: ["marker", "custom", "icon", "html", "css", "emoji", "image", "div", "getElement"] description: "Create Markers with custom HTML elements as icons instead of the default pin" --- # Add Custom Icons with Markers Use the `Marker` API with a custom HTML element to display any icon — emoji, SVG, image, or styled div — as a map marker.
## Default vs Custom Marker ```javascript // Default marker (blue pin) new mapmetricsgl.Marker() .setLngLat([lng, lat]) .addTo(map); // Custom HTML marker const el = document.createElement('div'); el.innerHTML = '⭐'; el.style.fontSize = '32px'; el.style.cursor = 'pointer'; new mapmetricsgl.Marker({ element: el }) .setLngLat([lng, lat]) .addTo(map); ``` ## Create a Styled Div Marker ```javascript function createCustomMarker(emoji, color) { const el = document.createElement('div'); el.style.cssText = ` width: 44px; height: 44px; background: ${color}; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 22px; border: 2px solid #fff; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); cursor: pointer; `; el.textContent = emoji; return el; } const el = createCustomMarker('🏪', '#3b82f6'); new mapmetricsgl.Marker({ element: el }) .setLngLat([-74, 40.7]) .addTo(map); ``` ## Use an Image as the Marker ```javascript const el = document.createElement('img'); el.src = 'https://example.com/icon.png'; el.style.width = '40px'; el.style.height = '40px'; new mapmetricsgl.Marker({ element: el }) .setLngLat([lng, lat]) .addTo(map); ``` ## Marker with Popup ```javascript const popup = new mapmetricsgl.Popup({ offset: 25 }) .setHTML('My Location

Description here

'); new mapmetricsgl.Marker({ element: el }) .setLngLat([lng, lat]) .setPopup(popup) .addTo(map); ``` ## Marker Options ```javascript new mapmetricsgl.Marker({ element: el, // custom HTML element anchor: 'center', // 'center', 'top', 'bottom', 'left', 'right', 'top-left', etc. offset: [0, -20], // [x, y] pixel offset draggable: true, // allow the user to drag the marker }) ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a Generated Icon to the Map https://docs.mapatlas.xyz/sdk/examples/add-generated-icon --- title: "Add a Generated Icon to the Map" category: "icons-images" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addImage", "addSource", "addLayer", "icon-image", "Canvas"] tags: ["icon", "generated", "canvas", "symbol", "programmatic", "custom", "image", "addImage"] description: "Generate a custom icon programmatically using the Canvas API and add it to the map" --- # Add a Generated Icon to the Map Create a custom icon programmatically using the Canvas 2D API and register it with `map.addImage()`.
## Generate an Icon with Canvas API ```javascript function generateIcon(color) { const size = 48; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); // Draw a circle ctx.fillStyle = color; ctx.beginPath(); ctx.arc(size / 2, size / 2, size / 2 - 2, 0, Math.PI * 2); ctx.fill(); // White border ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 3; ctx.stroke(); return ctx.getImageData(0, 0, size, size); } ``` ## Register and Use the Icon ```javascript map.on('load', () => { const iconData = generateIcon('#3b82f6'); // Register with map map.addImage('generated-icon', iconData); // Use in a symbol layer map.addLayer({ id: 'icons', type: 'symbol', source: 'my-source', layout: { 'icon-image': 'generated-icon', 'icon-size': 1.0, 'icon-allow-overlap': true, } }); }); ``` ## Switch Icon at Runtime ```javascript // Update which image is used for the layer map.setLayoutProperty('icons', 'icon-image', 'new-icon-name'); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a GeoJSON Line https://docs.mapatlas.xyz/sdk/examples/add-geojson-line --- title: "Add a GeoJSON Line" category: "geometry" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "addSource", "addLayer", "LineString"] tags: ["line", "geojson", "linestring", "polyline", "route", "path", "layer"] description: "Add a GeoJSON LineString to the map as a styled line layer" --- # Add a GeoJSON Line Add a GeoJSON LineString source and display it as a styled line layer on the map.
## Add a GeoJSON Line ```javascript map.on('load', () => { map.addSource('route', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [ [lng1, lat1], [lng2, lat2], [lng3, lat3] ] } } }); map.addLayer({ id: 'route-line', type: 'line', source: 'route', layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#3b82f6', 'line-width': 4, 'line-opacity': 0.9 } }); }); ``` ## Line Style Options ```javascript paint: { 'line-color': '#3b82f6', 'line-width': 4, 'line-opacity': 0.9, 'line-dasharray': [2, 4], // dashed line 'line-blur': 1, // soft edge 'line-gap-width': 2 // gap between double lines } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a GeoJSON Polygon https://docs.mapatlas.xyz/sdk/examples/add-geojson-polygon --- title: "Add a GeoJSON Polygon" category: "geometry" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "addSource", "addLayer", "Polygon", "fill"] tags: ["polygon", "geojson", "fill", "area", "shape", "layer", "geometry"] description: "Add a GeoJSON Polygon to the map as a filled area with an outline" --- # Add a GeoJSON Polygon Add a GeoJSON Polygon and display it as a filled area with an outline border.
## Add a Polygon ```javascript map.on('load', () => { map.addSource('polygon', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[ [lng1, lat1], [lng2, lat2], [lng3, lat3], [lng1, lat1] // close the ring ]] } } }); // Filled area map.addLayer({ id: 'polygon-fill', type: 'fill', source: 'polygon', paint: { 'fill-color': '#3b82f6', 'fill-opacity': 0.3 } }); // Border outline map.addLayer({ id: 'polygon-outline', type: 'line', source: 'polygon', paint: { 'line-color': '#1d4ed8', 'line-width': 2 } }); }); ``` ## Polygon with a Hole ```javascript coordinates: [ // Outer ring [[-5, 40], [10, 40], [10, 50], [-5, 50], [-5, 40]], // Inner ring (hole) [[0, 44], [5, 44], [5, 47], [0, 47], [0, 44]] ] ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a Image as a Marker https://docs.mapatlas.xyz/sdk/examples/add-image-marker # Add a Image as a Marker ##
## Prerequisites Before you begin, ensure you have: - **Generated a style and API key:** [How to create an API key](./key-creation.md) and [How to create a map style](./style-creation.md). --- # Add a New Layer Below Labels https://docs.mapatlas.xyz/sdk/examples/add-layer-below-labels --- title: "Add a New Layer Below Labels" category: "styling" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addLayer", "beforeId", "addSource"] tags: ["layer", "order", "labels", "beforeId", "z-order", "below", "insert"] description: "Insert a new layer below existing map labels so labels remain visible on top" --- # Add a New Layer Below Labels Use the `beforeId` parameter in `addLayer()` to insert layers below map labels so they stay readable.
## Insert a Layer Below Labels ```javascript map.on('load', () => { // Find the first symbol (label) layer in the style const layers = map.getStyle().layers; let firstSymbolId; for (const layer of layers) { if (layer.type === 'symbol') { firstSymbolId = layer.id; break; } } map.addSource('my-source', { type: 'geojson', data: myGeoJSON }); // Pass firstSymbolId as the second argument to insert below labels map.addLayer({ id: 'my-fill', type: 'fill', source: 'my-source', paint: { 'fill-color': '#3b82f6', 'fill-opacity': 0.4 } }, firstSymbolId); }); ``` ## Insert Before a Specific Layer ```javascript // Insert before a known layer ID map.addLayer({ id: 'my-layer', type: 'fill', source: 'my-source', paint: {} }, 'road-label'); // Insert at the very bottom (no beforeId = on top) map.addLayer({ id: 'my-layer', type: 'fill', source: 'my-source', paint: {} }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a Pattern to a Polygon https://docs.mapatlas.xyz/sdk/examples/add-pattern-to-polygon --- title: "Add a Pattern to a Polygon" category: "lines-polygons" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "loadImage", "addImage", "fill-pattern"] tags: ["pattern", "polygon", "fill", "texture", "image", "fill-pattern", "hatch", "repeat"] description: "Fill a polygon with a repeating image pattern using fill-pattern and loadImage" --- # Add a Pattern to a Polygon Fill a polygon with a repeating image texture using `fill-pattern` and `map.loadImage()`.
## How It Works 1. Load or create an image to use as the tile pattern 2. Register it with `map.addImage('name', imageData)` 3. Apply it using `fill-pattern: 'name'` in the layer paint ## Using a Remote Image ```javascript map.on('load', () => { map.loadImage('https://example.com/pattern.png', (error, image) => { if (error) throw error; map.addImage('my-pattern', image); map.addLayer({ id: 'polygon-fill', type: 'fill', source: 'polygon', paint: { 'fill-pattern': 'my-pattern' } }); }); }); ``` ## Using a Canvas-Generated Pattern ```javascript // Create pattern programmatically with Canvas API const size = 16; const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); // Draw diagonal lines ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0, size); ctx.lineTo(size, 0); ctx.stroke(); // Register as a map image const imageData = ctx.getImageData(0, 0, size, size); map.addImage('hatch', imageData); map.addLayer({ id: 'fill', type: 'fill', source: 'polygon', paint: { 'fill-pattern': 'hatch' } }); ``` ## Pattern Layer Properties ```javascript map.addLayer({ id: 'polygon-fill', type: 'fill', source: 'polygon', paint: { 'fill-pattern': 'my-pattern', // image name from addImage() 'fill-opacity': 0.9, // overall opacity // Note: fill-color is ignored when fill-pattern is set } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add a Stretchable Image to the Map https://docs.mapatlas.xyz/sdk/examples/add-stretchable-image --- title: "Add a Stretchable Image to the Map" category: "icons-images" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addImage", "addSource", "addLayer", "stretchX", "stretchY", "content", "icon-text-fit"] tags: ["stretchable", "nine-patch", "icon", "image", "addImage", "stretchX", "stretchY", "content", "label", "icon-text-fit"] description: "Use a stretchable (nine-patch) image as an icon that scales without distortion" --- # Add a Stretchable Image to the Map A stretchable image (nine-patch) allows certain regions to stretch while keeping corners and edges crisp. Use `stretchX`, `stretchY`, and `content` in `map.addImage()` to define which parts can stretch and where text is placed.
## What is a Stretchable Image? A stretchable image is similar to Android's **nine-patch** format. You define: - `stretchX` — pixel ranges along the X axis that can be stretched - `stretchY` — pixel ranges along the Y axis that can be stretched - `content` — the bounding box `[left, top, right, bottom]` where text/icon content is placed This lets the image scale gracefully without distorting corners or borders. ## Create and Register a Stretchable Image ```javascript const width = 60, height = 24; const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); // Draw a pill shape (rounded rect) const r = height / 2; ctx.fillStyle = '#3b82f6'; ctx.beginPath(); ctx.moveTo(r, 0); ctx.arcTo(width, 0, width, height, r); ctx.arcTo(width, height, 0, height, r); ctx.arcTo(0, height, 0, 0, r); ctx.arcTo(0, 0, width, 0, r); ctx.closePath(); ctx.fill(); const imageData = ctx.getImageData(0, 0, width, height); map.addImage('stretchable-pill', imageData, { stretchX: [[8, width - 8]], // horizontal stretch zone stretchY: [[4, height - 4]], // vertical stretch zone content: [8, 4, width - 8, height - 4], // where text goes pixelRatio: 1 }); ``` ## Use with `icon-text-fit` ```javascript map.addLayer({ id: 'labels', type: 'symbol', source: 'points', layout: { 'icon-image': 'stretchable-pill', 'icon-text-fit': 'both', // 'none' | 'width' | 'height' | 'both' 'icon-text-fit-padding': [4, 8, 4, 8], // padding inside the content box 'icon-allow-overlap': true, } }); ``` ## `stretchX` / `stretchY` Format ```javascript // Array of [from, to] pixel ranges (0-indexed) stretchX: [[8, 52]] // pixels 8–52 can stretch horizontally stretchY: [[4, 20]] // pixels 4–20 can stretch vertically // Multiple stretch zones stretchX: [[4, 12], [48, 56]] ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Animate a Line https://docs.mapatlas.xyz/sdk/examples/animate-a-line --- title: "Animate a Line" category: "lines-polygons" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setData", "requestAnimationFrame"] tags: ["line", "animate", "animation", "lineString", "draw", "route", "path", "requestAnimationFrame"] description: "Animate a line being drawn on the map step by step using requestAnimationFrame" --- # Animate a Line Draw a line on the map that animates progressively, revealing a path step by step.
## How It Works Use `setData()` on a GeoJSON source to progressively add coordinates to a LineString, driven by `requestAnimationFrame` or `setTimeout`. ## Key Pattern ```javascript // 1. Add source with empty line map.addSource('route', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [] } } }); // 2. Add line layer map.addLayer({ id: 'route-line', type: 'line', source: 'route', paint: { 'line-color': '#3b82f6', 'line-width': 3 } }); // 3. Animate by adding one coordinate at a time let step = 0; const coordinates = [[lng1, lat1], [lng2, lat2], ...]; function animate() { if (step >= coordinates.length) return; step++; map.getSource('route').setData({ type: 'Feature', geometry: { type: 'LineString', coordinates: coordinates.slice(0, step) } }); setTimeout(() => requestAnimationFrame(animate), 300); } animate(); ``` ## Smooth Animation with Interpolation For very smooth animation between two points: ```javascript const from = [0, 0]; const to = [10, 10]; const steps = 100; let i = 0; function animate() { if (i > steps) return; const t = i / steps; const lng = from[0] + (to[0] - from[0]) * t; const lat = from[1] + (to[1] - from[1]) * t; map.getSource('route').setData({ type: 'Feature', geometry: { type: 'LineString', coordinates: [from, [lng, lat]] } }); i++; requestAnimationFrame(animate); } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Animate Camera Around a Point https://docs.mapatlas.xyz/sdk/examples/animate-camera-around-point --- title: "Animate Camera Around a Point" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "easeTo", "getBearing"] tags: ["camera", "animation", "rotate", "orbit", "bearing"] description: "Continuously rotate the map camera around a fixed point to create an orbiting animation" --- # Animate Camera Around a Point Continuously rotate the map camera around a fixed point to create a smooth orbiting animation.
## How It Works The animation works by repeatedly calling `map.easeTo()` with an incremented bearing on each animation frame using `requestAnimationFrame`. ## Basic Usage ```javascript let rotating = false; let animationId = null; function rotateCamera() { if (!rotating) return; // Increment bearing by a small amount each frame map.easeTo({ bearing: map.getBearing() + 0.3, duration: 0, easing: (t) => t }); // Request next frame animationId = requestAnimationFrame(rotateCamera); } // Start rotation function startRotation() { rotating = true; rotateCamera(); } // Stop rotation function stopRotation() { rotating = false; if (animationId) cancelAnimationFrame(animationId); } ``` ## Customization ```javascript // Faster rotation map.easeTo({ bearing: map.getBearing() + 1.0, duration: 0 }); // Slower rotation map.easeTo({ bearing: map.getBearing() + 0.1, duration: 0 }); // Rotate AND change pitch for a dynamic effect map.easeTo({ bearing: map.getBearing() + 0.5, pitch: 60, duration: 0 }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Animate a Marker https://docs.mapatlas.xyz/sdk/examples/animate-marker --- title: "Animate a Marker" category: "animations" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "Marker", "setLngLat", "requestAnimationFrame"] tags: ["animate", "marker", "animation", "moving", "requestAnimationFrame", "pulse"] description: "Animate a marker moving on the map using requestAnimationFrame" --- # Animate a Marker Animate a Marker smoothly across the map using `requestAnimationFrame` and `setLngLat()`.
## Animate a Marker with requestAnimationFrame ```javascript const marker = new mapmetricsgl.Marker({ color: '#3b82f6' }) .setLngLat([0, 0]) .addTo(map); let t = 0; function animate() { t += 0.005; // Move in a sine wave pattern const lng = (t * 30) % 360 - 180; const lat = Math.sin(t) * 40; marker.setLngLat([lng, lat]); requestAnimationFrame(animate); } animate(); ``` ## Animate Along Waypoints ```javascript const waypoints = [[2.35, 48.85], [-0.12, 51.50], [13.40, 52.52]]; let step = 0; const stepsPerSegment = 100; let subStep = 0; function animate() { const from = waypoints[step]; const to = waypoints[(step + 1) % waypoints.length]; const t = subStep / stepsPerSegment; marker.setLngLat([ from[0] + (to[0] - from[0]) * t, from[1] + (to[1] - from[1]) * t ]); subStep++; if (subStep > stepsPerSegment) { subStep = 0; step = (step + 1) % waypoints.length; } requestAnimationFrame(animate); } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Animate a Point Along a Route https://docs.mapatlas.xyz/sdk/examples/animate-point-along-route --- title: "Animate a Point Along a Route" category: "lines-polygons" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setData", "requestAnimationFrame"] tags: ["animate", "point", "route", "path", "moving-marker", "animation", "lineString", "symbol"] description: "Animate a point marker moving along a defined route path on the map" --- # Animate a Point Along a Route Move a point marker smoothly along a route path using frame-by-frame animation.
## How It Works 1. Add a GeoJSON `Point` source at the starting position 2. Add a `circle` (or `symbol`) layer to display the point 3. On each animation frame, update the point's coordinates using `setData()` ## Key Pattern ```javascript // 1. Add point source at start position map.addSource('point', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'Point', coordinates: route[0] } } }); // 2. Display as circle map.addLayer({ id: 'moving-point', type: 'circle', source: 'point', paint: { 'circle-radius': 10, 'circle-color': '#3b82f6', 'circle-stroke-width': 3, 'circle-stroke-color': '#fff' } }); // 3. Animate along route let step = 0; function animate() { if (step >= route.length) return; map.getSource('point').setData({ type: 'Feature', geometry: { type: 'Point', coordinates: route[step] } }); step++; requestAnimationFrame(animate); } animate(); ``` ## Interpolation for Smooth Movement Interpolate between waypoints to get smooth frame-by-frame movement: ```javascript function interpolate(from, to, t) { return [ from[0] + (to[0] - from[0]) * t, from[1] + (to[1] - from[1]) * t ]; } // Generate 60 steps between each pair of waypoints for (let i = 0; i < waypoints.length - 1; i++) { for (let s = 0; s < 60; s++) { const t = s / 60; points.push(interpolate(waypoints[i], waypoints[i + 1], t)); } } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Animate a Point https://docs.mapatlas.xyz/sdk/examples/animate-point --- title: "Animate a Point" category: "special" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "addSource", "addLayer", "setData", "requestAnimationFrame"] tags: ["animate", "point", "circle", "moving", "orbit", "sine", "cosine", "requestAnimationFrame"] description: "Animate a point on the map using requestAnimationFrame to create smooth movement" --- # Animate a Point Move a point continuously on the map using `requestAnimationFrame` for smooth, frame-based animation.
## How It Works Use `requestAnimationFrame` to call a function on every browser repaint (~60fps). On each frame, compute the new position and call `setData()` to move the point. ## Key Pattern ```javascript // 1. Add point source map.addSource('point', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} } }); // 2. Display as circle layer map.addLayer({ id: 'point', type: 'circle', source: 'point', paint: { 'circle-radius': 12, 'circle-color': '#f59e0b' } }); // 3. Animate position on each frame let t = 0; function animate() { t += 0.01; const lng = Math.cos(t) * 60; const lat = Math.sin(t) * 30; map.getSource('point').setData({ type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: {} }); requestAnimationFrame(animate); } animate(); ``` ## Stop and Resume Animation ```javascript let animId = null; let running = false; function start() { if (running) return; running = true; function tick() { if (!running) return; // update position... animId = requestAnimationFrame(tick); } tick(); } function stop() { running = false; if (animId) cancelAnimationFrame(animId); } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Arc Layer — Flight Routes https://docs.mapatlas.xyz/sdk/examples/arc-layer --- title: "Arc Layer — Flight Routes" category: "data-visualization" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setData"] tags: ["arc", "line", "flow", "animation", "geojson", "flight", "routes", "connections"] description: "Visualize connections between locations using animated curved arc lines — great for flight routes, migration flows, and network links" --- # Arc Layer — Flight Routes Visualize connections between cities using animated curved arcs. Each arc is a bezier curve drawn between an origin and destination, colored by direction and animated with a flowing dash effect.
## How It Works Each arc is a **quadratic bezier curve** computed in JavaScript between two `[lng, lat]` coordinates. The curve is added as a GeoJSON `LineString` source, then rendered with a `line` layer. A flowing animation is achieved by cycling through `line-dasharray` values each frame using `requestAnimationFrame`. ### Step 1 — Generate bezier arc coordinates ```javascript function bezierArc(from, to, steps = 80) { const coords = []; const midLng = (from[0] + to[0]) / 2; const midLat = (from[1] + to[1]) / 2; const dist = Math.sqrt(Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)); const ctrl = [midLng, midLat + dist * 0.25]; // control point lifted upward for (let i = 0; i <= steps; i++) { const t = i / steps; const lng = (1-t)*(1-t)*from[0] + 2*(1-t)*t*ctrl[0] + t*t*to[0]; const lat = (1-t)*(1-t)*from[1] + 2*(1-t)*t*ctrl[1] + t*t*to[1]; coords.push([lng, lat]); } return coords; } ``` ### Step 2 — Add source and layer ```javascript map.addSource('arc-0', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: bezierArc(from, to) } } }); map.addLayer({ id: 'arc-line-0', type: 'line', source: 'arc-0', paint: { 'line-color': '#ef4444', 'line-width': 2, 'line-opacity': 0.9, 'line-dasharray': [0, 4, 3], } }); ``` ### Step 3 — Animate the dash ```javascript const dashArraySequence = [ [0, 4, 3], [0.5, 4, 2.5], [1, 4, 2], /* ... */ ]; let step = 0; function animate(timestamp) { const newStep = Math.floor((timestamp / 80) % dashArraySequence.length); if (newStep !== step) { step = newStep; map.setPaintProperty('arc-line-0', 'line-dasharray', dashArraySequence[step]); } requestAnimationFrame(animate); } requestAnimationFrame(animate); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Change Building Color Based on Zoom Level https://docs.mapatlas.xyz/sdk/examples/building-color-zoom --- title: "Change Building Color Based on Zoom Level" category: "labels" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addLayer", "setPaintProperty", "fill-extrusion-color", "interpolate", "zoom"] tags: ["building", "3D", "zoom", "color", "fill-extrusion", "interpolate", "zoom-based", "style"] description: "Style 3D buildings with colors that change dynamically based on the current zoom level" --- # Change Building Color Based on Zoom Level Use the `interpolate` expression with `zoom` to change 3D building colors as the user zooms in and out.
Zoom in to see building colors transition from grey → blue → gold.
## Zoom-Based Color with `interpolate` Use the `interpolate` expression with `['zoom']` as the input to smoothly transition colors between zoom levels: ```javascript // Add your building polygons as a GeoJSON source map.addSource('buildings', { type: 'geojson', data: { type: 'FeatureCollection', features: [ { type: 'Feature', properties: { height: 80 }, geometry: { type: 'Polygon', coordinates: [[[lng1,lat1],[lng2,lat1],[lng2,lat2],[lng1,lat2],[lng1,lat1]]] } }, // ... more building polygons ] } }); map.addLayer({ id: 'buildings-3d', type: 'fill-extrusion', source: 'buildings', paint: { 'fill-extrusion-height': ['get', 'height'], 'fill-extrusion-base': 0, 'fill-extrusion-opacity': 0.85, // Color changes based on zoom 'fill-extrusion-color': [ 'interpolate', ['linear'], ['zoom'], 13, '#94a3b8', // grey at zoom 13 15, '#3b82f6', // blue at zoom 15 17, '#f59e0b', // gold at zoom 17+ ], } }); ``` ## Step-Based Color (Discrete Jumps) Use `step` for discrete color jumps instead of smooth transitions: ```javascript 'fill-extrusion-color': [ 'step', ['zoom'], '#94a3b8', // default (zoom < 14) 14, '#3b82f6', // zoom >= 14 → blue 16, '#f59e0b', // zoom >= 16 → gold 18, '#ef4444', // zoom >= 18 → red ] ``` ## Update Color at Runtime ```javascript map.setPaintProperty('buildings-3d', 'fill-extrusion-color', [ 'interpolate', ['linear'], ['zoom'], 13, '#e2e8f0', 16, '#22c55e', ]); ``` ## Also Apply to Height and Opacity ```javascript paint: { // Height from data 'fill-extrusion-height': ['get', 'height'], 'fill-extrusion-base': ['get', 'min_height'], // Fade in as zoom increases 'fill-extrusion-opacity': [ 'interpolate', ['linear'], ['zoom'], 13, 0, // invisible at zoom 13 14, 0.85 // fully visible at zoom 14 ], // Color by zoom 'fill-extrusion-color': [ 'interpolate', ['linear'], ['zoom'], 14, '#94a3b8', 17, '#3b82f6', ], } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Change the Case of Labels https://docs.mapatlas.xyz/sdk/examples/change-label-case --- title: "Change the Case of Labels" category: "labels" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "setLayoutProperty", "text-transform", "uppercase", "lowercase", "none"] tags: ["label", "case", "text-transform", "uppercase", "lowercase", "capitalize", "symbol", "text"] description: "Transform map label text to uppercase, lowercase, or default case using text-transform" --- # Change the Case of Labels Use the `text-transform` layout property to render map labels in uppercase, lowercase, or default case.
## `text-transform` Property Set `text-transform` in a symbol layer's `layout` to control label casing: ```javascript map.addLayer({ id: 'my-labels', type: 'symbol', source: 'my-source', layout: { 'text-field': ['get', 'name'], 'text-transform': 'uppercase', // 'none' | 'uppercase' | 'lowercase' } }); ``` ## Update at Runtime ```javascript // Apply to a specific layer map.setLayoutProperty('my-labels', 'text-transform', 'uppercase'); // Apply to ALL symbol layers in the style map.getStyle().layers.forEach(layer => { if (layer.type === 'symbol') { map.setLayoutProperty(layer.id, 'text-transform', 'uppercase'); } }); ``` ## Values | Value | Effect | |---|---| | `'none'` | Use the text as-is from the data (default) | | `'uppercase'` | Convert all characters to uppercase | | `'lowercase'` | Convert all characters to lowercase | ## Data-Driven Case (per feature) ```javascript 'text-transform': [ 'match', ['get', 'type'], 'highway', 'uppercase', 'suburb', 'lowercase', 'none' // default ] ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Change a Layer's Color with Buttons https://docs.mapatlas.xyz/sdk/examples/change-layer-color --- title: "Change a Layer's Color with Buttons" category: "styling" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "setPaintProperty", "addLayer", "addSource"] tags: ["color", "setPaintProperty", "style", "layer", "dynamic", "buttons", "theme"] description: "Dynamically change a map layer's color at runtime using setPaintProperty" --- # Change a Layer's Color with Buttons Use `setPaintProperty()` to update a layer's color dynamically without reloading the map.
## setPaintProperty ```javascript // Change fill color map.setPaintProperty('my-layer', 'fill-color', '#ef4444'); // Change line color and width map.setPaintProperty('my-line-layer', 'line-color', '#22c55e'); map.setPaintProperty('my-line-layer', 'line-width', 5); // Change circle radius and color map.setPaintProperty('my-circle-layer', 'circle-color', '#8b5cf6'); map.setPaintProperty('my-circle-layer', 'circle-radius', 15); // Change opacity map.setPaintProperty('my-layer', 'fill-opacity', 0.8); ``` ## setLayoutProperty For layout properties (visibility, text, icon): ```javascript // Toggle layer visibility map.setLayoutProperty('my-layer', 'visibility', 'none'); // hide map.setLayoutProperty('my-layer', 'visibility', 'visible'); // show ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Customize Camera Animations https://docs.mapatlas.xyz/sdk/examples/customize-camera-animations --- title: "Customize Camera Animations" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "flyTo", "easeTo", "jumpTo", "AnimationOptions", "CameraOptions"] tags: ["camera", "animation", "flyTo", "easeTo", "jumpTo", "duration", "easing", "curve", "speed"] description: "Control camera animation speed, easing curves, and behavior with flyTo and easeTo options" --- # Customize Camera Animations Fine-tune how the map camera moves using animation options like `duration`, `easing`, `curve`, and `speed` with `flyTo()` and `easeTo()`.
## `flyTo` — Flight Animation `flyTo` zooms out, pans, and zooms back in, simulating a flight arc. ```javascript map.flyTo({ center: [lng, lat], zoom: 12, speed: 1.2, // how fast to fly (default 1.2) curve: 1.42, // arc height (higher = more zoom-out) duration: 3000, // ms (overrides speed if set) essential: true, // not affected by prefers-reduced-motion }); ``` ## `easeTo` — Smooth Pan/Zoom `easeTo` smoothly transitions all camera properties simultaneously. ```javascript map.easeTo({ center: [lng, lat], zoom: 10, bearing: 45, pitch: 30, duration: 2000, easing: t => t * (2 - t), // ease-out quadratic }); ``` ## `jumpTo` — Instant Move `jumpTo` moves the camera immediately with no animation. ```javascript map.jumpTo({ center: [lng, lat], zoom: 12, bearing: 0, pitch: 0, }); ``` ## Custom Easing Functions The `easing` option takes a function `(t) => number` where `t` goes from 0 to 1: ```javascript // Linear (no easing) easing: t => t // Ease-in (slow start) easing: t => t * t // Ease-out (slow end) easing: t => t * (2 - t) // Ease-in-out easing: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t // Bounce easing: t => { if (t < 1 / 2.75) return 7.5625 * t * t; if (t < 2 / 2.75) { t -= 1.5 / 2.75; return 7.5625 * t * t + 0.75; } if (t < 2.5 / 2.75) { t -= 2.25 / 2.75; return 7.5625 * t * t + 0.9375; } t -= 2.625 / 2.75; return 7.5625 * t * t + 0.984375; } ``` ## Animation Events ```javascript map.on('movestart', () => console.log('camera started moving')); map.on('move', () => console.log('camera moving')); map.on('moveend', () => console.log('camera stopped')); map.on('zoomend', () => console.log('zoom finished')); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Customize the Map Transform Constrain https://docs.mapatlas.xyz/sdk/examples/customize-map-transform-constrain --- title: "Customize the Map Transform Constrain" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "transformRequest", "transformCameraUpdate", "maxBounds", "setMaxBounds", "setMinZoom", "setMaxZoom"] tags: ["constrain", "transform", "bounds", "zoom", "maxBounds", "transformCameraUpdate", "restrict", "limit"] description: "Constrain the map camera to specific bounds, zoom levels, and custom transform rules" --- # Customize the Map Transform Constrain Control how and where the map camera can move by setting bounds, zoom limits, and using `transformCameraUpdate` to enforce custom constraints.
## Set Max Bounds Restrict panning so the camera cannot move outside a bounding box: ```javascript // At initialization const map = new mapmetricsgl.Map({ container: 'map', style: '', maxBounds: [[-25, 34], [45, 72]], // [SW, NE] }); // At runtime map.setMaxBounds([[-25, 34], [45, 72]]); // Remove constraint map.setMaxBounds(null); ``` ## Limit Zoom Levels ```javascript // At initialization const map = new mapmetricsgl.Map({ minZoom: 3, maxZoom: 18, // ... }); // At runtime map.setMinZoom(3); map.setMaxZoom(12); // Remove limits map.setMinZoom(null); map.setMaxZoom(null); ``` ## Limit Pitch and Bearing ```javascript const map = new mapmetricsgl.Map({ minPitch: 0, maxPitch: 60, // default 60 // ... }); ``` ## `transformCameraUpdate` — Custom Constraints Use `transformCameraUpdate` to intercept and modify every camera update before it is applied: ```javascript const map = new mapmetricsgl.Map({ // ... transformCameraUpdate: ({ center, zoom, pitch, bearing, elevation, padding }) => { // Example: keep latitude above the equator return { center: { lng: center.lng, lat: Math.max(0, center.lat), // clamp lat to >= 0 }, zoom, pitch, bearing, }; } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Style Lines with a Data-Driven Property https://docs.mapatlas.xyz/sdk/examples/data-driven-lines --- title: "Style Lines with a Data-Driven Property" category: "lines-polygons" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "line-color", "line-width", "get expression"] tags: ["line", "data-driven", "style", "expression", "geojson", "layer", "properties", "color", "width"] description: "Style map lines dynamically based on feature properties using data-driven expressions" --- # Style Lines with a Data-Driven Property Change line color and width based on feature data properties using MapMetrics GL expressions.

Lines are styled by speed property: 🔴 fast highway · 🟡 medium road · 🟢 slow street

## How It Works Use MapMetrics GL **expressions** in layer paint properties to read feature properties and apply styles dynamically. ## Color by Property Value ### `match` expression — exact value matching ```javascript 'line-color': [ 'match', ['get', 'type'], 'highway', '#ef4444', // red for highways 'road', '#eab308', // yellow for roads 'street', '#22c55e', // green for streets '#94a3b8' // default (gray) ] ``` ### `step` expression — numeric ranges ```javascript 'line-color': [ 'step', ['get', 'speed'], '#22c55e', // default (speed < 60) 60, '#eab308', // speed >= 60 100, '#ef4444' // speed >= 100 ] ``` ### `interpolate` expression — smooth gradient ```javascript 'line-color': [ 'interpolate', ['linear'], ['get', 'speed'], 0, '#22c55e', // green at 0 km/h 60, '#eab308', // yellow at 60 km/h 130, '#ef4444' // red at 130 km/h ] ``` ## Width by Property ```javascript 'line-width': [ 'interpolate', ['linear'], ['get', 'speed'], 0, 1, // thin at slow speed 130, 8 // thick at high speed ] ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Disable Map Rotation https://docs.mapatlas.xyz/sdk/examples/disable-map-rotation --- title: "Disable Map Rotation" category: "controls" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "dragRotate", "touchZoomRotate", "keyboard"] tags: ["rotation", "disable", "lock", "bearing", "dragRotate", "touchZoomRotate", "controls"] description: "Disable map rotation from mouse drag, touch gestures, and keyboard shortcuts" --- # Disable Map Rotation Prevent users from rotating the map by disabling drag-rotate, touch-rotate, and keyboard rotation.

Try right-click dragging or using the compass — rotation is disabled on this map.

## How to Disable Rotation Call these three methods after map initialization to fully disable rotation from all input sources: ```javascript // Disable right-click drag rotation (mouse) map.dragRotate.disable(); // Disable two-finger rotation (touch devices) map.touchZoomRotate.disableRotation(); // Disable keyboard rotation (Alt + Arrow keys) map.keyboard.disable(); ``` ## Disable at Initialization You can also disable rotation when creating the map: ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', center: [0, 20], zoom: 2, dragRotate: false // disable drag rotation at init }); // Still need to disable touch rotation separately map.touchZoomRotate.disableRotation(); ``` ## Re-enable Rotation ```javascript // Re-enable rotation later if needed map.dragRotate.enable(); map.touchZoomRotate.enableRotation(); map.keyboard.enable(); ``` ## Hide the Compass If rotation is disabled, you can also hide the compass from `NavigationControl`: ```javascript map.addControl(new mapmetricsgl.NavigationControl({ showCompass: false // hide compass since rotation is disabled }), 'top-right'); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Disable Scroll Zoom https://docs.mapatlas.xyz/sdk/examples/disable-scroll-zoom --- title: "Disable Scroll Zoom" category: "controls" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "scrollZoom", "doubleClickZoom", "touchZoomRotate"] tags: ["scroll", "zoom", "disable", "scrollZoom", "touch", "interaction", "controls"] description: "Disable scroll wheel zoom and touch pinch zoom to prevent accidental map zooming" --- # Disable Scroll Zoom Disable scroll wheel zooming to prevent the map from accidentally zooming when users scroll the page.

Try scrolling over the map — scroll zoom is disabled. Use the +/− buttons or pinch to zoom.

## Disable Scroll Zoom ```javascript // Disable scroll wheel zoom map.scrollZoom.disable(); // Re-enable later if needed map.scrollZoom.enable(); ``` ## Disable at Initialization ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', center: [0, 20], zoom: 2, scrollZoom: false // disable scroll zoom at init }); ``` ## Disable All Zoom Interactions ```javascript // Disable scroll wheel zoom map.scrollZoom.disable(); // Disable double-click zoom map.doubleClickZoom.disable(); // Disable touch pinch zoom (keeps rotation) map.touchZoomRotate.disable(); ``` ## Common Use Case: Embed in a Scrollable Page When embedding a map in a long scrollable page, scroll zoom causes a frustrating experience. The recommended pattern is to only enable scroll zoom when the user clicks/focuses the map: ```javascript map.scrollZoom.disable(); // Enable on map focus map.getCanvas().addEventListener('focus', () => { map.scrollZoom.enable(); }); // Disable when leaving map map.getCanvas().addEventListener('blur', () => { map.scrollZoom.disable(); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Display a Popup https://docs.mapatlas.xyz/sdk/examples/display-popup --- title: "Display a Popup" category: "popups" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Popup", "setLngLat", "setHTML", "addTo", "remove"] tags: ["popup", "tooltip", "click", "marker", "info", "overlay", "html"] description: "Display a popup at a specific location on the map with custom HTML content" --- # Display a Popup Show a popup with custom HTML at a specific map location, or trigger it from a marker or click event.
## Basic Popup ```javascript const popup = new mapmetricsgl.Popup({ closeButton: true, closeOnClick: true }) .setLngLat([2.3499, 48.853]) .setHTML('Paris
Capital of France') .addTo(map); // Remove programmatically popup.remove(); ``` ## Popup Options ```javascript new mapmetricsgl.Popup({ closeButton: true, // Show × button (default: true) closeOnClick: true, // Close when map is clicked (default: true) anchor: 'bottom', // Anchor: 'top' | 'bottom' | 'left' | 'right' | 'center' offset: [0, -10], // Pixel offset [x, y] maxWidth: '300px', // Max popup width }) ``` ## Popup with Text vs HTML ```javascript // Plain text (safe, auto-escaped) popup.setText('Hello World'); // HTML content popup.setHTML('Title

Description

'); // Set coordinates popup.setLngLat([lng, lat]); ``` ## Attach Popup to Marker ```javascript const marker = new mapmetricsgl.Marker() .setLngLat([2.3499, 48.853]) .setPopup( new mapmetricsgl.Popup().setHTML('Paris') ) .addTo(map); // Open/close programmatically marker.togglePopup(); ``` ## Popup on Feature Click ```javascript map.on('click', 'my-layer', (e) => { const feature = e.features[0]; new mapmetricsgl.Popup() .setLngLat(e.lngLat) .setHTML(`${feature.properties.name}`) .addTo(map); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Display a Remote SVG Symbol https://docs.mapatlas.xyz/sdk/examples/display-remote-svg-symbol --- title: "Display a Remote SVG Symbol" category: "icons-images" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addImage", "addSource", "addLayer", "fetch", "Image", "icon-image"] tags: ["svg", "symbol", "remote", "icon", "fetch", "image", "addImage", "url", "external"] description: "Fetch an SVG from a URL and render it as a map icon using a canvas" --- # Display a Remote SVG Symbol Fetch an SVG from a remote URL, render it onto a canvas using an `Image` element, and register it with `map.addImage()` for use in symbol layers.
## Approach: SVG → Blob → Image → Canvas → addImage SVG images cannot be passed directly to `map.addImage()`. The recommended workflow is: 1. Get the SVG string (inline or fetched) 2. Create a `Blob` and object URL from the SVG 3. Draw it onto a `` via an `Image` element 4. Extract `ImageData` and register with `map.addImage()` ```javascript function svgToImageData(svgString, size) { return new Promise((resolve, reject) => { const blob = new Blob([svgString], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); const img = new Image(size, size); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; canvas.getContext('2d').drawImage(img, 0, 0, size, size); URL.revokeObjectURL(url); resolve(canvas.getContext('2d').getImageData(0, 0, size, size)); }; img.onerror = reject; img.src = url; }); } ``` ## Fetch SVG from a Remote URL ```javascript async function loadRemoteSvg(url, size) { const response = await fetch(url); const svgText = await response.text(); return svgToImageData(svgText, size); } map.on('load', async () => { const imageData = await loadRemoteSvg('https://example.com/pin.svg', 48); map.addImage('remote-svg', imageData); // Use in a symbol layer map.addLayer({ id: 'svg-layer', type: 'symbol', source: 'my-source', layout: { 'icon-image': 'remote-svg', 'icon-size': 1.0, 'icon-allow-overlap': true, } }); }); ``` ## Using an Inline SVG String ```javascript const svg = ` `; svgToImageData(svg, 48).then(imageData => { map.addImage('dot-icon', imageData); }); ``` ## CORS Note When fetching SVGs from external servers, the server must send `Access-Control-Allow-Origin: *`. If CORS is blocked, use an inline SVG string or a data URI instead. ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Display the Globe https://docs.mapatlas.xyz/sdk/examples/display-whole-world # Display the Globe This example demonstrates how to display a 3D globe view with an optional animated starfield background. You can toggle the stars effect on/off using the button below. ## Interactive Demo
--- ## Code Implementation Below are two implementation examples: one with the starfield effect and one without. ### Option 1: Globe with Stars Background (Full Interactive Experience) This implementation includes: - Animated starfield background with multiple layers - Stars that rotate and move with globe interactions - Parallax mouse effect for depth - Toggle button to show/hide stars ```html
``` ### Option 2: Simple Globe (No Stars) This is a basic implementation without the starfield effect - just the globe: ```html
``` --- ## Key Features Explained ### Toggle Button - **Location**: Top-right corner of the map - **Initial State**: Stars are visible by default - **Functionality**: Click to show/hide the starfield background - **Button Text**: Changes between "Hide Stars" and "Show Stars" ### Stars Animation The starfield includes: 1. **Multiple Star Layers**: Large bright stars, medium stars, and tiny distant stars 2. **Color Variety**: White, blue, yellow, orange, and other star colors for realism 3. **Twinkling Effect**: Stars gently pulse with opacity changes 4. **Interactive Rotation**: Stars rotate as you move the globe 5. **Smooth Transitions**: 0.3s fade effect when toggling visibility ### Globe Projection - Uses `map.setProjection({ type: "globe" })` to display Earth in 3D - Interactive rotation, pitch, and zoom ### Customization Options **Change toggle button position:** ```css #toggleStars { top: 20px; /* Distance from top */ right: 20px; /* Distance from right */ } ``` **Adjust star intensity:** ```css #stars::before { opacity: 0.8; /* Reduce for dimmer stars */ } ``` --- ## Browser Compatibility This example works in all modern browsers that support: - CSS animations - CSS filter effects - WebGL (for globe rendering) - ES6 JavaScript Tested on: Chrome, Firefox, Safari, Edge --- # Create a Draggable Marker https://docs.mapatlas.xyz/sdk/examples/draggable-marker --- title: "Create a Draggable Marker" category: "interaction" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Marker", "on('dragend')", "getLngLat"] tags: ["marker", "draggable", "drag", "dragend", "interaction", "lngLat"] description: "Create markers that users can drag to new locations, and capture the new position" --- # Create a Draggable Marker Create markers that users can drag to any location on the map, and capture the updated coordinates.
🔵 Drag the blue marker to see its new coordinates
## How It Works Pass `draggable: true` to the `Marker` constructor, then listen to `drag` and `dragend` events to get the new position. ## Basic Draggable Marker ```javascript const marker = new mapmetricsgl.Marker({ color: '#3b82f6', draggable: true // Enable dragging }) .setLngLat([2.349902, 48.852966]) .addTo(map); // Get new position when drag ends marker.on('dragend', () => { const { lng, lat } = marker.getLngLat(); console.log(`Marker dropped at: ${lng}, ${lat}`); }); ``` ## Listen to All Drag Events ```javascript // Fires when drag starts marker.on('dragstart', () => { console.log('Started dragging'); }); // Fires continuously while dragging marker.on('drag', () => { const pos = marker.getLngLat(); console.log(`Dragging: ${pos.lng}, ${pos.lat}`); }); // Fires when drag ends marker.on('dragend', () => { const pos = marker.getLngLat(); console.log(`Dropped at: ${pos.lng}, ${pos.lat}`); }); ``` ## Use Case: Pick-up / Drop-off Points ```javascript const pickup = new mapmetricsgl.Marker({ color: '#22c55e', draggable: true }) .setLngLat([2.34, 48.85]) .addTo(map); const dropoff = new mapmetricsgl.Marker({ color: '#ef4444', draggable: true }) .setLngLat([2.36, 48.86]) .addTo(map); function getRoute() { const from = pickup.getLngLat(); const to = dropoff.getLngLat(); console.log('From:', from, 'To:', to); // Call Directions API with these coordinates } pickup.on('dragend', getRoute); dropoff.on('dragend', getRoute); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Create a Draggable Point https://docs.mapatlas.xyz/sdk/examples/draggable-point --- title: "Create a Draggable Point" category: "user-interaction" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setData", "mousedown", "mousemove", "mouseup"] tags: ["drag", "draggable", "point", "geojson", "interactive", "mouse", "move", "edit"] description: "Create a draggable GeoJSON point that the user can move by clicking and dragging" --- # Create a Draggable Point Allow users to drag a GeoJSON point on the map using mouse events.
Click and drag the point to move it
## How It Works 1. Add a GeoJSON `Point` source and display it as a `circle` layer 2. Listen for `mousedown` on the layer to start dragging 3. On `mousemove`, update the point position with `setData()` 4. On `mouseup`, end the drag and re-enable map panning ## Key Pattern ```javascript let isDragging = false; // Start drag when user clicks the point map.on('mousedown', 'my-point', (e) => { e.preventDefault(); isDragging = true; map.dragPan.disable(); // prevent map from panning map.getCanvas().style.cursor = 'grabbing'; }); // Move point while dragging map.on('mousemove', (e) => { if (!isDragging) return; map.getSource('my-point').setData({ type: 'Feature', geometry: { type: 'Point', coordinates: [e.lngLat.lng, e.lngLat.lat] }, properties: {} }); }); // End drag map.on('mouseup', () => { isDragging = false; map.dragPan.enable(); map.getCanvas().style.cursor = ''; }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Draw a Circle https://docs.mapatlas.xyz/sdk/examples/draw-a-circle --- title: "Draw a Circle" category: "lines-polygons" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "addSource", "addLayer", "circle-radius", "circle-color", "fill"] tags: ["circle", "polygon", "draw", "radius", "geojson", "layer", "shape", "area"] description: "Draw circles and filled areas on the map using circle layers or polygon GeoJSON" --- # Draw a Circle Draw circles on the map using the `circle` layer type or as filled polygon areas.

Click the map to add a circle at that location.

## Method 1: Circle Layer (Pixel-based) The simplest way — uses pixel radius, so the circle size stays constant regardless of zoom level: ```javascript map.addSource('points', { type: 'geojson', data: { type: 'FeatureCollection', features: [ { type: 'Feature', geometry: { type: 'Point', coordinates: [2.35, 48.85] }, properties: {} } ] } }); map.addLayer({ id: 'circle-layer', type: 'circle', source: 'points', paint: { 'circle-radius': 20, // pixels 'circle-color': '#3b82f6', 'circle-opacity': 0.5, 'circle-stroke-width': 2, 'circle-stroke-color': '#1d4ed8' } }); ``` ## Method 2: Polygon Circle (Geographic radius) For a circle with a real-world radius (e.g., 5km), generate polygon coordinates: ```javascript function createGeoCircle(center, radiusKm, points = 64) { const coords = []; for (let i = 0; i < points; i++) { const angle = (i / points) * 2 * Math.PI; const dx = radiusKm / 111.32; // degrees longitude const dy = radiusKm / 110.574; // degrees latitude coords.push([ center[0] + dx * Math.cos(angle), center[1] + dy * Math.sin(angle) ]); } coords.push(coords[0]); // close the ring return coords; } map.addSource('area', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'Polygon', coordinates: [createGeoCircle([2.35, 48.85], 50)] // 50km radius } } }); map.addLayer({ id: 'area-fill', type: 'fill', source: 'area', paint: { 'fill-color': '#3b82f6', 'fill-opacity': 0.2 } }); map.addLayer({ id: 'area-outline', type: 'line', source: 'area', paint: { 'line-color': '#3b82f6', 'line-width': 2 } }); ``` ## Data-Driven Circle Size ```javascript map.addLayer({ id: 'circles', type: 'circle', source: 'points', paint: { 'circle-radius': ['get', 'radius'], // from feature property 'circle-color': ['get', 'color'], // from feature property 'circle-opacity': 0.6 } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Draw GeoJSON Points https://docs.mapatlas.xyz/sdk/examples/draw-geojson-points --- title: "Draw GeoJSON Points" category: "geometry" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "addSource", "addLayer", "circle", "Point", "FeatureCollection"] tags: ["points", "geojson", "circle", "dot", "marker", "layer", "data"] description: "Draw multiple points on the map from a GeoJSON FeatureCollection using a circle layer" --- # Draw GeoJSON Points Render multiple points from a GeoJSON FeatureCollection using a circle layer — efficient for large datasets.
## GeoJSON Points vs Markers | | GeoJSON + Circle Layer | Marker | |---|---|---| | **Performance** | Excellent (WebGL rendered) | Good (DOM elements) | | **Best for** | Hundreds/thousands of points | Few points with rich HTML | | **Clustering** | Built-in support | Manual | | **Custom HTML** | No | Yes | ## Draw Points from GeoJSON ```javascript map.addSource('points', { type: 'geojson', data: { type: 'FeatureCollection', features: [ { type: 'Feature', properties: { name: 'Paris' }, geometry: { type: 'Point', coordinates: [2.35, 48.85] } }, { type: 'Feature', properties: { name: 'London' }, geometry: { type: 'Point', coordinates: [-0.12, 51.50] } } ] } }); map.addLayer({ id: 'points-layer', type: 'circle', source: 'points', paint: { 'circle-radius': 8, 'circle-color': '#3b82f6', 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Filter Symbols by Text Input https://docs.mapatlas.xyz/sdk/examples/filter-by-text-input --- title: "Filter Symbols by Text Input" category: "filtering" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "setFilter", "addSource", "addLayer"] tags: ["filter", "search", "text", "input", "setFilter", "layer", "dynamic"] description: "Filter map features in real time as the user types in a search input" --- # Filter Symbols by Text Input Filter map features dynamically as the user types in a search box using `setFilter()`.
## Filter by Text Input ```javascript const searchInput = document.getElementById('search'); searchInput.addEventListener('input', (e) => { const query = e.target.value.trim().toLowerCase(); if (!query) { map.setFilter('my-layer', null); // show all return; } // Filter: feature name contains the query string map.setFilter('my-layer', [ 'in', query, ['downcase', ['get', 'name']] ]); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Filter Features by Toggle List https://docs.mapatlas.xyz/sdk/examples/filter-by-toggle-list --- title: "Filter Features by Toggle List" category: "user-interaction" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setFilter", "filter expression", "in expression"] tags: ["filter", "toggle", "checkbox", "category", "buttons", "setFilter", "layer", "interactive"] description: "Filter map features by toggling category buttons that show or hide specific feature types" --- # Filter Features by Toggle List Toggle category buttons to show or hide specific feature types on the map using `setFilter()`.

Toggle categories:

## Filter by Active Categories Track which categories are selected, then build a filter expression: ```javascript const activeCategories = new Set(['restaurant', 'cafe', 'hotel']); function applyFilter() { const active = [...activeCategories]; if (active.length === 0) { // Hide everything map.setFilter('places-layer', ['==', '1', '0']); } else { // Show features whose category is in the active list map.setFilter('places-layer', [ 'in', ['get', 'category'], ['literal', active] ]); } } // Toggle a category on/off function toggleCategory(category) { if (activeCategories.has(category)) { activeCategories.delete(category); } else { activeCategories.add(category); } applyFilter(); } ``` ## Filter Expressions ```javascript // Show only restaurants map.setFilter('layer', ['==', ['get', 'category'], 'restaurant']); // Show restaurants OR cafes map.setFilter('layer', [ 'in', ['get', 'category'], ['literal', ['restaurant', 'cafe']] ]); // Remove filter (show all) map.setFilter('layer', null); // Hide all map.setFilter('layer', ['==', '1', '0']); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Filter Features Within a Layer https://docs.mapatlas.xyz/sdk/examples/filter-within-layer --- title: "Filter Features Within a Layer" category: "lines-polygons" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setFilter", "filter"] tags: ["filter", "layer", "setFilter", "geojson", "expression", "query", "search", "dynamic"] description: "Dynamically filter which features are displayed in a map layer using setFilter" --- # Filter Features Within a Layer Show or hide specific map features dynamically using `setFilter()` without reloading data.
## How It Works `setFilter()` applies a filter expression to a layer — only features that match the expression are displayed. The data stays in the source; only the rendering changes. ## Basic Filter Patterns ```javascript // Show all features (clear filter) map.setFilter('my-layer', null); // Match exact string value map.setFilter('my-layer', ['==', ['get', 'type'], 'capital']); // Match boolean property map.setFilter('my-layer', ['==', ['get', 'port'], true]); // Numeric comparison map.setFilter('my-layer', ['>=', ['get', 'population'], 1000000]); // Multiple conditions (AND) map.setFilter('my-layer', [ 'all', ['==', ['get', 'type'], 'capital'], ['>=', ['get', 'population'], 1000000] ]); // Multiple conditions (OR) map.setFilter('my-layer', [ 'any', ['==', ['get', 'type'], 'capital'], ['==', ['get', 'port'], true] ]); ``` ## Filter by Text Search ```javascript const searchInput = document.getElementById('search'); searchInput.addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); if (!query) { map.setFilter('cities-layer', null); return; } // Filter by name containing the search text map.setFilter('cities-layer', [ 'in', query, ['downcase', ['get', 'name']] ]); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Fit a Map to a Bounding Box https://docs.mapatlas.xyz/sdk/examples/fit-to-bounding-box --- title: "Fit a Map to a Bounding Box" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "fitBounds", "LngLatBounds"] tags: ["camera", "bounds", "fitBounds", "bounding-box", "zoom"] description: "Automatically fit the map view to show all markers or a region using fitBounds" --- # Fit a Map to a Bounding Box Automatically adjust the map camera to fit any set of coordinates or a region into view using `fitBounds`.
## How It Works Use `map.fitBounds()` to fit the map view to any rectangular area defined by two corners `[southwest, northeast]`. ## Basic Usage ```javascript // Fit to a bounding box [southwest corner, northeast corner] map.fitBounds([ [-74.1, 40.6], // Southwest corner [lng, lat] [-73.9, 40.9] // Northeast corner [lng, lat] ], { padding: 40 // Padding in pixels around the bounds }); ``` ## Fit to a Set of Markers ```javascript // Calculate bounds from a set of points const bounds = new mapmetricsgl.LngLatBounds(); const points = [ [-74.006, 40.7128], // New York [-0.1276, 51.5074], // London [2.349902, 48.852966] // Paris ]; points.forEach(point => bounds.extend(point)); map.fitBounds(bounds, { padding: 60 }); ``` ## Options | Option | Type | Description | |--------|------|-------------| | `padding` | `number` or `object` | Padding in pixels. Can be `{ top, bottom, left, right }` | | `maxZoom` | `number` | Maximum zoom level to use | | `animate` | `boolean` | Whether to animate (default: `true`) | | `duration` | `number` | Animation duration in milliseconds | | `bearing` | `number` | Map bearing after fitting | | `pitch` | `number` | Map pitch after fitting | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Fit Map to a LineString https://docs.mapatlas.xyz/sdk/examples/fit-to-linestring --- title: "Fit Map to a LineString" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "fitBounds", "addSource", "addLayer", "LngLatBounds"] tags: ["fitBounds", "linestring", "bounds", "zoom", "camera", "route", "auto-fit", "padding"] description: "Automatically fit the map view to the bounds of a LineString or any set of coordinates" --- # Fit Map to a LineString Automatically zoom and pan to fit a LineString or route using `fitBounds()`.
## Fit to a Set of Coordinates Use `LngLatBounds` to compute the bounding box, then call `fitBounds()`: ```javascript const coordinates = [ [2.3499, 48.853], // Paris [-0.1276, 51.5074], // London [13.405, 52.52], // Berlin ]; // Build bounds from all coordinates const bounds = coordinates.reduce( (bounds, coord) => bounds.extend(coord), new mapmetricsgl.LngLatBounds(coordinates[0], coordinates[0]) ); // Fit map to bounds map.fitBounds(bounds, { padding: 60, // pixels of padding on each side duration: 1000, // animation duration in ms }); ``` ## fitBounds Options ```javascript map.fitBounds(bounds, { padding: { top: 50, bottom: 50, left: 50, right: 50 }, // or just a number maxZoom: 15, // don't zoom in more than this duration: 1000, // animation ms (0 = instant) bearing: 0, // target bearing pitch: 0, // target pitch }); ``` ## From a GeoJSON Feature ```javascript // Given a LineString feature const feature = { type: 'Feature', geometry: { type: 'LineString', coordinates: [[lng1, lat1], [lng2, lat2], ...] } }; const bounds = feature.geometry.coordinates.reduce( (b, c) => b.extend(c), new mapmetricsgl.LngLatBounds( feature.geometry.coordinates[0], feature.geometry.coordinates[0] ) ); map.fitBounds(bounds, { padding: 60 }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # 3D Buildings with Shadow in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-3d-buildings-with-shadow # 3D Buildings with Shadow in Flutter This tutorial shows how to display 3D extruded buildings with realistic shadow effects based on a simulated light source — adding depth and realism to your map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## 3D Buildings with Light and Shadow Add extruded buildings with shadow color and adjustable light position: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BuildingsWithShadowScreen extends StatefulWidget { @override _BuildingsWithShadowScreenState createState() => _BuildingsWithShadowScreenState(); } class _BuildingsWithShadowScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('3D Buildings with Shadow')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8606, 2.3376), // Louvre area zoom: 16.5, tilt: 60.0, bearing: -30.0, ), onStyleLoaded: () { _addBuildingsWithShadow(); }, ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton.small( heroTag: 'morning', onPressed: () => _setLightAngle(90.0, 30.0), child: Icon(Icons.wb_twilight), tooltip: 'Morning Light', ), SizedBox(height: 8), FloatingActionButton.small( heroTag: 'noon', onPressed: () => _setLightAngle(180.0, 80.0), child: Icon(Icons.wb_sunny), tooltip: 'Noon Light', ), SizedBox(height: 8), FloatingActionButton.small( heroTag: 'evening', onPressed: () => _setLightAngle(270.0, 20.0), child: Icon(Icons.nights_stay), tooltip: 'Evening Light', ), ], ), ); } void _addBuildingsWithShadow() { // Set the global light source for shadow casting mapController?.setLight( anchor: 'viewport', color: '#ffffff', intensity: 0.4, position: [1.5, 180.0, 40.0], // [radial, azimuthal, polar] ); // Add 3D buildings with shadow-aware colors mapController?.addFillExtrusionLayer( '3d-buildings-shadow', 'composite', sourceLayer: 'building', fillExtrusionColor: '#b0b0b0', fillExtrusionOpacity: 0.85, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], fillExtrusionVerticalGradient: true, // Darker at base, lighter at top minZoom: 14.0, ); } void _setLightAngle(double azimuthal, double polar) { mapController?.setLight( anchor: 'viewport', color: '#ffffff', intensity: 0.4, position: [1.5, azimuthal, polar], ); } } ``` ## Time-of-Day Shadow Simulation Simulate how building shadows change throughout the day: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ShadowTimeScreen extends StatefulWidget { @override _ShadowTimeScreenState createState() => _ShadowTimeScreenState(); } class _ShadowTimeScreenState extends State { MapMetricsController? mapController; double timeOfDay = 12.0; // 0-24 hours Timer? animTimer; bool isAnimating = false; // Map hour to light settings Map _lightForHour(double hour) { // Azimuthal: sun moves east (90°) → south (180°) → west (270°) final azimuthal = 90.0 + (hour - 6) * 15.0; // 6AM=90°, noon=180°, 6PM=270° // Polar: low at sunrise/sunset, high at noon final noonDist = (hour - 12).abs(); final polar = 80.0 - noonDist * 8.0; // 80° at noon, ~32° at 6AM/6PM // Intensity: brighter midday, dimmer morning/evening final intensity = 0.2 + (1.0 - noonDist / 6.0).clamp(0.0, 1.0) * 0.3; // Light color: warm at sunrise/sunset, white at noon String color; if (hour < 7 || hour > 19) { color = '#FF8C00'; // Deep orange } else if (hour < 9 || hour > 17) { color = '#FFB74D'; // Warm orange } else { color = '#FFFFFF'; // White } // Building color: warmer tones at golden hour String buildingColor; if (hour < 7 || hour > 19) { buildingColor = '#8B6914'; // Dark warm } else if (hour < 9 || hour > 17) { buildingColor = '#C0A060'; // Warm } else { buildingColor = '#b0b0b0'; // Neutral grey } return { 'azimuthal': azimuthal.clamp(45.0, 315.0), 'polar': polar.clamp(10.0, 80.0), 'intensity': intensity, 'color': color, 'buildingColor': buildingColor, }; } String _hourLabel(double hour) { final h = hour.toInt(); final m = ((hour - h) * 60).toInt(); final period = h >= 12 ? 'PM' : 'AM'; final displayH = h > 12 ? h - 12 : (h == 0 ? 12 : h); return '${displayH}:${m.toString().padLeft(2, '0')} $period'; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Shadow Time Simulation')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8606, 2.3376), zoom: 16.5, tilt: 55.0, bearing: -20.0, ), onStyleLoaded: () { _setupBuildings(); _updateLight(); }, ), // Time controls Positioned( bottom: 16, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( _hourLabel(timeOfDay), style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), ElevatedButton.icon( onPressed: isAnimating ? _stopAnimation : _startAnimation, icon: Icon(isAnimating ? Icons.pause : Icons.play_arrow), label: Text(isAnimating ? 'Pause' : 'Animate'), ), ], ), Slider( value: timeOfDay, min: 5.0, max: 21.0, divisions: 64, label: _hourLabel(timeOfDay), onChanged: (val) { setState(() => timeOfDay = val); _updateLight(); }, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('5 AM', style: TextStyle(fontSize: 11, color: Colors.grey)), Text('Noon', style: TextStyle(fontSize: 11, color: Colors.grey)), Text('9 PM', style: TextStyle(fontSize: 11, color: Colors.grey)), ], ), ], ), ), ), ), ], ), ); } void _setupBuildings() { final settings = _lightForHour(timeOfDay); mapController?.setLight( anchor: 'viewport', color: settings['color'], intensity: settings['intensity'], position: [1.5, settings['azimuthal'], settings['polar']], ); mapController?.addFillExtrusionLayer( '3d-buildings', 'composite', sourceLayer: 'building', fillExtrusionColor: settings['buildingColor'], fillExtrusionOpacity: 0.85, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], fillExtrusionVerticalGradient: true, minZoom: 14.0, ); } void _updateLight() { final settings = _lightForHour(timeOfDay); mapController?.setLight( anchor: 'viewport', color: settings['color'], intensity: settings['intensity'], position: [1.5, settings['azimuthal'], settings['polar']], ); mapController?.setPaintProperty( '3d-buildings', 'fill-extrusion-color', settings['buildingColor'], ); } void _startAnimation() { setState(() { isAnimating = true; if (timeOfDay >= 21.0) timeOfDay = 5.0; }); animTimer = Timer.periodic(Duration(milliseconds: 80), (_) { if (timeOfDay >= 21.0) { _stopAnimation(); return; } setState(() { timeOfDay += 0.05; }); _updateLight(); }); } void _stopAnimation() { animTimer?.cancel(); setState(() => isAnimating = false); } @override void dispose() { animTimer?.cancel(); super.dispose(); } } ``` ## Light Properties | Property | Type | Description | |----------|------|-------------| | `anchor` | `String` | `viewport` (relative to camera) or `map` (fixed direction) | | `color` | `String` | Light color hex string | | `intensity` | `double` | Brightness (0.0 - 1.0) | | `position` | `List` | `[radial, azimuthal, polar]` — distance, compass angle, elevation | | `fillExtrusionVerticalGradient` | `bool` | Darker at base, lighter at top | ## Next Steps - [3D Buildings](./flutter-3d-buildings) — Basic 3D building setup - [Building Color by Zoom](./flutter-building-color-zoom) — Zoom-dependent colors - [Sky, Fog & Terrain](./flutter-sky-fog-terrain) — Atmospheric effects --- **Tip**: Use `anchor: 'viewport'` so shadows rotate with the camera (feels natural), or `anchor: 'map'` so shadows stay fixed to compass direction (geographically accurate). The time-of-day animation makes a great demo for real estate or urban planning apps. --- # 3D Building Visualization in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-3d-buildings # 3D Building Visualization in Flutter This tutorial shows how to display 3D extruded buildings on your MapMetrics Flutter map — great for urban planning, real estate, and city exploration apps. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Enable 3D Buildings Display 3D buildings by tilting the camera and adding a fill-extrusion layer: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class Buildings3DScreen extends StatefulWidget { @override _Buildings3DScreenState createState() => _Buildings3DScreenState(); } class _Buildings3DScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('3D Buildings')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8584, 2.2945), // Eiffel Tower area zoom: 16.0, tilt: 60.0, // Tilt to see buildings in 3D bearing: 45.0, // Rotate for a better perspective ), onStyleLoaded: () { _add3DBuildings(); }, ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( heroTag: 'tilt_up', onPressed: () => _setTilt(60.0), child: Icon(Icons.landscape), tooltip: '3D View', ), SizedBox(height: 8), FloatingActionButton( heroTag: 'tilt_down', onPressed: () => _setTilt(0.0), child: Icon(Icons.map), tooltip: '2D View', ), ], ), ); } void _add3DBuildings() { // Add a 3D fill-extrusion layer for buildings mapController?.addFillExtrusionLayer( '3d-buildings', 'composite', // source (depends on your style) sourceLayer: 'building', fillExtrusionColor: '#aaaaaa', fillExtrusionOpacity: 0.6, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], minZoom: 14.0, ); } void _setTilt(double tilt) async { final position = await mapController?.getCameraPosition(); if (position != null) { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: position.target, zoom: position.zoom, tilt: tilt, bearing: position.bearing, ), ), ); } } } ``` ## 3D Buildings with Custom Colors Color buildings based on their height for a dramatic visualization: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ColoredBuildings3DScreen extends StatefulWidget { @override _ColoredBuildings3DScreenState createState() => _ColoredBuildings3DScreenState(); } class _ColoredBuildings3DScreenState extends State { MapMetricsController? mapController; String colorScheme = 'height'; // 'height', 'blue', 'warm' @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Colored 3D Buildings')), body: Column( children: [ // Color scheme selector Container( padding: EdgeInsets.all(8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _schemeChip('Height', 'height'), _schemeChip('Cool Blue', 'blue'), _schemeChip('Warm Sunset', 'warm'), ], ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8606, 2.3376), // Louvre area zoom: 16.0, tilt: 55.0, bearing: -20.0, ), onStyleLoaded: () { _applyColorScheme(); }, ), ), ], ), ); } Widget _schemeChip(String label, String scheme) { return ChoiceChip( label: Text(label), selected: colorScheme == scheme, onSelected: (selected) { if (selected) { setState(() => colorScheme = scheme); _applyColorScheme(); } }, ); } void _applyColorScheme() { String color; switch (colorScheme) { case 'blue': color = '#4a90d9'; break; case 'warm': color = '#e8775a'; break; default: // height-based color = '#8a8a8a'; } // Remove existing layer if present mapController?.removeLayer('3d-buildings'); mapController?.addFillExtrusionLayer( '3d-buildings', 'composite', sourceLayer: 'building', fillExtrusionColor: color, fillExtrusionOpacity: 0.7, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], minZoom: 14.0, ); } } ``` ## Interactive 3D Building Explorer Tap on buildings to see their details, rotate around them: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BuildingExplorerScreen extends StatefulWidget { @override _BuildingExplorerScreenState createState() => _BuildingExplorerScreenState(); } class _BuildingExplorerScreenState extends State { MapMetricsController? mapController; Timer? rotationTimer; double currentBearing = 0.0; bool isRotating = false; final List> landmarks = [ { 'name': 'Eiffel Tower Area', 'lat': 48.8584, 'lng': 2.2945, 'zoom': 17.0, 'tilt': 60.0, }, { 'name': 'Louvre Area', 'lat': 48.8606, 'lng': 2.3376, 'zoom': 16.5, 'tilt': 55.0, }, { 'name': 'Notre-Dame Area', 'lat': 48.8530, 'lng': 2.3499, 'zoom': 17.0, 'tilt': 65.0, }, { 'name': 'Opera Area', 'lat': 48.8720, 'lng': 2.3316, 'zoom': 16.5, 'tilt': 55.0, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('3D Explorer')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 17.0, tilt: 60.0, bearing: 0.0, ), onStyleLoaded: () { mapController?.addFillExtrusionLayer( '3d-buildings', 'composite', sourceLayer: 'building', fillExtrusionColor: '#667799', fillExtrusionOpacity: 0.7, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], minZoom: 14.0, ); }, ), // Landmark buttons Positioned( bottom: 24, left: 8, right: 8, child: SizedBox( height: 44, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: landmarks.length, separatorBuilder: (_, __) => SizedBox(width: 8), itemBuilder: (context, i) { final lm = landmarks[i]; return ElevatedButton( onPressed: () { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(lm['lat'], lm['lng']), zoom: lm['zoom'], tilt: lm['tilt'], bearing: currentBearing, ), ), ); }, child: Text(lm['name'], style: TextStyle(fontSize: 12)), ); }, ), ), ), // Rotate toggle Positioned( top: 16, right: 16, child: FloatingActionButton.small( onPressed: _toggleRotation, child: Icon(isRotating ? Icons.pause : Icons.rotate_right), tooltip: 'Auto Rotate', ), ), ], ), ); } void _toggleRotation() { if (isRotating) { rotationTimer?.cancel(); setState(() => isRotating = false); } else { setState(() => isRotating = true); rotationTimer = Timer.periodic(Duration(milliseconds: 50), (_) async { currentBearing = (currentBearing + 0.5) % 360; final pos = await mapController?.getCameraPosition(); if (pos != null) { mapController?.moveCamera( CameraUpdate.newCameraPosition( CameraPosition( target: pos.target, zoom: pos.zoom, tilt: pos.tilt, bearing: currentBearing, ), ), ); } }); } } @override void dispose() { rotationTimer?.cancel(); super.dispose(); } } ``` ## 3D Building Properties | Property | Description | |----------|-------------| | `fillExtrusionColor` | Building wall/roof color | | `fillExtrusionOpacity` | Transparency (0.0 - 1.0) | | `fillExtrusionHeight` | Building height in meters | | `fillExtrusionBase` | Base height (for elevated structures) | | `minZoom` | Minimum zoom level to show buildings | ## Next Steps - [3D Terrain](./flutter-3d-terrain) — Enable terrain elevation - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Control 3D camera angles - [Animate Camera Around Point](./flutter-animate-camera-around-point) — Orbit around buildings --- **Tip**: 3D buildings look best at zoom levels 15-18 with a tilt of 45-65 degrees. Combine with a bearing rotation for dramatic fly-through effects. --- # 3D Terrain in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-3d-terrain # 3D Terrain in Flutter This tutorial shows how to enable 3D terrain elevation on your MapMetrics Flutter map — hills, mountains, and valleys rendered in 3D for immersive map experiences. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Enable 3D Terrain Activate terrain elevation with a tilted camera to see mountains in 3D: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class Terrain3DScreen extends StatefulWidget { @override _Terrain3DScreenState createState() => _Terrain3DScreenState(); } class _Terrain3DScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('3D Terrain')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), // Swiss Alps zoom: 10.0, tilt: 60.0, bearing: 30.0, ), onStyleLoaded: () { _enableTerrain(); }, ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( heroTag: '3d', onPressed: () => _setView(60.0), child: Icon(Icons.terrain), tooltip: '3D View', ), SizedBox(height: 8), FloatingActionButton( heroTag: '2d', onPressed: () => _setView(0.0), child: Icon(Icons.map), tooltip: '2D View', ), ], ), ); } void _enableTerrain() { // Add terrain source mapController?.addRasterDemSource( 'terrain-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); // Enable terrain with exaggeration mapController?.setTerrain('terrain-source', exaggeration: 1.5); } void _setView(double tilt) async { final pos = await mapController?.getCameraPosition(); if (pos != null) { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: pos.target, zoom: pos.zoom, tilt: tilt, bearing: pos.bearing, ), ), ); } } } ``` ## Mountain Explorer with Location Buttons Jump between famous mountain locations: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MountainExplorerScreen extends StatefulWidget { @override _MountainExplorerScreenState createState() => _MountainExplorerScreenState(); } class _MountainExplorerScreenState extends State { MapMetricsController? mapController; final List> mountains = [ { 'name': 'Swiss Alps', 'lat': 46.8182, 'lng': 8.2275, 'zoom': 10.0, 'bearing': 30.0, }, { 'name': 'Mont Blanc', 'lat': 45.8326, 'lng': 6.8652, 'zoom': 12.0, 'bearing': 150.0, }, { 'name': 'Matterhorn', 'lat': 45.9763, 'lng': 7.6586, 'zoom': 13.0, 'bearing': 220.0, }, { 'name': 'Dolomites', 'lat': 46.4102, 'lng': 11.8440, 'zoom': 11.0, 'bearing': 90.0, }, { 'name': 'Pyrenees', 'lat': 42.6953, 'lng': 0.0414, 'zoom': 10.0, 'bearing': 45.0, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Mountain Explorer')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), zoom: 10.0, tilt: 60.0, bearing: 30.0, ), onStyleLoaded: () { mapController?.addRasterDemSource( 'terrain-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.setTerrain('terrain-source', exaggeration: 1.5); }, ), // Mountain selector Positioned( bottom: 16, left: 8, right: 8, child: SizedBox( height: 44, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: mountains.length, separatorBuilder: (_, __) => SizedBox(width: 8), itemBuilder: (context, i) { final m = mountains[i]; return ElevatedButton.icon( onPressed: () { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(m['lat'], m['lng']), zoom: m['zoom'], tilt: 60.0, bearing: m['bearing'], ), ), ); }, icon: Icon(Icons.terrain, size: 16), label: Text(m['name'], style: TextStyle(fontSize: 12)), ); }, ), ), ), ], ), ); } } ``` ## Terrain with Exaggeration Slider Let users control how dramatically terrain is displayed: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TerrainSliderScreen extends StatefulWidget { @override _TerrainSliderScreenState createState() => _TerrainSliderScreenState(); } class _TerrainSliderScreenState extends State { MapMetricsController? mapController; double exaggeration = 1.5; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Terrain Exaggeration')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), zoom: 10.0, tilt: 60.0, ), onStyleLoaded: () { mapController?.addRasterDemSource( 'terrain-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.setTerrain( 'terrain-source', exaggeration: exaggeration); }, ), // Exaggeration slider Positioned( bottom: 24, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Exaggeration: ${exaggeration.toStringAsFixed(1)}x', style: TextStyle(fontWeight: FontWeight.bold), ), Slider( value: exaggeration, min: 0.0, max: 3.0, divisions: 30, label: '${exaggeration.toStringAsFixed(1)}x', onChanged: (value) { setState(() { exaggeration = value; }); mapController?.setTerrain( 'terrain-source', exaggeration: value, ); }, ), Text( '0x = flat | 1x = real | 3x = dramatic', style: TextStyle(color: Colors.grey, fontSize: 11), ), ], ), ), ), ), ], ), ); } } ``` ## Terrain Parameters | Parameter | Range | Description | |-----------|-------|-------------| | `exaggeration` | 0.0 - 3.0 | Multiplier for terrain height. 1.0 = real scale | | `tileSize` | 256 / 512 | Resolution of terrain tiles | | `tilt` | 0 - 85 | Camera tilt angle for 3D effect | ## Next Steps - [3D Buildings](./flutter-3d-buildings) — Add extruded buildings - [Satellite Terrain](./flutter-satellite-terrain) — Satellite imagery with elevation - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Camera angle controls --- **Tip**: Set exaggeration to 1.5 for a good balance between realism and visibility. Values above 2.0 create a dramatic effect but can distort distances at close zoom levels. --- # Add Clusters in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-a-cluster # Add Clusters in Flutter This tutorial shows how to group nearby markers into clusters for better performance and readability when you have many data points on the map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Clustering When you have many markers close together, clustering groups them into a single icon showing the count. As the user zooms in, clusters break apart into individual markers: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:math'; class ClusterExampleScreen extends StatefulWidget { @override _ClusterExampleScreenState createState() => _ClusterExampleScreenState(); } class _ClusterExampleScreenState extends State { MapMetricsController? mapController; Set markers = {}; double currentZoom = 12.0; // Sample data: 100 random points around Paris late final List allPoints; @override void initState() { super.initState(); final random = Random(42); allPoints = List.generate(100, (_) { return LatLng( 48.82 + random.nextDouble() * 0.08, // Lat range around Paris 2.28 + random.nextDouble() * 0.14, // Lng range around Paris ); }); _updateMarkers(); } void _updateMarkers() { // Simple clustering: group nearby points based on zoom level final double gridSize = 0.01 * pow(2, 15 - currentZoom).toDouble(); final Map> grid = {}; for (final point in allPoints) { final key = '${(point.latitude / gridSize).floor()}_${(point.longitude / gridSize).floor()}'; grid.putIfAbsent(key, () => []).add(point); } final Set newMarkers = {}; for (final entry in grid.entries) { final points = entry.value; // Calculate center of the group final avgLat = points.map((p) => p.latitude).reduce((a, b) => a + b) / points.length; final avgLng = points.map((p) => p.longitude).reduce((a, b) => a + b) / points.length; final center = LatLng(avgLat, avgLng); if (points.length == 1) { // Single point — show as regular marker newMarkers.add( Marker( markerId: MarkerId(entry.key), position: center, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), infoWindow: InfoWindow( title: 'Point', snippet: '${center.latitude.toStringAsFixed(4)}, ${center.longitude.toStringAsFixed(4)}', ), ), ); } else { // Cluster — show count newMarkers.add( Marker( markerId: MarkerId('cluster_${entry.key}'), position: center, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), infoWindow: InfoWindow( title: '${points.length} points', snippet: 'Zoom in to see details', ), ), ); } } setState(() { markers = newMarkers; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Clustered Markers')), body: Column( children: [ Container( padding: EdgeInsets.all(10), color: Colors.grey[100], child: Text( '${allPoints.length} total points | ${markers.length} visible markers | Zoom: ${currentZoom.toStringAsFixed(1)}', style: TextStyle(fontSize: 13), ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onCameraIdle: () async { final position = await mapController?.getCameraPosition(); if (position != null && position.zoom != currentZoom) { currentZoom = position.zoom; _updateMarkers(); } }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), markers: markers, ), ), ], ), ); } } ``` ## Cluster with Custom Widget Icons Create visually distinct cluster icons that show the count and change color based on size: ```dart Future _createClusterIcon(int count) async { Color color; double size; if (count < 10) { color = Colors.blue; size = 40; } else if (count < 50) { color = Colors.orange; size = 50; } else { color = Colors.red; size = 60; } return await BitmapDescriptor.fromWidget( Container( width: size, height: size, decoration: BoxDecoration( color: color.withOpacity(0.8), shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 2), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), alignment: Alignment.center, child: Text( '$count', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: size * 0.35, ), ), ), ); } ``` ## Tap Cluster to Zoom In Zoom into a cluster when tapped: ```dart Marker( markerId: MarkerId('cluster_${entry.key}'), position: center, icon: clusterIcon, onTap: () { // Zoom in to break apart the cluster mapController?.animateCamera( CameraUpdate.newLatLngZoom(center, currentZoom + 2), ); }, ) ``` ## Cluster Size Colors | Count | Color | Size | |-------|-------|------| | 1–9 | Blue | Small (40px) | | 10–49 | Orange | Medium (50px) | | 50+ | Red | Large (60px) | ## Next Steps - [Add a Heatmap](./flutter-add-a-heatmap) — Density visualization alternative to clusters - [Markers and Annotations](./flutter-markers) — Basic marker features - [Fit to Bounding Box](./flutter-fit-to-bounding-box) — Zoom to show all clusters --- **Tip**: Recalculate clusters on `onCameraIdle` (not `onCameraMove`) to avoid excessive recomputation during panning. --- # Add a Heatmap in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-a-heatmap # Add a Heatmap in Flutter This tutorial shows how to create a heatmap overlay to visualize data density on your MapMetrics Flutter map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Heatmap with Weighted Circles Flutter doesn't have a built-in heatmap layer, but you can simulate one effectively using semi-transparent, overlapping circles: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:math'; class HeatmapExampleScreen extends StatefulWidget { @override _HeatmapExampleScreenState createState() => _HeatmapExampleScreenState(); } class _HeatmapExampleScreenState extends State { MapMetricsController? mapController; // Sample data: locations with intensity (weight) final List> heatData = [ {'position': LatLng(48.8584, 2.2945), 'weight': 0.9}, // Eiffel Tower {'position': LatLng(48.8606, 2.3376), 'weight': 0.85}, // Louvre {'position': LatLng(48.8530, 2.3499), 'weight': 0.8}, // Notre-Dame {'position': LatLng(48.8867, 2.3431), 'weight': 0.7}, // Sacré-Cœur {'position': LatLng(48.8738, 2.2950), 'weight': 0.75}, // Arc de Triomphe {'position': LatLng(48.8620, 2.3520), 'weight': 0.5}, {'position': LatLng(48.8450, 2.3400), 'weight': 0.4}, {'position': LatLng(48.8700, 2.3100), 'weight': 0.6}, {'position': LatLng(48.8550, 2.3700), 'weight': 0.3}, {'position': LatLng(48.8480, 2.3200), 'weight': 0.35}, {'position': LatLng(48.8650, 2.3300), 'weight': 0.55}, {'position': LatLng(48.8580, 2.3050), 'weight': 0.45}, {'position': LatLng(48.8750, 2.3500), 'weight': 0.5}, {'position': LatLng(48.8500, 2.2800), 'weight': 0.25}, {'position': LatLng(48.8800, 2.3200), 'weight': 0.4}, ]; Color _getHeatColor(double weight) { // Gradient: blue (cold) → green → yellow → red (hot) if (weight > 0.75) return Colors.red.withOpacity(0.4); if (weight > 0.5) return Colors.orange.withOpacity(0.35); if (weight > 0.25) return Colors.yellow.withOpacity(0.3); return Colors.green.withOpacity(0.25); } Set get heatCircles => heatData.asMap().entries.map((entry) { final data = entry.value; final double weight = data['weight']; return Circle( circleId: CircleId('heat_${entry.key}'), center: data['position'], radius: 300 + (weight * 500), // 300–800m radius based on weight strokeWidth: 0, fillColor: _getHeatColor(weight), ); }).toSet(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Heatmap')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8600, 2.3300), zoom: 13.0, ), circles: heatCircles, ), // Legend Positioned( bottom: 24, right: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Intensity', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13)), SizedBox(height: 8), _legendRow(Colors.red, 'High'), _legendRow(Colors.orange, 'Medium-High'), _legendRow(Colors.yellow, 'Medium-Low'), _legendRow(Colors.green, 'Low'), ], ), ), ), ], ), ); } Widget _legendRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 2), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 14, height: 14, decoration: BoxDecoration( color: color.withOpacity(0.5), shape: BoxShape.circle, ), ), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 12)), ], ), ); } } ``` ## Generate Heatmap from Random Data Create a realistic heatmap from a large dataset: ```dart List> _generateHeatData(LatLng center, int count) { final random = Random(42); return List.generate(count, (_) { // Cluster points near the center with normal-like distribution final lat = center.latitude + (random.nextDouble() - 0.5) * 0.05; final lng = center.longitude + (random.nextDouble() - 0.5) * 0.08; // Weight higher for points closer to center final dist = sqrt(pow(lat - center.latitude, 2) + pow(lng - center.longitude, 2)); final weight = max(0.1, 1.0 - dist * 30); return { 'position': LatLng(lat, lng), 'weight': weight, }; }); } ``` ## Toggle Heatmap and Points Switch between heatmap view and individual point markers: ```dart bool showHeatmap = true; // In your build method: MapMetrics( // ... config circles: showHeatmap ? heatCircles : {}, markers: showHeatmap ? {} : pointMarkers, ) // Toggle button: FloatingActionButton( onPressed: () => setState(() => showHeatmap = !showHeatmap), child: Icon(showHeatmap ? Icons.location_on : Icons.blur_on), tooltip: showHeatmap ? 'Show Points' : 'Show Heatmap', ) ``` ## Heatmap Color Scales | Scale | Colors | Best For | |-------|--------|----------| | Traffic | Green → Yellow → Red | Congestion, density | | Temperature | Blue → Green → Yellow → Red | Weather, temperature data | | Monochrome | Light blue → Dark blue | Clean, minimal design | | Viridis | Purple → Blue → Green → Yellow | Scientific data | ## Next Steps - [Add Clusters](./flutter-add-a-cluster) — Group markers instead of overlapping - [Multiple Geometries](./flutter-multiple-geometries) — Combine heatmap with other layers - [Draw a Circle](./flutter-draw-a-circle) — More about circle styling --- **Tip**: For the best visual result, use `strokeWidth: 0` on heatmap circles so there are no visible borders, and use generous opacity overlap between neighboring circles. --- # Add a Polygon in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-a-polygon # Add a Polygon in Flutter This tutorial shows how to draw filled polygons on your MapMetrics Flutter map. Polygons are useful for highlighting areas, zones, or boundaries. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Polygon Draw a simple polygon by providing a list of `LatLng` points. The shape will automatically close by connecting the last point to the first: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PolygonExampleScreen extends StatefulWidget { @override _PolygonExampleScreenState createState() => _PolygonExampleScreenState(); } class _PolygonExampleScreenState extends State { MapMetricsController? mapController; final Set polygons = { Polygon( polygonId: PolygonId('manhattan'), points: [ LatLng(40.800, -73.958), LatLng(40.800, -74.020), LatLng(40.700, -74.020), LatLng(40.700, -73.970), ], strokeWidth: 2, strokeColor: Colors.blue, fillColor: Colors.blue.withOpacity(0.2), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Polygon Example')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(40.750, -73.990), zoom: 12.0, ), polygons: polygons, ), ); } } ``` ## Multiple Colored Zones Show different areas with distinct colors: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ColoredZonesScreen extends StatefulWidget { @override _ColoredZonesScreenState createState() => _ColoredZonesScreenState(); } class _ColoredZonesScreenState extends State { MapMetricsController? mapController; final Set zones = { // Zone A - Green (safe zone) Polygon( polygonId: PolygonId('zone_a'), points: [ LatLng(48.870, 2.330), LatLng(48.870, 2.350), LatLng(48.860, 2.350), LatLng(48.860, 2.330), ], strokeWidth: 2, strokeColor: Colors.green, fillColor: Colors.green.withOpacity(0.25), ), // Zone B - Orange (caution zone) Polygon( polygonId: PolygonId('zone_b'), points: [ LatLng(48.860, 2.330), LatLng(48.860, 2.350), LatLng(48.850, 2.350), LatLng(48.850, 2.330), ], strokeWidth: 2, strokeColor: Colors.orange, fillColor: Colors.orange.withOpacity(0.25), ), // Zone C - Red (restricted zone) Polygon( polygonId: PolygonId('zone_c'), points: [ LatLng(48.850, 2.330), LatLng(48.850, 2.350), LatLng(48.840, 2.350), LatLng(48.840, 2.330), ], strokeWidth: 2, strokeColor: Colors.red, fillColor: Colors.red.withOpacity(0.25), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Colored Zones')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.855, 2.340), zoom: 14.0, ), polygons: zones, ), // Legend Positioned( top: 16, right: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _legendItem(Colors.green, 'Safe Zone'), SizedBox(height: 6), _legendItem(Colors.orange, 'Caution Zone'), SizedBox(height: 6), _legendItem(Colors.red, 'Restricted Zone'), ], ), ), ), ], ), ); } Widget _legendItem(Color color, String label) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 16, height: 16, decoration: BoxDecoration( color: color.withOpacity(0.3), border: Border.all(color: color, width: 2), borderRadius: BorderRadius.circular(3), ), ), SizedBox(width: 8), Text(label, style: TextStyle(fontSize: 13)), ], ); } } ``` ## Tappable Polygons Make polygons respond to taps: ```dart Polygon( polygonId: PolygonId('tappable_area'), points: [ LatLng(48.870, 2.330), LatLng(48.870, 2.360), LatLng(48.850, 2.360), LatLng(48.850, 2.330), ], strokeWidth: 2, strokeColor: Colors.purple, fillColor: Colors.purple.withOpacity(0.2), consumeTapEvents: true, onTap: () { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Area Selected'), content: Text('You tapped on the highlighted area.'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ), ); }, ) ``` ## Polygon Properties | Property | Type | Description | |----------|------|-------------| | `polygonId` | `PolygonId` | Unique identifier for the polygon | | `points` | `List` | Vertices of the polygon (auto-closed) | | `strokeWidth` | `int` | Border width in pixels | | `strokeColor` | `Color` | Border color | | `fillColor` | `Color` | Fill color (use `withOpacity` for transparency) | | `visible` | `bool` | Whether the polygon is visible | | `zIndex` | `int` | Drawing order relative to other overlays | | `consumeTapEvents` | `bool` | If `true`, tap events are consumed by the polygon | | `onTap` | `VoidCallback` | Called when the polygon is tapped | ## Next Steps - [Draw a Circle](./flutter-draw-a-circle) — Draw circular areas on the map - [Add a Polyline](./flutter-add-a-polyline) — Draw lines and routes - [Markers and Annotations](./flutter-markers) — Add markers to your polygons --- **Tip**: Use `withOpacity` on your fill colors to keep the polygon semi-transparent so the map underneath remains visible. --- # Add a Polyline in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-a-polyline # Add a Polyline in Flutter This tutorial shows how to draw polylines (lines connecting multiple points) on your MapMetrics Flutter map. Polylines are useful for showing routes, paths, or borders. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Polyline Draw a simple polyline by providing a list of `LatLng` points: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PolylineExampleScreen extends StatefulWidget { @override _PolylineExampleScreenState createState() => _PolylineExampleScreenState(); } class _PolylineExampleScreenState extends State { MapMetricsController? mapController; final Set polylines = { Polyline( polylineId: PolylineId('paris_route'), points: [ LatLng(48.8589, 2.3398), LatLng(48.8566, 2.3491), LatLng(48.8547, 2.3539), LatLng(48.8519, 2.3610), LatLng(48.8483, 2.3589), LatLng(48.8516, 2.3505), LatLng(48.8540, 2.3434), LatLng(48.8573, 2.3386), LatLng(48.8579, 2.3344), LatLng(48.8590, 2.3399), ], color: Colors.red, width: 4, ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Polyline Example')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8540, 2.3470), zoom: 14.0, ), polylines: polylines, ), ); } } ``` ## Styled Polylines Customize the look of your polylines with color, width, and patterns: ```dart final Set styledPolylines = { // Solid thick line Polyline( polylineId: PolylineId('thick_line'), points: [ LatLng(48.860, 2.330), LatLng(48.855, 2.345), LatLng(48.850, 2.360), ], color: Colors.blue, width: 6, ), // Dotted line Polyline( polylineId: PolylineId('dotted_line'), points: [ LatLng(48.858, 2.330), LatLng(48.853, 2.345), LatLng(48.848, 2.360), ], color: Colors.orange, width: 3, patterns: [PatternItem.dot], ), // Dashed line Polyline( polylineId: PolylineId('dashed_line'), points: [ LatLng(48.856, 2.330), LatLng(48.851, 2.345), LatLng(48.846, 2.360), ], color: Colors.green, width: 3, patterns: [PatternItem.dash(20), PatternItem.gap(10)], ), }; ``` ## Multiple Routes Show several routes with different colors: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultipleRoutesScreen extends StatefulWidget { @override _MultipleRoutesScreenState createState() => _MultipleRoutesScreenState(); } class _MultipleRoutesScreenState extends State { MapMetricsController? mapController; final Set routes = { // Route 1: New York → Philadelphia → Washington DC Polyline( polylineId: PolylineId('east_coast'), points: [ LatLng(40.7128, -74.0060), // New York LatLng(39.9526, -75.1652), // Philadelphia LatLng(38.9072, -77.0369), // Washington DC ], color: Colors.blue, width: 4, ), // Route 2: Los Angeles → Las Vegas → Phoenix Polyline( polylineId: PolylineId('southwest'), points: [ LatLng(34.0522, -118.2437), // Los Angeles LatLng(36.1699, -115.1398), // Las Vegas LatLng(33.4484, -112.0740), // Phoenix ], color: Colors.red, width: 4, ), }; // Markers at each city final Set cityMarkers = { Marker( markerId: MarkerId('nyc'), position: LatLng(40.7128, -74.0060), infoWindow: InfoWindow(title: 'New York'), ), Marker( markerId: MarkerId('philly'), position: LatLng(39.9526, -75.1652), infoWindow: InfoWindow(title: 'Philadelphia'), ), Marker( markerId: MarkerId('dc'), position: LatLng(38.9072, -77.0369), infoWindow: InfoWindow(title: 'Washington DC'), ), Marker( markerId: MarkerId('la'), position: LatLng(34.0522, -118.2437), infoWindow: InfoWindow(title: 'Los Angeles'), ), Marker( markerId: MarkerId('vegas'), position: LatLng(36.1699, -115.1398), infoWindow: InfoWindow(title: 'Las Vegas'), ), Marker( markerId: MarkerId('phoenix'), position: LatLng(33.4484, -112.0740), infoWindow: InfoWindow(title: 'Phoenix'), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Multiple Routes')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(37.0902, -95.7129), // Center of USA zoom: 4.0, ), polylines: routes, markers: cityMarkers, ), ); } } ``` ## Polyline Properties | Property | Type | Description | |----------|------|-------------| | `polylineId` | `PolylineId` | Unique identifier for the polyline | | `points` | `List` | List of coordinates that form the line | | `color` | `Color` | Line color (default: black) | | `width` | `int` | Line width in pixels (default: 10) | | `patterns` | `List` | Dash/dot/gap pattern for the line | | `geodesic` | `bool` | If `true`, line follows Earth's curvature | | `visible` | `bool` | Whether the polyline is visible | | `zIndex` | `int` | Drawing order relative to other overlays | ## Next Steps - [Add a Polygon](./flutter-add-a-polygon) — Draw filled shapes on the map - [Draw a Circle](./flutter-draw-a-circle) — Draw circular areas - [Add a Popup](./flutter-add-a-popup) — Show popups on markers --- **Tip**: Combine polylines with markers at key points to create an interactive route map. Use `geodesic: true` for long-distance routes so the line follows the curvature of the Earth. --- # Add a Popup in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-a-popup # Add a Popup in Flutter This tutorial shows how to display popups (info windows) on markers in your MapMetrics Flutter map. Popups are great for showing extra information when a user taps on a marker. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Popup with InfoWindow The simplest way to show a popup is by using the `InfoWindow` property on a `Marker`: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PopupExampleScreen extends StatefulWidget { @override _PopupExampleScreenState createState() => _PopupExampleScreenState(); } class _PopupExampleScreenState extends State { MapMetricsController? mapController; final Set markers = { Marker( markerId: MarkerId('paris'), position: LatLng(48.852966, 2.349902), infoWindow: InfoWindow( title: 'Hello MapMetrics', snippet: 'A good coffee shop', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Popup Example')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.852966, 2.349902), zoom: 13.0, ), markers: markers, ), ); } } ``` Tap on the marker to see the popup with the title and snippet text. ## Multiple Markers with Popups Add several markers, each with their own popup content: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultiplePopupsScreen extends StatefulWidget { @override _MultiplePopupsScreenState createState() => _MultiplePopupsScreenState(); } class _MultiplePopupsScreenState extends State { MapMetricsController? mapController; final Set markers = { Marker( markerId: MarkerId('eiffel_tower'), position: LatLng(48.8584, 2.2945), infoWindow: InfoWindow( title: 'Eiffel Tower', snippet: 'Iconic iron lattice tower on the Champ de Mars', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), ), Marker( markerId: MarkerId('louvre'), position: LatLng(48.8606, 2.3376), infoWindow: InfoWindow( title: 'Louvre Museum', snippet: 'World\'s largest art museum and historic monument', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), ), Marker( markerId: MarkerId('notre_dame'), position: LatLng(48.8530, 2.3499), infoWindow: InfoWindow( title: 'Notre-Dame Cathedral', snippet: 'Medieval Catholic cathedral on the Île de la Cité', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Paris Landmarks')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3222), zoom: 13.0, ), markers: markers, ), ); } } ``` ## Custom Popup with Bottom Sheet For richer popup content beyond a simple title and snippet, you can show a bottom sheet when a marker is tapped: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CustomPopupScreen extends StatefulWidget { @override _CustomPopupScreenState createState() => _CustomPopupScreenState(); } class _CustomPopupScreenState extends State { MapMetricsController? mapController; final Map> markerData = { 'cafe': { 'title': 'Le Petit Café', 'description': 'A cozy Parisian café with excellent croissants and espresso.', 'hours': 'Mon-Sat: 7:00 AM - 9:00 PM', 'rating': '4.5', }, 'bookstore': { 'title': 'Shakespeare & Company', 'description': 'Historic English-language bookstore on the Left Bank.', 'hours': 'Daily: 10:00 AM - 10:00 PM', 'rating': '4.8', }, }; Set get markers => { Marker( markerId: MarkerId('cafe'), position: LatLng(48.8530, 2.3470), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), ), Marker( markerId: MarkerId('bookstore'), position: LatLng(48.8526, 2.3471), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Custom Popups')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, onMarkerTapped: (MarkerId markerId) { _showCustomPopup(markerId.value); }, initialCameraPosition: CameraPosition( target: LatLng(48.8530, 2.3470), zoom: 17.0, ), markers: markers, ), ); } void _showCustomPopup(String markerId) { final data = markerData[markerId]; if (data == null) return; showModalBottomSheet( context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return Padding( padding: EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( data['title']!, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text( data['description']!, style: TextStyle(fontSize: 14, color: Colors.grey[700]), ), SizedBox(height: 12), Row( children: [ Icon(Icons.access_time, size: 16, color: Colors.grey), SizedBox(width: 4), Text(data['hours']!, style: TextStyle(fontSize: 13)), ], ), SizedBox(height: 8), Row( children: [ Icon(Icons.star, size: 16, color: Colors.amber), SizedBox(width: 4), Text(data['rating']!, style: TextStyle(fontSize: 13)), ], ), SizedBox(height: 16), ], ), ); }, ); } } ``` ## Add Popup on Map Tap You can also show a popup when the user taps anywhere on the map: ```dart MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onMapClick: (Point point, LatLng coordinates) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Location'), content: Text( 'Lat: ${coordinates.latitude.toStringAsFixed(6)}\n' 'Lng: ${coordinates.longitude.toStringAsFixed(6)}', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ), ); }, initialCameraPosition: CameraPosition( target: LatLng(48.852966, 2.349902), zoom: 13.0, ), ) ``` ## Next Steps - [Add a Polyline](./flutter-add-a-polyline) — Draw lines and routes on the map - [Fly to a Location](./flutter-fly-to-location) — Animate the camera to different places - [Draggable Marker](./flutter-draggable-marker) — Let users drag markers around the map --- **Tip**: For simple information, use `InfoWindow`. For richer content with images, buttons, or custom layouts, use a bottom sheet or dialog triggered by `onMarkerTapped`. --- # Add an Animated Icon to the Map in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-animated-icon # Add an Animated Icon to the Map in Flutter This tutorial shows how to create animated marker icons — rotating, scaling, or changing appearance over time — for live tracking, alerts, or eye-catching points of interest. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Rotating Icon Marker Create a marker icon that rotates continuously (e.g., a compass or loading indicator): ```dart import 'dart:math'; import 'dart:ui' as ui; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RotatingIconScreen extends StatefulWidget { @override _RotatingIconScreenState createState() => _RotatingIconScreenState(); } class _RotatingIconScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animController; BitmapDescriptor? currentIcon; final LatLng position = LatLng(48.8584, 2.2945); @override void initState() { super.initState(); _animController = AnimationController( duration: Duration(seconds: 3), vsync: this, )..repeat(); _animController.addListener(() { _generateRotatedIcon(_animController.value * 2 * pi); }); } Future _generateRotatedIcon(double angle) async { final size = 64.0; final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); // Background circle canvas.drawCircle( Offset(size / 2, size / 2), size / 2, Paint()..color = Colors.blue, ); // Rotating arrow canvas.save(); canvas.translate(size / 2, size / 2); canvas.rotate(angle); final arrowPaint = Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 3 ..strokeCap = StrokeCap.round; // Arrow pointing up canvas.drawLine(Offset(0, 12), Offset(0, -12), arrowPaint); canvas.drawLine(Offset(0, -12), Offset(-6, -4), arrowPaint); canvas.drawLine(Offset(0, -12), Offset(6, -4), arrowPaint); canvas.restore(); final picture = recorder.endRecording(); final image = await picture.toImage(size.toInt(), size.toInt()); final bytes = await image.toByteData(format: ui.ImageByteFormat.png); if (mounted) { setState(() { currentIcon = BitmapDescriptor.fromBytes(bytes!.buffer.asUint8List()); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Rotating Icon')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: position, zoom: 15.0, ), markers: currentIcon != null ? { Marker( markerId: MarkerId('rotating'), position: position, icon: currentIcon!, anchor: Offset(0.5, 0.5), ), } : {}, ), ); } @override void dispose() { _animController.dispose(); super.dispose(); } } ``` ## Color-Cycling Alert Icon A marker that cycles through colors to draw attention: ```dart import 'dart:ui' as ui; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AlertIconScreen extends StatefulWidget { @override _AlertIconScreenState createState() => _AlertIconScreenState(); } class _AlertIconScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animController; late Animation _colorAnimation; BitmapDescriptor? currentIcon; final List alertLocations = [ LatLng(48.860, 2.340), LatLng(48.855, 2.350), LatLng(48.865, 2.325), ]; @override void initState() { super.initState(); _animController = AnimationController( duration: Duration(seconds: 2), vsync: this, )..repeat(reverse: true); _colorAnimation = ColorTween( begin: Colors.red, end: Colors.yellow, ).animate(_animController); _animController.addListener(() { _generateColoredIcon(_colorAnimation.value!); }); } Future _generateColoredIcon(Color color) async { final size = 48.0; final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); // Outer glow canvas.drawCircle( Offset(size / 2, size / 2), size / 2, Paint()..color = color.withOpacity(0.3), ); // Inner circle canvas.drawCircle( Offset(size / 2, size / 2), size / 3, Paint()..color = color, ); // White border canvas.drawCircle( Offset(size / 2, size / 2), size / 3, Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 2, ); // Exclamation mark final textPainter = TextPainter( text: TextSpan( text: '!', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, ); textPainter.layout(); textPainter.paint( canvas, Offset((size - textPainter.width) / 2, (size - textPainter.height) / 2), ); final picture = recorder.endRecording(); final image = await picture.toImage(size.toInt(), size.toInt()); final bytes = await image.toByteData(format: ui.ImageByteFormat.png); if (mounted) { setState(() { currentIcon = BitmapDescriptor.fromBytes(bytes!.buffer.asUint8List()); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Alert Icons')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.858, 2.340), zoom: 14.0, ), markers: currentIcon != null ? alertLocations.asMap().entries.map((e) { return Marker( markerId: MarkerId('alert_${e.key}'), position: e.value, icon: currentIcon!, anchor: Offset(0.5, 0.5), infoWindow: InfoWindow(title: 'Alert ${e.key + 1}'), ); }).toSet() : {}, ), ); } @override void dispose() { _animController.dispose(); super.dispose(); } } ``` ## Frame-by-Frame Sprite Animation Cycle through pre-made icon frames for sprite-sheet style animation: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SpriteAnimationScreen extends StatefulWidget { @override _SpriteAnimationScreenState createState() => _SpriteAnimationScreenState(); } class _SpriteAnimationScreenState extends State { MapMetricsController? mapController; Timer? frameTimer; int currentFrame = 0; List frames = []; @override void initState() { super.initState(); _loadFrames(); } Future _loadFrames() async { // Load animation frames from assets // e.g., assets/anim/frame_0.png, frame_1.png, frame_2.png... final loadedFrames = []; for (int i = 0; i < 8; i++) { final icon = await BitmapDescriptor.fromAssetImage( ImageConfiguration(size: Size(48, 48)), 'assets/anim/frame_$i.png', ); loadedFrames.add(icon); } setState(() { frames = loadedFrames; }); // Start frame cycling frameTimer = Timer.periodic(Duration(milliseconds: 150), (_) { setState(() { currentFrame = (currentFrame + 1) % frames.length; }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Sprite Animation')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 15.0, ), markers: frames.isNotEmpty ? { Marker( markerId: MarkerId('animated'), position: LatLng(48.8584, 2.2945), icon: frames[currentFrame], ), } : {}, ), ); } @override void dispose() { frameTimer?.cancel(); super.dispose(); } } ``` Declare frames in `pubspec.yaml`: ```yaml flutter: assets: - assets/anim/ ``` ## Animation Approaches | Approach | FPS | Best For | |----------|-----|----------| | Canvas drawing + Timer | 15-30 | Rotating/color-changing icons | | Pre-loaded sprite frames | 10-15 | Complex custom animations | | AnimationController | 60 | Smooth transitions | ## Next Steps - [Animate a Point](./flutter-animate-point) — Moving/bouncing markers - [Add Icon to Map](./flutter-add-icon-to-map) — Static custom icons - [Add Custom Icons with Markers](./flutter-add-custom-icons-markers) — Custom marker styles --- **Tip**: Generating icons on every animation frame is CPU-intensive. For production apps, pre-generate all frames during `initState` and store them in a list, then just swap the `icon` property each frame — this is much more efficient than drawing on every tick. --- # Add a Color Relief Layer in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-color-relief-layer # Add a Color Relief Layer in Flutter This tutorial shows how to add a color relief (hypsometric tinting) layer to your MapMetrics Flutter map — coloring terrain by elevation bands from green lowlands to white mountain peaks. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Color Relief Color the map terrain based on elevation bands using a raster DEM source: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ColorReliefScreen extends StatefulWidget { @override _ColorReliefScreenState createState() => _ColorReliefScreenState(); } class _ColorReliefScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Color Relief Layer')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), // Swiss Alps zoom: 8.0, ), onStyleLoaded: () { _addColorRelief(); }, ), // Elevation legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Elevation', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12)), SizedBox(height: 6), _elevRow(Color(0xFF1a6e1a), '0 - 200m'), _elevRow(Color(0xFF4ca64c), '200 - 500m'), _elevRow(Color(0xFFb3d98c), '500 - 1000m'), _elevRow(Color(0xFFe6d96e), '1000 - 2000m'), _elevRow(Color(0xFFc9854c), '2000 - 3000m'), _elevRow(Color(0xFF8c5a2e), '3000 - 4000m'), _elevRow(Color(0xFFffffff), '4000m+'), ], ), ), ), ), ], ), ); } Widget _elevRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 16, height: 12, decoration: BoxDecoration( color: color, border: Border.all(color: Colors.grey[400]!, width: 0.5), ), ), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 11)), ], ), ); } void _addColorRelief() { // Add terrain DEM source mapController?.addRasterDemSource( 'terrain-dem', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); // Add color relief raster layer with elevation-based color ramp mapController?.addRasterLayer( 'color-relief', 'terrain-dem', rasterColor: [ 'interpolate', ['linear'], ['raster-value'], 0.0, '#1a6e1a', // Deep green (sea level) 0.05, '#4ca64c', // Green (200m) 0.125, '#b3d98c', // Light green (500m) 0.25, '#e6d96e', // Yellow-green (1000m) 0.5, '#c9854c', // Brown (2000m) 0.75, '#8c5a2e', // Dark brown (3000m) 1.0, '#ffffff', // White (4000m+) ], rasterOpacity: 0.6, ); } } ``` ## Color Relief with Hillshade Combine elevation coloring with hillshade for a professional topographic look: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ReliefHillshadeScreen extends StatefulWidget { @override _ReliefHillshadeScreenState createState() => _ReliefHillshadeScreenState(); } class _ReliefHillshadeScreenState extends State { MapMetricsController? mapController; bool showRelief = true; bool showHillshade = true; double reliefOpacity = 0.5; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Relief + Hillshade')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(45.9763, 7.6586), // Matterhorn zoom: 10.0, ), onStyleLoaded: () { _addLayers(); }, ), // Controls Positioned( top: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(10), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Layers', style: TextStyle(fontWeight: FontWeight.bold)), Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: showRelief, onChanged: (val) { setState(() => showRelief = val!); mapController?.setLayerVisibility( 'color-relief', val!); }, ), Text('Color Relief', style: TextStyle(fontSize: 13)), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: showHillshade, onChanged: (val) { setState(() => showHillshade = val!); mapController?.setLayerVisibility( 'hillshade', val!); }, ), Text('Hillshade', style: TextStyle(fontSize: 13)), ], ), SizedBox(height: 4), Text('Opacity', style: TextStyle(fontSize: 12)), SizedBox( width: 150, child: Slider( value: reliefOpacity, min: 0.1, max: 1.0, onChanged: (val) { setState(() => reliefOpacity = val); mapController?.setPaintProperty( 'color-relief', 'raster-opacity', val, ); }, ), ), ], ), ), ), ), // Location presets Positioned( bottom: 16, left: 8, right: 8, child: SizedBox( height: 40, child: ListView( scrollDirection: Axis.horizontal, children: [ _locationChip('Matterhorn', 45.976, 7.659, 10.0), SizedBox(width: 6), _locationChip('Mont Blanc', 45.833, 6.865, 10.0), SizedBox(width: 6), _locationChip('Swiss Alps', 46.818, 8.228, 8.0), SizedBox(width: 6), _locationChip('Dolomites', 46.410, 11.844, 10.0), SizedBox(width: 6), _locationChip('Pyrenees', 42.695, 0.041, 8.0), ], ), ), ), ], ), ); } Widget _locationChip(String name, double lat, double lng, double zoom) { return ActionChip( avatar: Icon(Icons.terrain, size: 14), label: Text(name, style: TextStyle(fontSize: 12)), onPressed: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom(LatLng(lat, lng), zoom), ); }, ); } void _addLayers() { mapController?.addRasterDemSource( 'terrain-dem', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); // Hillshade layer (below relief for shadow effect) mapController?.addHillshadeLayer( 'hillshade', 'terrain-dem', hillshadeExaggeration: 0.5, hillshadeIlluminationDirection: 315.0, ); // Color relief on top with transparency mapController?.addRasterLayer( 'color-relief', 'terrain-dem', rasterColor: [ 'interpolate', ['linear'], ['raster-value'], 0.0, '#1a6e1a', 0.05, '#4ca64c', 0.125, '#b3d98c', 0.25, '#e6d96e', 0.5, '#c9854c', 0.75, '#8c5a2e', 1.0, '#ffffff', ], rasterOpacity: reliefOpacity, ); } } ``` ## Color Scheme Switcher Switch between different elevation color palettes: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ReliefSchemesScreen extends StatefulWidget { @override _ReliefSchemesScreenState createState() => _ReliefSchemesScreenState(); } class _ReliefSchemesScreenState extends State { MapMetricsController? mapController; String activeScheme = 'natural'; final Map> schemes = { 'natural': { 'name': 'Natural', 'colors': [ 0.0, '#1a6e1a', 0.05, '#4ca64c', 0.125, '#b3d98c', 0.25, '#e6d96e', 0.5, '#c9854c', 0.75, '#8c5a2e', 1.0, '#ffffff', ], }, 'ocean': { 'name': 'Ocean Blue', 'colors': [ 0.0, '#0d47a1', 0.1, '#1565c0', 0.25, '#42a5f5', 0.5, '#90caf9', 0.75, '#bbdefb', 1.0, '#e3f2fd', ], }, 'thermal': { 'name': 'Thermal', 'colors': [ 0.0, '#00008B', 0.15, '#0000FF', 0.3, '#00FFFF', 0.5, '#00FF00', 0.7, '#FFFF00', 0.85, '#FF8C00', 1.0, '#FF0000', ], }, 'grayscale': { 'name': 'Grayscale', 'colors': [ 0.0, '#1a1a1a', 0.25, '#4d4d4d', 0.5, '#808080', 0.75, '#b3b3b3', 1.0, '#ffffff', ], }, }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Relief Color Schemes')), body: Column( children: [ Container( padding: EdgeInsets.all(8), child: Wrap( spacing: 8, children: schemes.entries.map((entry) { return ChoiceChip( label: Text(entry.value['name']), selected: activeScheme == entry.key, onSelected: (selected) { if (selected) { setState(() => activeScheme = entry.key); _applyScheme(entry.value['colors']); } }, ); }).toList(), ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.818, 8.228), zoom: 8.0, ), onStyleLoaded: () { _setupRelief(); }, ), ), ], ), ); } void _setupRelief() { mapController?.addRasterDemSource( 'terrain-dem', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); final colors = schemes[activeScheme]!['colors'] as List; mapController?.addRasterLayer( 'color-relief', 'terrain-dem', rasterColor: [ 'interpolate', ['linear'], ['raster-value'], ...colors, ], rasterOpacity: 0.6, ); } void _applyScheme(List colors) { // Remove and re-add with new color ramp mapController?.removeLayer('color-relief'); mapController?.addRasterLayer( 'color-relief', 'terrain-dem', rasterColor: [ 'interpolate', ['linear'], ['raster-value'], ...colors, ], rasterOpacity: 0.6, ); } } ``` ## Color Relief Properties | Property | Type | Description | |----------|------|-------------| | `rasterColor` | `List` | Color interpolation expression mapping elevation to colors | | `rasterOpacity` | `double` | Layer transparency (0.0 - 1.0) | | `rasterValue` | Expression | Normalized elevation value from DEM (0.0 - 1.0) | ## Standard Elevation Color Bands | Elevation | Color | Terrain Type | |-----------|-------|-------------| | 0 - 200m | Dark green | Lowlands, valleys | | 200 - 500m | Green | Hills, foothills | | 500 - 1000m | Light green | Uplands | | 1000 - 2000m | Yellow-green | Mountains | | 2000 - 3000m | Brown | High mountains | | 3000 - 4000m | Dark brown | Alpine zone | | 4000m+ | White | Glaciers, snow | ## Next Steps - [Add a Hillshade Layer](./flutter-add-hillshade-layer) — Shaded relief effect - [Add Contour Lines](./flutter-add-contour-lines) — Elevation contour lines - [3D Terrain](./flutter-3d-terrain) — Full 3D elevation view --- **Tip**: Combine color relief + hillshade + contour lines for a complete topographic map. Set the relief layer opacity to 0.4-0.6 so the base map labels remain readable underneath. --- # Add Contour Lines in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-contour-lines # Add Contour Lines in Flutter This tutorial shows how to add elevation contour lines to your MapMetrics Flutter map — essential for topographic maps, hiking apps, and geographic analysis. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Contour Lines Add contour lines from a vector tile source: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ContourLinesScreen extends StatefulWidget { @override _ContourLinesScreenState createState() => _ContourLinesScreenState(); } class _ContourLinesScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Contour Lines')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), // Swiss Alps zoom: 11.0, ), onStyleLoaded: () { _addContourLines(); }, ), ); } void _addContourLines() { // Add contour source (vector tiles with elevation data) mapController?.addVectorSource( 'contour-source', 'https://gateway.mapmetrics.org/contours/{z}/{x}/{y}.pbf', ); // Add thin contour lines (every 100m) mapController?.addLineLayer( 'contour-lines', 'contour-source', sourceLayer: 'contour', lineColor: '#8B4513', lineWidth: 0.5, lineOpacity: 0.5, ); // Add thicker lines for major contours (every 500m) mapController?.addLineLayer( 'contour-lines-major', 'contour-source', sourceLayer: 'contour', lineColor: '#8B4513', lineWidth: 1.5, lineOpacity: 0.7, filter: ['==', ['%', ['get', 'ele'], 500], 0], ); // Add elevation labels on major contours mapController?.addSymbolLayer( 'contour-labels', 'contour-source', sourceLayer: 'contour', textField: '{ele}m', textSize: 10.0, textColor: '#8B4513', symbolPlacement: 'line', filter: ['==', ['%', ['get', 'ele'], 500], 0], ); } } ``` ## Contour Lines with Hillshade Combine contour lines with hillshade for a classic topographic map: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TopoMapScreen extends StatefulWidget { @override _TopoMapScreenState createState() => _TopoMapScreenState(); } class _TopoMapScreenState extends State { MapMetricsController? mapController; bool showContours = true; bool showHillshade = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Topographic Map')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(45.9763, 7.6586), // Matterhorn zoom: 12.0, ), onStyleLoaded: () { _addLayers(); }, ), // Layer toggles Positioned( top: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(8), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: showHillshade, onChanged: (val) { setState(() => showHillshade = val!); _toggleLayer('hillshade-layer', val!); }, ), Text('Hillshade', style: TextStyle(fontSize: 13)), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: showContours, onChanged: (val) { setState(() => showContours = val!); _toggleLayer('contour-lines', val!); _toggleLayer('contour-lines-major', val!); _toggleLayer('contour-labels', val!); }, ), Text('Contours', style: TextStyle(fontSize: 13)), ], ), ], ), ), ), ), ], ), ); } void _addLayers() { // Hillshade mapController?.addRasterDemSource( 'dem-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.addHillshadeLayer( 'hillshade-layer', 'dem-source', hillshadeExaggeration: 0.4, hillshadeIlluminationDirection: 315.0, ); // Contour lines mapController?.addVectorSource( 'contour-source', 'https://gateway.mapmetrics.org/contours/{z}/{x}/{y}.pbf', ); mapController?.addLineLayer( 'contour-lines', 'contour-source', sourceLayer: 'contour', lineColor: '#8B4513', lineWidth: 0.5, lineOpacity: 0.4, ); mapController?.addLineLayer( 'contour-lines-major', 'contour-source', sourceLayer: 'contour', lineColor: '#8B4513', lineWidth: 1.2, lineOpacity: 0.6, filter: ['==', ['%', ['get', 'ele'], 500], 0], ); mapController?.addSymbolLayer( 'contour-labels', 'contour-source', sourceLayer: 'contour', textField: '{ele}m', textSize: 9.0, textColor: '#654321', symbolPlacement: 'line', filter: ['==', ['%', ['get', 'ele'], 500], 0], ); } void _toggleLayer(String layerId, bool visible) { mapController?.setLayerVisibility(layerId, visible); } } ``` ## Contour Line Properties | Property | Value | Description | |----------|-------|-------------| | `lineColor` | `#8B4513` | Brown for topographic convention | | `lineWidth` | 0.5 (minor) / 1.5 (major) | Thicker for index contours | | `lineOpacity` | 0.4 - 0.7 | Semi-transparent to not obscure the base map | | `filter` | `['==', ['%', ['get', 'ele'], 500], 0]` | Show only every 500m | ## Next Steps - [Add a Hillshade Layer](./flutter-add-hillshade-layer) — Shaded relief effect - [3D Terrain](./flutter-3d-terrain) — Full 3D elevation - [Satellite Terrain](./flutter-satellite-terrain) — Satellite with terrain --- **Tip**: Use brown (`#8B4513`) for contour lines — it's the cartographic standard. Show minor contours (every 100m) as thin/light lines and major contours (every 500m) as thick/labeled for readability. --- # Add Custom Icons with Markers in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-custom-icons-markers # Add Custom Icons with Markers in Flutter This tutorial shows how to create markers with custom icons — using asset images, network images, or Flutter widgets — instead of the default pin. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Custom Asset Icon Markers Use a local image asset as a marker icon: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CustomIconMarkerScreen extends StatefulWidget { @override _CustomIconMarkerScreenState createState() => _CustomIconMarkerScreenState(); } class _CustomIconMarkerScreenState extends State { MapMetricsController? mapController; Set markers = {}; @override void initState() { super.initState(); _loadCustomMarkers(); } Future _loadCustomMarkers() async { // Load a custom icon from assets final customIcon = await BitmapDescriptor.fromAssetImage( ImageConfiguration(size: Size(48, 48)), 'assets/icons/custom_pin.png', ); setState(() { markers = { Marker( markerId: MarkerId('paris'), position: LatLng(48.8566, 2.3522), icon: customIcon, infoWindow: InfoWindow(title: 'Paris'), ), Marker( markerId: MarkerId('london'), position: LatLng(51.5074, -0.1276), icon: customIcon, infoWindow: InfoWindow(title: 'London'), ), Marker( markerId: MarkerId('berlin'), position: LatLng(52.52, 13.405), icon: customIcon, infoWindow: InfoWindow(title: 'Berlin'), ), }; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Custom Icon Markers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(50.0, 5.0), zoom: 4.0, ), markers: markers, ), ); } } ``` Make sure to add your image to `assets/icons/` and declare it in `pubspec.yaml`: ```yaml flutter: assets: - assets/icons/custom_pin.png ``` ## Color-Coded Markers Use different hue values of the default marker for categories: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ColorCodedMarkersScreen extends StatefulWidget { @override _ColorCodedMarkersScreenState createState() => _ColorCodedMarkersScreenState(); } class _ColorCodedMarkersScreenState extends State { MapMetricsController? mapController; final Set markers = { // Restaurants - Red markers Marker( markerId: MarkerId('restaurant_1'), position: LatLng(48.8566, 2.3422), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'Le Petit Bistro', snippet: 'Restaurant'), ), Marker( markerId: MarkerId('restaurant_2'), position: LatLng(48.8600, 2.3500), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'Cafe de Flore', snippet: 'Restaurant'), ), // Hotels - Blue markers Marker( markerId: MarkerId('hotel_1'), position: LatLng(48.8650, 2.3300), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Grand Hotel', snippet: 'Hotel'), ), Marker( markerId: MarkerId('hotel_2'), position: LatLng(48.8530, 2.3600), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Hotel Rivoli', snippet: 'Hotel'), ), // Attractions - Green markers Marker( markerId: MarkerId('attraction_1'), position: LatLng(48.8584, 2.2945), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), infoWindow: InfoWindow(title: 'Eiffel Tower', snippet: 'Attraction'), ), Marker( markerId: MarkerId('attraction_2'), position: LatLng(48.8606, 2.3376), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), infoWindow: InfoWindow(title: 'Louvre Museum', snippet: 'Attraction'), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Color-Coded Markers')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3422), zoom: 13.0, ), markers: markers, ), // Legend Positioned( top: 16, left: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ _legendItem(Colors.red, 'Restaurants'), _legendItem(Colors.blue, 'Hotels'), _legendItem(Colors.green, 'Attractions'), ], ), ), ), ], ), ); } Widget _legendItem(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 2), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.location_on, color: color, size: 18), SizedBox(width: 4), Text(label, style: TextStyle(fontSize: 13)), ], ), ); } } ``` ## Custom Widget Markers Create fully custom markers using Flutter widgets painted to a bitmap: ```dart import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:mapmetrics/mapmetrics.dart'; class WidgetMarkerScreen extends StatefulWidget { @override _WidgetMarkerScreenState createState() => _WidgetMarkerScreenState(); } class _WidgetMarkerScreenState extends State { MapMetricsController? mapController; Set markers = {}; @override void initState() { super.initState(); _createWidgetMarkers(); } Future _createNumberedIcon(int number, Color color) async { final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); final size = 48.0; // Draw circle background final paint = Paint()..color = color; canvas.drawCircle(Offset(size / 2, size / 2), size / 2, paint); // Draw white border final borderPaint = Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 3; canvas.drawCircle(Offset(size / 2, size / 2), size / 2 - 1.5, borderPaint); // Draw number text final textPainter = TextPainter( text: TextSpan( text: '$number', style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, ); textPainter.layout(); textPainter.paint( canvas, Offset( (size - textPainter.width) / 2, (size - textPainter.height) / 2, ), ); final picture = recorder.endRecording(); final image = await picture.toImage(size.toInt(), size.toInt()); final bytes = await image.toByteData(format: ui.ImageByteFormat.png); return BitmapDescriptor.fromBytes(bytes!.buffer.asUint8List()); } Future _createWidgetMarkers() async { final locations = [ {'name': 'Stop 1: Eiffel Tower', 'lat': 48.8584, 'lng': 2.2945}, {'name': 'Stop 2: Louvre', 'lat': 48.8606, 'lng': 2.3376}, {'name': 'Stop 3: Notre-Dame', 'lat': 48.8530, 'lng': 2.3499}, {'name': 'Stop 4: Sacre-Coeur', 'lat': 48.8867, 'lng': 2.3431}, ]; final markerSet = {}; for (int i = 0; i < locations.length; i++) { final icon = await _createNumberedIcon(i + 1, Colors.blue); markerSet.add( Marker( markerId: MarkerId('stop_$i'), position: LatLng( locations[i]['lat'] as double, locations[i]['lng'] as double, ), icon: icon, infoWindow: InfoWindow(title: locations[i]['name'] as String), ), ); } setState(() { markers = markerSet; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Widget Markers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8600, 2.3200), zoom: 13.0, ), markers: markers, ), ); } } ``` ## Available Marker Hue Constants | Constant | Color | |----------|-------| | `BitmapDescriptor.hueRed` | Red (0.0) | | `BitmapDescriptor.hueOrange` | Orange (30.0) | | `BitmapDescriptor.hueYellow` | Yellow (60.0) | | `BitmapDescriptor.hueGreen` | Green (120.0) | | `BitmapDescriptor.hueCyan` | Cyan (180.0) | | `BitmapDescriptor.hueAzure` | Azure (210.0) | | `BitmapDescriptor.hueBlue` | Blue (240.0) | | `BitmapDescriptor.hueViolet` | Violet (270.0) | | `BitmapDescriptor.hueMagenta` | Magenta (300.0) | | `BitmapDescriptor.hueRose` | Rose (330.0) | ## Next Steps - [Add Image Markers](./flutter-add-image-marker) — Use network images as markers - [Draggable Marker](./flutter-draggable-marker) — Make markers draggable - [Filter Markers](./flutter-filter-markers) — Show/hide markers by category --- **Tip**: For the best quality on high-DPI screens, provide marker images at 2x or 3x resolution and use `ImageConfiguration(devicePixelRatio: 2.0)` when loading from assets. --- # Add a GeoJSON Line in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-geojson-line # Add a GeoJSON Line in Flutter This tutorial shows how to add a GeoJSON LineString to your MapMetrics Flutter map using a source and layer approach — ideal for routes, borders, or paths. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic GeoJSON Line Add a GeoJSON line source and render it as a styled line layer: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GeoJsonLineScreen extends StatefulWidget { @override _GeoJsonLineScreenState createState() => _GeoJsonLineScreenState(); } class _GeoJsonLineScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('GeoJSON Line')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(50.0, 10.0), zoom: 4.0, ), onStyleLoaded: () { _addGeoJsonLine(); }, ), ); } void _addGeoJsonLine() { // Define the GeoJSON data final geoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': [ [2.349902, 48.852966], // Paris [-0.1276, 51.5074], // London [13.405, 52.52], // Berlin [16.3738, 48.2082], // Vienna [12.4964, 41.9028], // Rome ], }, }; // Add the GeoJSON source mapController?.addGeoJsonSource('route-source', geoJson); // Add a line layer using the source mapController?.addLineLayer( 'route-layer', 'route-source', lineColor: '#3b82f6', lineWidth: 4.0, lineJoin: 'round', lineCap: 'round', ); } } ``` ## Styled GeoJSON Line Customize the line with dashes, opacity, and width: ```dart void _addStyledLine() { final geoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': [ [-3.7038, 40.4168], // Madrid [2.349902, 48.853], // Paris [13.405, 52.52], // Berlin ], }, }; mapController?.addGeoJsonSource('styled-route', geoJson); mapController?.addLineLayer( 'styled-route-layer', 'styled-route', lineColor: '#ef4444', lineWidth: 5.0, lineOpacity: 0.8, lineDashArray: [2.0, 1.0], // dashed pattern lineJoin: 'round', lineCap: 'round', ); } ``` ## Multiple GeoJSON Lines Display several routes with different styles: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultipleGeoJsonLinesScreen extends StatefulWidget { @override _MultipleGeoJsonLinesScreenState createState() => _MultipleGeoJsonLinesScreenState(); } class _MultipleGeoJsonLinesScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Multiple GeoJSON Lines')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), onStyleLoaded: () { _addMultipleLines(); }, ), ); } void _addMultipleLines() { // Route 1: Northern Europe final northRoute = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': [ [-0.1276, 51.5074], // London [4.9041, 52.3676], // Amsterdam [13.405, 52.52], // Berlin [21.0122, 52.2297], // Warsaw ], }, }; // Route 2: Southern Europe final southRoute = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': [ [-9.1393, 38.7223], // Lisbon [-3.7038, 40.4168], // Madrid [2.1734, 41.3851], // Barcelona [12.4964, 41.9028], // Rome [23.7275, 37.9838], // Athens ], }, }; mapController?.addGeoJsonSource('north-route', northRoute); mapController?.addLineLayer( 'north-route-layer', 'north-route', lineColor: '#3b82f6', lineWidth: 4.0, lineJoin: 'round', lineCap: 'round', ); mapController?.addGeoJsonSource('south-route', southRoute); mapController?.addLineLayer( 'south-route-layer', 'south-route', lineColor: '#ef4444', lineWidth: 4.0, lineDashArray: [3.0, 2.0], lineJoin: 'round', lineCap: 'round', ); } } ``` ## GeoJSON Line Properties | Property | Type | Description | |----------|------|-------------| | `lineColor` | `String` | Line color as hex string | | `lineWidth` | `double` | Width of the line in pixels | | `lineOpacity` | `double` | Opacity from 0.0 to 1.0 | | `lineDashArray` | `List` | Dash and gap lengths | | `lineJoin` | `String` | How line segments join: `round`, `bevel`, `miter` | | `lineCap` | `String` | Shape at line ends: `round`, `butt`, `square` | ## Next Steps - [Add a GeoJSON Polygon](./flutter-add-geojson-polygon) — Draw filled areas from GeoJSON - [Draw GeoJSON Points](./flutter-draw-geojson-points) — Render point data on the map - [Animate a Line](./flutter-animate-a-line) — Animate a line being drawn --- **Tip**: GeoJSON sources are powerful for displaying dynamic data. You can update the source data at runtime using `mapController?.setGeoJsonSource('source-id', newData)` to reflect real-time changes. --- # Add a GeoJSON Polygon in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-geojson-polygon # Add a GeoJSON Polygon in Flutter This tutorial shows how to add a GeoJSON Polygon to your MapMetrics Flutter map — perfect for highlighting regions, zones, or areas. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic GeoJSON Polygon Add a filled polygon from GeoJSON data: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GeoJsonPolygonScreen extends StatefulWidget { @override _GeoJsonPolygonScreenState createState() => _GeoJsonPolygonScreenState(); } class _GeoJsonPolygonScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('GeoJSON Polygon')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8, 2.35), zoom: 11.0, ), onStyleLoaded: () { _addGeoJsonPolygon(); }, ), ); } void _addGeoJsonPolygon() { final geoJson = { 'type': 'Feature', 'properties': {'name': 'Central Paris'}, 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [2.3200, 48.8400], [2.3800, 48.8400], [2.3800, 48.8700], [2.3200, 48.8700], [2.3200, 48.8400], // close the ring ] ], }, }; // Add the GeoJSON source mapController?.addGeoJsonSource('region-source', geoJson); // Add a fill layer mapController?.addFillLayer( 'region-fill', 'region-source', fillColor: '#3b82f6', fillOpacity: 0.3, ); // Add an outline layer mapController?.addLineLayer( 'region-outline', 'region-source', lineColor: '#1d4ed8', lineWidth: 2.0, ); } } ``` ## Multiple Polygons with FeatureCollection Display several regions with different colors: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultiPolygonScreen extends StatefulWidget { @override _MultiPolygonScreenState createState() => _MultiPolygonScreenState(); } class _MultiPolygonScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Multiple Polygons')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), onStyleLoaded: () { _addMultiplePolygons(); }, ), ); } void _addMultiplePolygons() { final featureCollection = { 'type': 'FeatureCollection', 'features': [ { 'type': 'Feature', 'properties': {'name': 'France', 'color': 'blue'}, 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [-1.75, 43.3], [3.0, 43.3], [7.7, 43.8], [8.2, 48.9], [2.5, 51.1], [-4.8, 48.5], [-1.75, 43.3], ] ], }, }, { 'type': 'Feature', 'properties': {'name': 'Germany', 'color': 'red'}, 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [5.9, 47.3], [15.0, 47.3], [15.0, 55.0], [5.9, 55.0], [5.9, 47.3], ] ], }, }, ], }; mapController?.addGeoJsonSource('countries', featureCollection); // Fill layer with semi-transparent color mapController?.addFillLayer( 'countries-fill', 'countries', fillColor: '#3b82f6', fillOpacity: 0.2, ); // Outline layer mapController?.addLineLayer( 'countries-outline', 'countries', lineColor: '#1e40af', lineWidth: 2.0, ); } } ``` ## Polygon with Hole Create a polygon with a cutout hole inside: ```dart void _addPolygonWithHole() { final geoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'Polygon', 'coordinates': [ // Outer ring [ [2.2800, 48.8200], [2.4200, 48.8200], [2.4200, 48.8900], [2.2800, 48.8900], [2.2800, 48.8200], ], // Inner ring (hole) [ [2.3300, 48.8450], [2.3700, 48.8450], [2.3700, 48.8650], [2.3300, 48.8650], [2.3300, 48.8450], ], ], }, }; mapController?.addGeoJsonSource('polygon-hole', geoJson); mapController?.addFillLayer( 'polygon-hole-fill', 'polygon-hole', fillColor: '#22c55e', fillOpacity: 0.4, ); mapController?.addLineLayer( 'polygon-hole-outline', 'polygon-hole', lineColor: '#15803d', lineWidth: 2.0, ); } ``` ## GeoJSON Polygon Properties | Property | Type | Description | |----------|------|-------------| | `fillColor` | `String` | Fill color as hex string | | `fillOpacity` | `double` | Fill opacity from 0.0 to 1.0 | | `lineColor` | `String` | Outline color as hex string | | `lineWidth` | `double` | Outline width in pixels | ## Next Steps - [Add a GeoJSON Line](./flutter-add-geojson-line) — Draw lines from GeoJSON data - [Draw GeoJSON Points](./flutter-draw-geojson-points) — Render point data on the map - [Show Polygon Info on Click](./flutter-show-polygon-info-on-click) — Display polygon data on tap --- **Tip**: Always close polygon rings — the first and last coordinate must be identical. Use `FeatureCollection` to group multiple polygons into one source for better performance. --- # Add a Hillshade Layer in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-hillshade-layer # Add a Hillshade Layer in Flutter This tutorial shows how to add a hillshade layer to your MapMetrics Flutter map — creating a shaded relief effect that makes terrain features like mountains, valleys, and ridges visible on a 2D map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Hillshade Add a hillshade layer using a raster DEM (Digital Elevation Model) source: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class HillshadeScreen extends StatefulWidget { @override _HillshadeScreenState createState() => _HillshadeScreenState(); } class _HillshadeScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hillshade Layer')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), // Swiss Alps zoom: 9.0, ), onStyleLoaded: () { _addHillshade(); }, ), ); } void _addHillshade() { // Add terrain DEM source mapController?.addRasterDemSource( 'hillshade-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); // Add hillshade layer mapController?.addHillshadeLayer( 'hillshade-layer', 'hillshade-source', hillshadeExaggeration: 0.5, hillshadeShadowColor: '#000000', hillshadeHighlightColor: '#ffffff', hillshadeAccentColor: '#000000', hillshadeIlluminationDirection: 315.0, ); } } ``` ## Hillshade with Adjustable Light Direction Let users control the sun direction to see terrain from different angles: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AdjustableHillshadeScreen extends StatefulWidget { @override _AdjustableHillshadeScreenState createState() => _AdjustableHillshadeScreenState(); } class _AdjustableHillshadeScreenState extends State { MapMetricsController? mapController; double lightDirection = 315.0; double exaggeration = 0.5; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Adjustable Hillshade')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), zoom: 9.0, ), onStyleLoaded: () { _addHillshade(); }, ), // Controls Positioned( bottom: 16, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Icon(Icons.wb_sunny, size: 18), SizedBox(width: 8), Text('Light: ${lightDirection.toInt()}°'), Expanded( child: Slider( value: lightDirection, min: 0, max: 360, onChanged: (val) { setState(() => lightDirection = val); mapController?.setPaintProperty( 'hillshade-layer', 'hillshade-illumination-direction', val, ); }, ), ), ], ), Row( children: [ Icon(Icons.terrain, size: 18), SizedBox(width: 8), Text('Relief: ${exaggeration.toStringAsFixed(1)}'), Expanded( child: Slider( value: exaggeration, min: 0.0, max: 1.0, onChanged: (val) { setState(() => exaggeration = val); mapController?.setPaintProperty( 'hillshade-layer', 'hillshade-exaggeration', val, ); }, ), ), ], ), ], ), ), ), ), ], ), ); } void _addHillshade() { mapController?.addRasterDemSource( 'hillshade-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.addHillshadeLayer( 'hillshade-layer', 'hillshade-source', hillshadeExaggeration: exaggeration, hillshadeIlluminationDirection: lightDirection, ); } } ``` ## Hillshade with Location Presets Jump to famous mountain ranges to see the hillshade effect: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class HillshadePresetsScreen extends StatefulWidget { @override _HillshadePresetsScreenState createState() => _HillshadePresetsScreenState(); } class _HillshadePresetsScreenState extends State { MapMetricsController? mapController; final List> presets = [ {'name': 'Swiss Alps', 'lat': 46.818, 'lng': 8.228, 'zoom': 9.0}, {'name': 'Norwegian Fjords', 'lat': 61.0, 'lng': 6.5, 'zoom': 8.0}, {'name': 'Pyrenees', 'lat': 42.695, 'lng': 0.041, 'zoom': 8.0}, {'name': 'Scottish Highlands', 'lat': 57.0, 'lng': -5.0, 'zoom': 8.0}, {'name': 'Dolomites', 'lat': 46.410, 'lng': 11.844, 'zoom': 10.0}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hillshade Presets')), body: Column( children: [ Container( padding: EdgeInsets.all(8), child: Wrap( spacing: 6, runSpacing: 6, children: presets.map((p) { return ActionChip( avatar: Icon(Icons.terrain, size: 16), label: Text(p['name'], style: TextStyle(fontSize: 12)), onPressed: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(p['lat'], p['lng']), p['zoom'], ), ); }, ); }).toList(), ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.818, 8.228), zoom: 9.0, ), onStyleLoaded: () { mapController?.addRasterDemSource( 'hillshade-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.addHillshadeLayer( 'hillshade-layer', 'hillshade-source', hillshadeExaggeration: 0.5, hillshadeIlluminationDirection: 315.0, ); }, ), ), ], ), ); } } ``` ## Hillshade Properties | Property | Type | Default | Description | |----------|------|---------|-------------| | `hillshadeExaggeration` | `double` | 0.5 | Intensity of the shading (0.0 - 1.0) | | `hillshadeIlluminationDirection` | `double` | 335.0 | Sun angle in degrees (0-360) | | `hillshadeShadowColor` | `String` | `#000000` | Color of shaded areas | | `hillshadeHighlightColor` | `String` | `#ffffff` | Color of illuminated areas | | `hillshadeAccentColor` | `String` | `#000000` | Color for emphasizing terrain | ## Next Steps - [3D Terrain](./flutter-3d-terrain) — Full 3D terrain elevation - [Add Contour Lines](./flutter-add-contour-lines) — Elevation contour lines - [Satellite Terrain](./flutter-satellite-terrain) — Satellite with elevation --- **Tip**: Hillshade works on flat 2D maps (no tilt needed) and is lighter on performance than full 3D terrain. It's ideal for hiking apps where you want terrain visibility without the rendering overhead of 3D. --- # Add an Icon to the Map in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-icon-to-map # Add an Icon to the Map in Flutter This tutorial shows how to add custom icon images to the map style and use them as symbols on markers or layers. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Add Asset Image as Map Icon Load a local asset image and add it to the map style for use in symbol layers: ```dart import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AddIconToMapScreen extends StatefulWidget { @override _AddIconToMapScreenState createState() => _AddIconToMapScreenState(); } class _AddIconToMapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Add Icon to Map')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), onStyleLoaded: () { _addIconAndSymbolLayer(); }, ), ); } Future _addIconAndSymbolLayer() async { // Load the icon image from assets final ByteData bytes = await rootBundle.load('assets/icons/pin.png'); final Uint8List imageData = bytes.buffer.asUint8List(); // Add the image to the map style await mapController?.addImage('custom-pin', imageData); // Create a GeoJSON source with points final geoJson = { 'type': 'FeatureCollection', 'features': [ { 'type': 'Feature', 'properties': {'name': 'Eiffel Tower'}, 'geometry': { 'type': 'Point', 'coordinates': [2.2945, 48.8584], }, }, { 'type': 'Feature', 'properties': {'name': 'Louvre Museum'}, 'geometry': { 'type': 'Point', 'coordinates': [2.3376, 48.8606], }, }, { 'type': 'Feature', 'properties': {'name': 'Notre-Dame'}, 'geometry': { 'type': 'Point', 'coordinates': [2.3499, 48.8530], }, }, ], }; // Add source and symbol layer using the custom icon mapController?.addGeoJsonSource('landmarks', geoJson); mapController?.addSymbolLayer( 'landmarks-icons', 'landmarks', iconImage: 'custom-pin', iconSize: 0.5, textField: '{name}', textSize: 12.0, textOffset: [0.0, 1.5], textAnchor: 'top', ); } } ``` Make sure to declare the asset in `pubspec.yaml`: ```yaml flutter: assets: - assets/icons/pin.png ``` ## Multiple Icon Types Add different icons for different place categories: ```dart import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultiIconScreen extends StatefulWidget { @override _MultiIconScreenState createState() => _MultiIconScreenState(); } class _MultiIconScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Multiple Icons')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3400), zoom: 13.0, ), onStyleLoaded: () { _addMultipleIcons(); }, ), // Legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Legend', style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(height: 4), _legendRow(Icons.restaurant, Colors.red, 'Restaurants'), _legendRow(Icons.hotel, Colors.blue, 'Hotels'), _legendRow(Icons.museum, Colors.green, 'Museums'), ], ), ), ), ), ], ), ); } Widget _legendRow(IconData icon, Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 2), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, color: color, size: 18), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 13)), ], ), ); } Future _addMultipleIcons() async { // Load different icon assets final restaurantBytes = await rootBundle.load('assets/icons/restaurant.png'); final hotelBytes = await rootBundle.load('assets/icons/hotel.png'); final museumBytes = await rootBundle.load('assets/icons/museum.png'); await mapController?.addImage( 'icon-restaurant', restaurantBytes.buffer.asUint8List()); await mapController?.addImage( 'icon-hotel', hotelBytes.buffer.asUint8List()); await mapController?.addImage( 'icon-museum', museumBytes.buffer.asUint8List()); final geoJson = { 'type': 'FeatureCollection', 'features': [ { 'type': 'Feature', 'properties': {'name': 'Le Jules Verne', 'icon': 'icon-restaurant'}, 'geometry': { 'type': 'Point', 'coordinates': [2.2945, 48.8580], }, }, { 'type': 'Feature', 'properties': {'name': 'Hotel Ritz', 'icon': 'icon-hotel'}, 'geometry': { 'type': 'Point', 'coordinates': [2.3285, 48.8682], }, }, { 'type': 'Feature', 'properties': {'name': 'Louvre Museum', 'icon': 'icon-museum'}, 'geometry': { 'type': 'Point', 'coordinates': [2.3376, 48.8606], }, }, ], }; mapController?.addGeoJsonSource('places', geoJson); // Use data-driven icon based on the 'icon' property mapController?.addSymbolLayer( 'places-layer', 'places', iconImage: '{icon}', // References the 'icon' property in GeoJSON iconSize: 0.4, textField: '{name}', textSize: 11.0, textOffset: [0.0, 1.8], textAnchor: 'top', textColor: '#333333', ); } } ``` ## Generate Icon from Flutter Widget Create an icon programmatically using Canvas drawing: ```dart import 'dart:ui' as ui; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GeneratedIconScreen extends StatefulWidget { @override _GeneratedIconScreenState createState() => _GeneratedIconScreenState(); } class _GeneratedIconScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Generated Icon')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3400), zoom: 13.0, ), onStyleLoaded: () { _addGeneratedIcons(); }, ), ); } /// Draw a colored circle icon with a label Future _generateCircleIcon( Color color, String label, double size) async { final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); // Draw filled circle final paint = Paint()..color = color; canvas.drawCircle(Offset(size / 2, size / 2), size / 2, paint); // Draw border final borderPaint = Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 3; canvas.drawCircle( Offset(size / 2, size / 2), size / 2 - 1.5, borderPaint); // Draw label final textPainter = TextPainter( text: TextSpan( text: label, style: TextStyle( color: Colors.white, fontSize: size * 0.35, fontWeight: FontWeight.bold), ), textDirection: TextDirection.ltr, ); textPainter.layout(); textPainter.paint( canvas, Offset( (size - textPainter.width) / 2, (size - textPainter.height) / 2), ); final picture = recorder.endRecording(); final image = await picture.toImage(size.toInt(), size.toInt()); final bytes = await image.toByteData(format: ui.ImageByteFormat.png); return bytes!.buffer.asUint8List(); } Future _addGeneratedIcons() async { // Generate numbered icons final colors = [Colors.blue, Colors.red, Colors.green]; final labels = ['1', '2', '3']; final positions = [ [2.2945, 48.8584], [2.3376, 48.8606], [2.3499, 48.8530], ]; final names = ['Eiffel Tower', 'Louvre', 'Notre-Dame']; for (int i = 0; i < 3; i++) { final iconData = await _generateCircleIcon(colors[i], labels[i], 64); await mapController?.addImage('gen-icon-$i', iconData); } final features = >[]; for (int i = 0; i < positions.length; i++) { features.add({ 'type': 'Feature', 'properties': {'name': names[i], 'iconId': 'gen-icon-$i'}, 'geometry': { 'type': 'Point', 'coordinates': positions[i], }, }); } mapController?.addGeoJsonSource('generated-icons', { 'type': 'FeatureCollection', 'features': features, }); mapController?.addSymbolLayer( 'generated-icons-layer', 'generated-icons', iconImage: '{iconId}', iconSize: 0.6, textField: '{name}', textSize: 12.0, textOffset: [0.0, 2.0], textAnchor: 'top', ); } } ``` ## Next Steps - [Add Custom Icons with Markers](./flutter-add-custom-icons-markers) — Use custom marker icons - [Add Image Markers](./flutter-add-image-marker) — Network image markers - [Draw GeoJSON Points](./flutter-draw-geojson-points) — Circle-based point rendering --- **Tip**: For data-driven icons, set the `iconImage` property to `'{propertyName}'` — the map engine will look up the icon name from each feature's properties. This lets you use different icons for different categories from a single layer. --- # Add Image Markers in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-image-marker # Add Image Markers in Flutter This tutorial shows how to use custom images as map markers instead of the default pin icons. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Using Asset Images First, add your marker image to your project's `assets/images/` folder and register it in `pubspec.yaml`: ```yaml flutter: assets: - assets/images/ ``` Then use `BitmapDescriptor.fromAssetImage` to load it: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ImageMarkerScreen extends StatefulWidget { @override _ImageMarkerScreenState createState() => _ImageMarkerScreenState(); } class _ImageMarkerScreenState extends State { MapMetricsController? mapController; Set markers = {}; @override void initState() { super.initState(); _loadMarkers(); } Future _loadMarkers() async { final BitmapDescriptor customIcon = await BitmapDescriptor.fromAssetImage( ImageConfiguration(size: Size(48, 48)), 'assets/images/custom_pin.png', ); setState(() { markers = { Marker( markerId: MarkerId('cafe'), position: LatLng(48.8566, 2.3522), icon: customIcon, infoWindow: InfoWindow( title: 'My Favorite Café', snippet: 'Best coffee in Paris', ), ), }; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Image Markers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 14.0, ), markers: markers, ), ); } } ``` ## Multiple Image Markers from Data Load several markers with different custom icons from a data list: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultiImageMarkersScreen extends StatefulWidget { @override _MultiImageMarkersScreenState createState() => _MultiImageMarkersScreenState(); } class _MultiImageMarkersScreenState extends State { MapMetricsController? mapController; Set markers = {}; final List> places = [ { 'id': 'restaurant', 'name': 'Le Bistrot', 'snippet': 'French restaurant', 'position': LatLng(48.8580, 2.3500), 'icon': 'assets/images/restaurant_pin.png', }, { 'id': 'hotel', 'name': 'Grand Hotel', 'snippet': '5-star hotel', 'position': LatLng(48.8550, 2.3480), 'icon': 'assets/images/hotel_pin.png', }, { 'id': 'museum', 'name': 'Art Gallery', 'snippet': 'Modern art museum', 'position': LatLng(48.8540, 2.3550), 'icon': 'assets/images/museum_pin.png', }, ]; @override void initState() { super.initState(); _loadMarkers(); } Future _loadMarkers() async { final Set loadedMarkers = {}; for (final place in places) { final icon = await BitmapDescriptor.fromAssetImage( ImageConfiguration(size: Size(48, 48)), place['icon'], ); loadedMarkers.add( Marker( markerId: MarkerId(place['id']), position: place['position'], icon: icon, infoWindow: InfoWindow( title: place['name'], snippet: place['snippet'], ), ), ); } setState(() { markers = loadedMarkers; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Places in Paris')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8560, 2.3510), zoom: 15.0, ), markers: markers, ), ); } } ``` ## Using Network Images Load marker icons from a URL: ```dart Future _loadNetworkMarker() async { final BitmapDescriptor networkIcon = await BitmapDescriptor.fromNetworkImage( ImageConfiguration(size: Size(48, 48)), 'https://example.com/marker-icon.png', ); setState(() { markers.add( Marker( markerId: MarkerId('network_marker'), position: LatLng(48.8600, 2.3400), icon: networkIcon, infoWindow: InfoWindow(title: 'Network Icon'), ), ); }); } ``` ## Create Markers from Widgets For fully custom markers, you can create a `BitmapDescriptor` from a Flutter widget: ```dart Future _createWidgetMarker(String label, Color color) async { return await BitmapDescriptor.fromWidget( Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow(color: Colors.black26, blurRadius: 4, offset: Offset(0, 2)), ], ), child: Text( label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12), ), ), ); } // Usage final icon = await _createWidgetMarker('Café', Colors.brown); ``` ## Image Marker Best Practices | Tip | Details | |-----|---------| | **Size** | Keep images small (48x48 to 96x96 px) for performance | | **Format** | Use PNG with transparency for best results | | **Resolution** | Provide 2x/3x variants for high-DPI screens | | **Loading** | Load icons in `initState` — they are async operations | | **Caching** | Store loaded `BitmapDescriptor` objects to avoid reloading | ## Next Steps - [Markers and Annotations](./flutter-markers) — Default markers with color options - [Draggable Marker](./flutter-draggable-marker) — Make image markers draggable - [Add a Popup](./flutter-add-a-popup) — Show popups on image marker taps --- **Tip**: Always load your `BitmapDescriptor` icons asynchronously in `initState` and call `setState` when they are ready. Trying to create them synchronously in the `build` method will cause errors. --- # Add a Layer Below Labels in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-layer-below-labels # Add a Layer Below Labels in Flutter This tutorial shows how to insert new layers below the map's text labels — so your data layers don't cover up important place names and road labels. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Insert Layer Below Labels Add a polygon fill that renders beneath all text labels: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LayerBelowLabelsScreen extends StatefulWidget { @override _LayerBelowLabelsScreenState createState() => _LayerBelowLabelsScreenState(); } class _LayerBelowLabelsScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Layer Below Labels')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), onStyleLoaded: () { _addLayerBelowLabels(); }, ), ); } void _addLayerBelowLabels() { // Find the first symbol (label) layer in the style final firstSymbolLayer = _findFirstSymbolLayer(); // Add polygon source final geoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [2.28, 48.84], [2.42, 48.84], [2.42, 48.88], [2.28, 48.88], [2.28, 48.84], ] ], }, }; mapController?.addGeoJsonSource('highlight-area', geoJson); // Insert fill layer BELOW the first label layer mapController?.addFillLayer( 'highlight-fill', 'highlight-area', fillColor: '#3b82f6', fillOpacity: 0.3, belowLayerId: firstSymbolLayer, // Insert below labels ); // Insert outline also below labels mapController?.addLineLayer( 'highlight-outline', 'highlight-area', lineColor: '#1d4ed8', lineWidth: 2.0, belowLayerId: firstSymbolLayer, ); } /// Find the first symbol layer (text/label) in the map style String? _findFirstSymbolLayer() { final layers = mapController?.getStyleLayers(); if (layers != null) { for (final layer in layers) { if (layer.type == 'symbol') { return layer.id; } } } return null; // If no symbol layer found, add on top } } ``` ## Multiple Data Layers with Proper Ordering Add several data layers that all sit below labels: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class OrderedLayersScreen extends StatefulWidget { @override _OrderedLayersScreenState createState() => _OrderedLayersScreenState(); } class _OrderedLayersScreenState extends State { MapMetricsController? mapController; bool showZones = true; bool showRoutes = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Ordered Layers')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.857, 2.345), zoom: 12.0, ), onStyleLoaded: () { _addOrderedLayers(); }, ), // Layer toggles Positioned( top: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(8), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: showZones, onChanged: (val) { setState(() => showZones = val!); mapController?.setLayerVisibility( 'zone-fill', val!); mapController?.setLayerVisibility( 'zone-outline', val!); }, ), Text('Zones'), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: showRoutes, onChanged: (val) { setState(() => showRoutes = val!); mapController?.setLayerVisibility( 'route-line', val!); }, ), Text('Routes'), ], ), ], ), ), ), ), ], ), ); } void _addOrderedLayers() { final firstSymbol = _findFirstSymbolLayer(); // Layer 1: Zone polygons (bottom) final zoneGeoJson = { 'type': 'FeatureCollection', 'features': [ { 'type': 'Feature', 'properties': {'name': 'Zone A'}, 'geometry': { 'type': 'Polygon', 'coordinates': [[ [2.30, 48.84], [2.36, 48.84], [2.36, 48.87], [2.30, 48.87], [2.30, 48.84], ]], }, }, { 'type': 'Feature', 'properties': {'name': 'Zone B'}, 'geometry': { 'type': 'Polygon', 'coordinates': [[ [2.34, 48.85], [2.40, 48.85], [2.40, 48.88], [2.34, 48.88], [2.34, 48.85], ]], }, }, ], }; mapController?.addGeoJsonSource('zones', zoneGeoJson); mapController?.addFillLayer( 'zone-fill', 'zones', fillColor: '#22c55e', fillOpacity: 0.2, belowLayerId: firstSymbol, ); mapController?.addLineLayer( 'zone-outline', 'zones', lineColor: '#15803d', lineWidth: 2.0, belowLayerId: firstSymbol, ); // Layer 2: Route lines (above zones, below labels) final routeGeoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': [ [2.29, 48.86], [2.34, 48.855], [2.38, 48.86], [2.41, 48.865], ], }, }; mapController?.addGeoJsonSource('route', routeGeoJson); mapController?.addLineLayer( 'route-line', 'route', lineColor: '#ef4444', lineWidth: 4.0, lineJoin: 'round', lineCap: 'round', belowLayerId: firstSymbol, ); } String? _findFirstSymbolLayer() { final layers = mapController?.getStyleLayers(); if (layers != null) { for (final layer in layers) { if (layer.type == 'symbol') return layer.id; } } return null; } } ``` ## Layer Ordering | Position | Layer Types | Labels Visible? | |----------|-------------|-----------------| | Top (default) | Data added without `belowLayerId` | Covered by data | | Below labels | Data with `belowLayerId: firstSymbol` | Yes, readable | | Bottom | Base map tiles | Always below everything | ## Next Steps - [Add a GeoJSON Polygon](./flutter-add-geojson-polygon) — Draw polygons - [Change Layer Color](./flutter-change-layer-color) — Dynamic layer styling - [Add a GeoJSON Line](./flutter-add-geojson-line) — Draw lines --- **Tip**: Always insert data layers below labels in production apps. Users expect to see place names even when data overlays are active. Use `getStyleLayers()` to find the right insertion point. --- # Add a Pattern to a Polygon in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-add-pattern-to-polygon # Add a Pattern to a Polygon in Flutter This tutorial shows how to create patterned polygon fills — stripes, crosshatch, dots, or custom patterns — instead of a plain solid color. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Striped Polygon Using Overlapping Lines Simulate a striped pattern by overlaying diagonal lines on top of a filled polygon: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class StripedPolygonScreen extends StatefulWidget { @override _StripedPolygonScreenState createState() => _StripedPolygonScreenState(); } class _StripedPolygonScreenState extends State { MapMetricsController? mapController; final List regionPoints = [ LatLng(48.88, 2.28), LatLng(48.88, 2.40), LatLng(48.82, 2.40), LatLng(48.82, 2.28), LatLng(48.88, 2.28), // close ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Striped Polygon')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.85, 2.34), zoom: 12.0, ), polygons: { // Semi-transparent fill Polygon( polygonId: PolygonId('region_fill'), points: regionPoints, fillColor: Colors.blue.withOpacity(0.15), strokeColor: Colors.blue, strokeWidth: 2, ), }, polylines: _buildStripeLines(), ), ); } /// Generate diagonal stripe lines across the polygon area Set _buildStripeLines() { final stripes = {}; final step = 0.008; // spacing between stripes // Generate diagonal lines from bottom-left to top-right for (double offset = -0.15; offset < 0.25; offset += step) { final lat1 = 48.82; final lng1 = 2.28 + offset; final lat2 = 48.88; final lng2 = 2.28 + offset + 0.06; // Only draw if within polygon bounds if (lng1 < 2.40 || lng2 > 2.28) { stripes.add( Polyline( polylineId: PolylineId('stripe_${offset.toStringAsFixed(3)}'), points: [ LatLng(lat1, lng1.clamp(2.28, 2.40)), LatLng(lat2, lng2.clamp(2.28, 2.40)), ], color: Colors.blue.withOpacity(0.3), width: 1, ), ); } } return stripes; } } ``` ## Dashed Border Polygon Use dashed polyline borders around a filled polygon: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DashedPolygonScreen extends StatefulWidget { @override _DashedPolygonScreenState createState() => _DashedPolygonScreenState(); } class _DashedPolygonScreenState extends State { MapMetricsController? mapController; final List area = [ LatLng(48.870, 2.300), LatLng(48.870, 2.370), LatLng(48.840, 2.370), LatLng(48.840, 2.300), LatLng(48.870, 2.300), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Dashed Border Polygon')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.855, 2.335), zoom: 13.0, ), polygons: { Polygon( polygonId: PolygonId('dashed_area'), points: area, fillColor: Colors.orange.withOpacity(0.15), strokeWidth: 0, // no solid stroke ), }, polylines: { Polyline( polylineId: PolylineId('dashed_border'), points: area, color: Colors.orange, width: 3, patterns: [PatternItem.dash(15), PatternItem.gap(10)], ), }, ), ); } } ``` ## Multiple Pattern Styles Show several polygons with different visual patterns: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultiPatternScreen extends StatefulWidget { @override _MultiPatternScreenState createState() => _MultiPatternScreenState(); } class _MultiPatternScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Pattern Styles')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.855, 2.335), zoom: 12.5, ), polygons: { // Zone A: Solid fill Polygon( polygonId: PolygonId('solid'), points: [ LatLng(48.870, 2.280), LatLng(48.870, 2.320), LatLng(48.855, 2.320), LatLng(48.855, 2.280), LatLng(48.870, 2.280), ], fillColor: Colors.blue.withOpacity(0.3), strokeColor: Colors.blue, strokeWidth: 2, ), // Zone B: Light with thick border Polygon( polygonId: PolygonId('thick_border'), points: [ LatLng(48.870, 2.330), LatLng(48.870, 2.370), LatLng(48.855, 2.370), LatLng(48.855, 2.330), LatLng(48.870, 2.330), ], fillColor: Colors.green.withOpacity(0.1), strokeColor: Colors.green, strokeWidth: 4, ), // Zone C: Very transparent Polygon( polygonId: PolygonId('transparent'), points: [ LatLng(48.850, 2.280), LatLng(48.850, 2.320), LatLng(48.835, 2.320), LatLng(48.835, 2.280), LatLng(48.850, 2.280), ], fillColor: Colors.red.withOpacity(0.05), strokeColor: Colors.red, strokeWidth: 2, ), }, polylines: { // Zone D: Dotted border Polyline( polylineId: PolylineId('dotted_border'), points: [ LatLng(48.850, 2.330), LatLng(48.850, 2.370), LatLng(48.835, 2.370), LatLng(48.835, 2.330), LatLng(48.850, 2.330), ], color: Colors.purple, width: 3, patterns: [PatternItem.dot], ), }, ), // Legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Zones', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12)), _legendRow(Colors.blue, 'A: Solid fill'), _legendRow(Colors.green, 'B: Thick border'), _legendRow(Colors.red, 'C: Transparent'), _legendRow(Colors.purple, 'D: Dotted border'), ], ), ), ), ), ], ), ); } Widget _legendRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 14, height: 14, decoration: BoxDecoration( color: color.withOpacity(0.3), border: Border.all(color: color, width: 1.5), )), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 11)), ], ), ); } } ``` ## Pattern Techniques | Technique | Method | Best For | |-----------|--------|----------| | Solid fill + border | `Polygon` with `fillColor` + `strokeColor` | Default zones | | Dashed border | `Polyline` with `PatternItem.dash` | Boundaries, limits | | Dotted border | `Polyline` with `PatternItem.dot` | Proposed areas | | Stripe overlay | Multiple `Polyline` on top of `Polygon` | Restricted zones | | Transparency | Low `fillColor` opacity | Background regions | ## Next Steps - [Add a Polygon](./flutter-add-a-polygon) — Basic polygon drawing - [Add a GeoJSON Polygon](./flutter-add-geojson-polygon) — GeoJSON-based polygons - [Show Polygon Info on Click](./flutter-show-polygon-info-on-click) — Interactive polygons --- **Tip**: Combine a semi-transparent `Polygon` fill with a `Polyline` border using `PatternItem.dash` for a professional "planned area" or "restricted zone" look. --- # Animate a Line in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-animate-a-line # Animate a Line in Flutter This tutorial shows how to animate a polyline being drawn on the map step by step, as if tracing a route in real time. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Line Animation Draw a route one segment at a time using an `AnimationController`: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AnimateLineScreen extends StatefulWidget { @override _AnimateLineScreenState createState() => _AnimateLineScreenState(); } class _AnimateLineScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animationController; bool isAnimating = false; // Full route coordinates (Paris walking route) final List fullRoute = [ LatLng(48.8584, 2.2945), // Eiffel Tower LatLng(48.8575, 2.3050), LatLng(48.8560, 2.3150), LatLng(48.8580, 2.3250), LatLng(48.8606, 2.3376), // Louvre LatLng(48.8590, 2.3420), LatLng(48.8560, 2.3460), LatLng(48.8530, 2.3499), // Notre-Dame ]; List get visibleRoute { final progress = _animationController.value; final totalPoints = fullRoute.length; final visibleCount = (progress * totalPoints).ceil().clamp(1, totalPoints); return fullRoute.sublist(0, visibleCount); } Set get polylines => { // Faded full route (background) Polyline( polylineId: PolylineId('full_route'), points: fullRoute, color: Colors.blue.withOpacity(0.2), width: 3, patterns: [PatternItem.dash(8), PatternItem.gap(6)], ), // Animated route (foreground) if (visibleRoute.length >= 2) Polyline( polylineId: PolylineId('animated_route'), points: visibleRoute, color: Colors.blue, width: 4, ), }; Set get markers => { // Start marker Marker( markerId: MarkerId('start'), position: fullRoute.first, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), infoWindow: InfoWindow(title: 'Start'), ), // End marker (only show when animation is complete) if (_animationController.value >= 1.0) Marker( markerId: MarkerId('end'), position: fullRoute.last, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'End'), ), // Current position marker if (visibleRoute.isNotEmpty && _animationController.value < 1.0) Marker( markerId: MarkerId('current'), position: visibleRoute.last, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), ), }; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 5), ); _animationController.addListener(() => setState(() {})); _animationController.addStatusListener((status) { if (status == AnimationStatus.completed) { setState(() => isAnimating = false); } }); } @override Widget build(BuildContext context) { final progress = (_animationController.value * 100).toInt(); return Scaffold( appBar: AppBar(title: Text('Animate a Line')), body: Column( children: [ // Progress bar Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Colors.grey[100], child: Row( children: [ Text('Progress: $progress%'), SizedBox(width: 12), Expanded( child: LinearProgressIndicator( value: _animationController.value, backgroundColor: Colors.grey[300], ), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8570, 2.3200), zoom: 14.0, ), polylines: polylines, markers: markers, ), ), ], ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton.small( heroTag: 'reset', onPressed: _reset, child: Icon(Icons.replay), ), SizedBox(width: 8), FloatingActionButton.extended( heroTag: 'play', onPressed: isAnimating ? _pause : _play, icon: Icon(isAnimating ? Icons.pause : Icons.play_arrow), label: Text(isAnimating ? 'Pause' : 'Draw'), ), ], ), ); } void _play() { setState(() => isAnimating = true); if (_animationController.value >= 1.0) { _animationController.reset(); } _animationController.forward(); } void _pause() { _animationController.stop(); setState(() => isAnimating = false); } void _reset() { _animationController.reset(); setState(() => isAnimating = false); } @override void dispose() { _animationController.dispose(); mapController?.dispose(); super.dispose(); } } ``` ## Smooth Interpolated Animation For smoother line drawing, interpolate between points: ```dart List get smoothRoute { final progress = _animationController.value; final totalSegments = fullRoute.length - 1; final exactPosition = progress * totalSegments; final segmentIndex = exactPosition.floor().clamp(0, totalSegments - 1); final t = exactPosition - segmentIndex; // All completed segments plus interpolated current segment final result = fullRoute.sublist(0, segmentIndex + 1); if (segmentIndex < totalSegments) { final from = fullRoute[segmentIndex]; final to = fullRoute[segmentIndex + 1]; result.add(LatLng( from.latitude + (to.latitude - from.latitude) * t, from.longitude + (to.longitude - from.longitude) * t, )); } return result; } ``` ## Next Steps - [Animate a Marker](./flutter-animate-marker) — Move a marker along a route - [Add a Polyline](./flutter-add-a-polyline) — Static polyline styling - [Fly to a Location](./flutter-fly-to-location) — Animate the camera along with the line --- **Tip**: Show a faded version of the full route as a background layer so users can see where the line is heading, while the solid animated line shows progress. --- # Animate Camera Around a Point in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-animate-camera-around-point # Animate Camera Around a Point in Flutter This tutorial shows how to smoothly rotate the camera around a fixed point on the map, creating a cinematic orbiting effect. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Rotation Animation Use a Flutter `AnimationController` to continuously rotate the bearing around a point: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AnimateCameraScreen extends StatefulWidget { @override _AnimateCameraScreenState createState() => _AnimateCameraScreenState(); } class _AnimateCameraScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animationController; bool isRotating = false; double currentBearing = 0.0; final LatLng center = LatLng(40.7128, -74.0060); // New York @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 60), // Full rotation in 60 seconds ); _animationController.addListener(() { if (mapController != null && isRotating) { currentBearing = _animationController.value * 360; mapController?.moveCamera( CameraUpdate.newCameraPosition( CameraPosition( target: center, zoom: 15.0, bearing: currentBearing, tilt: 45.0, ), ), ); } }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Orbiting Camera')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: center, zoom: 15.0, tilt: 45.0, ), ), // Play/Stop button Positioned( bottom: 24, left: 0, right: 0, child: Center( child: FloatingActionButton.extended( onPressed: _toggleRotation, icon: Icon(isRotating ? Icons.stop : Icons.play_arrow), label: Text(isRotating ? 'Stop' : 'Orbit'), ), ), ), ], ), ); } void _toggleRotation() { setState(() { isRotating = !isRotating; }); if (isRotating) { _animationController.repeat(); } else { _animationController.stop(); } } @override void dispose() { _animationController.dispose(); mapController?.dispose(); super.dispose(); } } ``` ## Adjustable Speed and Tilt Let users control the orbit speed and tilt angle: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CustomOrbitScreen extends StatefulWidget { @override _CustomOrbitScreenState createState() => _CustomOrbitScreenState(); } class _CustomOrbitScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animationController; bool isRotating = false; double tilt = 45.0; double speed = 1.0; // rotations per minute final LatLng center = LatLng(48.8584, 2.2945); // Eiffel Tower @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 60), ); _animationController.addListener(_onAnimationTick); } void _onAnimationTick() { if (mapController != null && isRotating) { final bearing = (_animationController.value * 360 * speed) % 360; mapController?.moveCamera( CameraUpdate.newCameraPosition( CameraPosition( target: center, zoom: 16.0, bearing: bearing, tilt: tilt, ), ), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Custom Orbit')), body: Column( children: [ // Controls Container( padding: EdgeInsets.all(12), color: Colors.grey[100], child: Column( children: [ Row( children: [ SizedBox(width: 60, child: Text('Tilt: ${tilt.toInt()}°')), Expanded( child: Slider( value: tilt, min: 0, max: 60, onChanged: (value) => setState(() => tilt = value), ), ), ], ), Row( children: [ SizedBox(width: 60, child: Text('Speed: ${speed.toStringAsFixed(1)}x')), Expanded( child: Slider( value: speed, min: 0.2, max: 5.0, onChanged: (value) => setState(() => speed = value), ), ), ], ), ], ), ), // Map Expanded( child: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: center, zoom: 16.0, tilt: tilt, ), ), Positioned( bottom: 24, left: 0, right: 0, child: Center( child: FloatingActionButton.extended( onPressed: _toggleRotation, icon: Icon(isRotating ? Icons.stop : Icons.play_arrow), label: Text(isRotating ? 'Stop' : 'Start Orbit'), ), ), ), ], ), ), ], ), ); } void _toggleRotation() { setState(() { isRotating = !isRotating; }); if (isRotating) { _animationController.repeat(); } else { _animationController.stop(); } } @override void dispose() { _animationController.dispose(); mapController?.dispose(); super.dispose(); } } ``` ## Key Concepts | Concept | Details | |---------|---------| | `AnimationController` | Drives the continuous rotation loop | | `SingleTickerProviderStateMixin` | Required mixin for `AnimationController` | | `moveCamera` | Used instead of `animateCamera` for frame-by-frame updates | | `repeat()` | Makes the animation loop continuously | | `bearing` | Incremented each frame to create rotation | ## Next Steps - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Manual pitch/bearing control - [Fly to a Location](./flutter-fly-to-location) — Animated camera transitions - [Jump to Locations](./flutter-jump-to-locations) — Tour through multiple locations --- **Tip**: Use `moveCamera` (not `animateCamera`) inside the animation listener for smooth frame-by-frame updates. `animateCamera` adds its own easing which conflicts with the animation controller. --- # Animate a Marker in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-animate-marker # Animate a Marker in Flutter This tutorial shows how to smoothly animate a marker's position along a path or between waypoints. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Marker Animation Animate a marker along a predefined route using `AnimationController`: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AnimateMarkerScreen extends StatefulWidget { @override _AnimateMarkerScreenState createState() => _AnimateMarkerScreenState(); } class _AnimateMarkerScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animationController; LatLng currentPosition = LatLng(48.8584, 2.2945); bool isAnimating = false; // Route waypoints (Paris landmarks) final List route = [ LatLng(48.8584, 2.2945), // Eiffel Tower LatLng(48.8606, 2.3376), // Louvre LatLng(48.8530, 2.3499), // Notre-Dame LatLng(48.8867, 2.3431), // Sacré-Cœur LatLng(48.8738, 2.2950), // Arc de Triomphe LatLng(48.8584, 2.2945), // Back to Eiffel Tower ]; Set get markers => { Marker( markerId: MarkerId('moving'), position: currentPosition, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Moving Marker'), ), }; // Show the route as a polyline Set get polylines => { Polyline( polylineId: PolylineId('route'), points: route, color: Colors.blue.withOpacity(0.5), width: 3, patterns: [PatternItem.dash(10), PatternItem.gap(8)], ), }; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 15), // Total animation time ); _animationController.addListener(_updateMarkerPosition); _animationController.addStatusListener((status) { if (status == AnimationStatus.completed) { setState(() => isAnimating = false); } }); } void _updateMarkerPosition() { final progress = _animationController.value; final totalSegments = route.length - 1; final segmentProgress = progress * totalSegments; final segmentIndex = segmentProgress.floor().clamp(0, totalSegments - 1); final t = segmentProgress - segmentIndex; final from = route[segmentIndex]; final to = route[segmentIndex + 1]; setState(() { currentPosition = LatLng( from.latitude + (to.latitude - from.latitude) * t, from.longitude + (to.longitude - from.longitude) * t, ); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Animate Marker')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8620, 2.3200), zoom: 13.0, ), markers: markers, polylines: polylines, ), // Controls Positioned( bottom: 24, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FloatingActionButton.extended( heroTag: 'play', onPressed: isAnimating ? _stopAnimation : _startAnimation, icon: Icon(isAnimating ? Icons.stop : Icons.play_arrow), label: Text(isAnimating ? 'Stop' : 'Start'), ), SizedBox(width: 12), FloatingActionButton.small( heroTag: 'reset', onPressed: _resetAnimation, child: Icon(Icons.replay), ), ], ), ), ], ), ); } void _startAnimation() { setState(() => isAnimating = true); _animationController.forward(); } void _stopAnimation() { _animationController.stop(); setState(() => isAnimating = false); } void _resetAnimation() { _animationController.reset(); setState(() { isAnimating = false; currentPosition = route.first; }); } @override void dispose() { _animationController.dispose(); mapController?.dispose(); super.dispose(); } } ``` ## Looping Animation Make the marker loop continuously along the route: ```dart void _startLoopAnimation() { setState(() => isAnimating = true); _animationController.repeat(); // Loops forever } ``` ## Camera Follows Marker Keep the camera centered on the moving marker: ```dart void _updateMarkerPosition() { // ... calculate currentPosition as above ... setState(() { currentPosition = interpolatedPosition; }); // Camera follows the marker mapController?.moveCamera( CameraUpdate.newLatLng(currentPosition), ); } ``` ## Easing Curves Use different animation curves for natural movement: ```dart // In initState, wrap with a CurvedAnimation: final curvedAnimation = CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, // Smooth start and end ); curvedAnimation.addListener(() { final progress = curvedAnimation.value; // ... use progress for interpolation }); ``` | Curve | Effect | |-------|--------| | `Curves.linear` | Constant speed (default) | | `Curves.easeInOut` | Slow start and end, fast middle | | `Curves.easeIn` | Slow start, fast end | | `Curves.easeOut` | Fast start, slow end | | `Curves.bounceOut` | Bounce effect at the end | ## Next Steps - [Animate Camera Around Point](./flutter-animate-camera-around-point) — Orbiting camera animation - [Fly to a Location](./flutter-fly-to-location) — Animated camera transitions - [Markers and Annotations](./flutter-markers) — Static marker features --- **Tip**: For smooth marker animation, use `SingleTickerProviderStateMixin` and keep the `AnimationController` duration proportional to the route length. Shorter routes need shorter durations. --- # Animate a Point Along a Route in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-animate-point-along-route # Animate a Point Along a Route in Flutter This tutorial shows how to smoothly animate a marker moving along a defined route path — great for delivery tracking, ride-hailing, or tour animations. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Point Animation Along Route Move a marker along a European city route with Start/Stop/Reset controls: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AnimatePointAlongRouteScreen extends StatefulWidget { @override _AnimatePointAlongRouteScreenState createState() => _AnimatePointAlongRouteScreenState(); } class _AnimatePointAlongRouteScreenState extends State { MapMetricsController? mapController; Timer? animationTimer; int currentIndex = 0; bool isAnimating = false; // Route waypoints (European capitals) final List waypoints = [ LatLng(40.4168, -3.7038), // Madrid LatLng(48.8566, 2.3522), // Paris LatLng(51.5074, -0.1276), // London LatLng(52.52, 13.405), // Berlin LatLng(48.2082, 16.3738), // Vienna LatLng(41.9028, 12.4964), // Rome ]; // Interpolated points for smooth animation late List smoothRoute; @override void initState() { super.initState(); smoothRoute = _interpolateRoute(waypoints, 50); } /// Create smooth intermediate points between waypoints List _interpolateRoute(List points, int stepsPerSegment) { final result = []; for (int i = 0; i < points.length - 1; i++) { final from = points[i]; final to = points[i + 1]; for (int s = 0; s < stepsPerSegment; s++) { final t = s / stepsPerSegment; result.add(LatLng( from.latitude + (to.latitude - from.latitude) * t, from.longitude + (to.longitude - from.longitude) * t, )); } } result.add(points.last); return result; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Animate Point Along Route')), body: Column( children: [ // Controls Container( padding: EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton.icon( onPressed: isAnimating ? null : _startAnimation, icon: Icon(Icons.play_arrow), label: Text('Start'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, ), ), SizedBox(width: 8), ElevatedButton.icon( onPressed: isAnimating ? _stopAnimation : null, icon: Icon(Icons.stop), label: Text('Stop'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), ), SizedBox(width: 8), ElevatedButton.icon( onPressed: _resetAnimation, icon: Icon(Icons.replay), label: Text('Reset'), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey, foregroundColor: Colors.white, ), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 5.0), zoom: 4.0, ), onStyleLoaded: () { _addRouteLineLayer(); }, markers: { Marker( markerId: MarkerId('moving_point'), position: smoothRoute[currentIndex], icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Traveling...'), ), }, polylines: { Polyline( polylineId: PolylineId('route'), points: waypoints, color: Colors.blue.withOpacity(0.4), width: 3, ), }, ), ), ], ), ); } void _addRouteLineLayer() { // Add city markers at each waypoint final cityNames = [ 'Madrid', 'Paris', 'London', 'Berlin', 'Vienna', 'Rome' ]; for (int i = 0; i < waypoints.length; i++) { // City markers are added via the markers set in the widget } } void _startAnimation() { setState(() { isAnimating = true; }); animationTimer = Timer.periodic(Duration(milliseconds: 50), (timer) { if (currentIndex >= smoothRoute.length - 1) { _stopAnimation(); return; } setState(() { currentIndex++; }); // Optionally follow the marker with the camera mapController?.animateCamera( CameraUpdate.newLatLng(smoothRoute[currentIndex]), ); }); } void _stopAnimation() { animationTimer?.cancel(); setState(() { isAnimating = false; }); } void _resetAnimation() { _stopAnimation(); setState(() { currentIndex = 0; }); mapController?.animateCamera( CameraUpdate.newLatLngZoom(LatLng(48.0, 5.0), 4.0), ); } @override void dispose() { animationTimer?.cancel(); super.dispose(); } } ``` ## Delivery Tracker Example A practical example showing a delivery moving along a path with status updates: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DeliveryTrackerScreen extends StatefulWidget { @override _DeliveryTrackerScreenState createState() => _DeliveryTrackerScreenState(); } class _DeliveryTrackerScreenState extends State { MapMetricsController? mapController; Timer? animationTimer; int currentIndex = 0; bool isDelivering = false; // Delivery route through Paris streets final List deliveryRoute = [ LatLng(48.8738, 2.2950), // Arc de Triomphe (pickup) LatLng(48.8700, 2.3050), LatLng(48.8650, 2.3150), LatLng(48.8620, 2.3250), LatLng(48.8600, 2.3350), LatLng(48.8580, 2.3400), LatLng(48.8566, 2.3522), // City center LatLng(48.8550, 2.3550), LatLng(48.8530, 2.3499), // Notre-Dame (delivery) ]; String get statusText { final progress = currentIndex / (deliveryRoute.length - 1); if (progress == 0) return 'Ready for pickup'; if (progress < 0.3) return 'Picked up — on the way'; if (progress < 0.7) return 'In transit'; if (progress < 1.0) return 'Almost there!'; return 'Delivered!'; } @override Widget build(BuildContext context) { final progress = currentIndex / (deliveryRoute.length - 1); return Scaffold( appBar: AppBar(title: Text('Delivery Tracker')), body: Column( children: [ // Status bar Container( padding: EdgeInsets.all(16), color: Colors.blue[50], child: Column( children: [ Text(statusText, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 8), LinearProgressIndicator( value: progress, backgroundColor: Colors.grey[300], valueColor: AlwaysStoppedAnimation(Colors.blue), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8620, 2.3250), zoom: 14.0, ), markers: { // Pickup point Marker( markerId: MarkerId('pickup'), position: deliveryRoute.first, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueGreen), infoWindow: InfoWindow(title: 'Pickup'), ), // Delivery point Marker( markerId: MarkerId('delivery'), position: deliveryRoute.last, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'Delivery'), ), // Moving delivery marker Marker( markerId: MarkerId('driver'), position: deliveryRoute[currentIndex], icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Driver'), ), }, polylines: { Polyline( polylineId: PolylineId('delivery_route'), points: deliveryRoute, color: Colors.blue, width: 4, ), }, ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: isDelivering ? null : _startDelivery, backgroundColor: isDelivering ? Colors.grey : Colors.blue, child: Icon(isDelivering ? Icons.local_shipping : Icons.play_arrow), ), ); } void _startDelivery() { setState(() { isDelivering = true; currentIndex = 0; }); animationTimer = Timer.periodic(Duration(milliseconds: 500), (timer) { if (currentIndex >= deliveryRoute.length - 1) { timer.cancel(); setState(() { isDelivering = false; }); return; } setState(() { currentIndex++; }); }); } @override void dispose() { animationTimer?.cancel(); super.dispose(); } } ``` ## Animation Tips | Approach | Speed | Smoothness | Best For | |----------|-------|------------|----------| | `Timer.periodic` 50ms | Fast | Very smooth | Visual demos | | `Timer.periodic` 200ms | Medium | Smooth | Tracking UIs | | `Timer.periodic` 500ms | Slow | Step-by-step | Delivery tracking | | Interpolate waypoints | — | Extra smooth | Long routes with few waypoints | ## Next Steps - [Animate a Marker](./flutter-animate-marker) — Bounce and pulse animations - [Animate a Line](./flutter-animate-a-line) — Draw a line progressively - [Fly to a Location](./flutter-fly-to-location) — Smooth camera transitions --- **Tip**: For production tracking apps, receive real GPS coordinates from a backend and update the marker position — the same `setState` pattern works with live data from a WebSocket or polling API. --- # Animate a Point in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-animate-point # Animate a Point in Flutter This tutorial shows how to animate a point (marker) bouncing, pulsing, or moving on the map using Flutter's built-in animation system. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Bouncing Marker Animate a marker that bounces up and down continuously: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BouncingMarkerScreen extends StatefulWidget { @override _BouncingMarkerScreenState createState() => _BouncingMarkerScreenState(); } class _BouncingMarkerScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animController; late Animation _bounceAnimation; final LatLng markerPosition = LatLng(48.8584, 2.2945); // Eiffel Tower @override void initState() { super.initState(); _animController = AnimationController( duration: Duration(milliseconds: 800), vsync: this, )..repeat(reverse: true); _bounceAnimation = Tween(begin: 0.0, end: 0.003).animate( CurvedAnimation(parent: _animController, curve: Curves.easeInOut), ); _animController.addListener(() { setState(() {}); }); } @override Widget build(BuildContext context) { // Offset the latitude slightly to simulate bouncing final animatedPosition = LatLng( markerPosition.latitude + _bounceAnimation.value, markerPosition.longitude, ); return Scaffold( appBar: AppBar(title: Text('Bouncing Marker')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: markerPosition, zoom: 15.0, ), markers: { Marker( markerId: MarkerId('bouncing'), position: animatedPosition, infoWindow: InfoWindow(title: 'Eiffel Tower'), ), }, ), ); } @override void dispose() { _animController.dispose(); super.dispose(); } } ``` ## Pulsing Circle Show a pulsing circle that grows and fades around a location: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PulsingCircleScreen extends StatefulWidget { @override _PulsingCircleScreenState createState() => _PulsingCircleScreenState(); } class _PulsingCircleScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animController; late Animation _radiusAnimation; late Animation _opacityAnimation; final LatLng center = LatLng(48.8566, 2.3522); // Paris center @override void initState() { super.initState(); _animController = AnimationController( duration: Duration(seconds: 2), vsync: this, )..repeat(); _radiusAnimation = Tween(begin: 100.0, end: 500.0).animate( CurvedAnimation(parent: _animController, curve: Curves.easeOut), ); _opacityAnimation = Tween(begin: 0.4, end: 0.0).animate( CurvedAnimation(parent: _animController, curve: Curves.easeOut), ); _animController.addListener(() { setState(() {}); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Pulsing Circle')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: center, zoom: 14.0, ), circles: { // Pulsing circle Circle( circleId: CircleId('pulse'), center: center, radius: _radiusAnimation.value, fillColor: Colors.blue.withOpacity(_opacityAnimation.value), strokeColor: Colors.blue.withOpacity(_opacityAnimation.value), strokeWidth: 2, ), // Static center dot Circle( circleId: CircleId('center_dot'), center: center, radius: 30, fillColor: Colors.blue.withOpacity(0.8), strokeColor: Colors.white, strokeWidth: 3, ), }, markers: { Marker( markerId: MarkerId('center'), position: center, infoWindow: InfoWindow(title: 'Your Location'), ), }, ), ); } @override void dispose() { _animController.dispose(); super.dispose(); } } ``` ## Moving Point Between Locations Smoothly move a point from one location to another: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MovingPointScreen extends StatefulWidget { @override _MovingPointScreenState createState() => _MovingPointScreenState(); } class _MovingPointScreenState extends State with SingleTickerProviderStateMixin { MapMetricsController? mapController; late AnimationController _animController; late Animation _latAnimation; late Animation _lngAnimation; final List> destinations = [ {'name': 'Paris', 'lat': 48.8566, 'lng': 2.3522}, {'name': 'London', 'lat': 51.5074, 'lng': -0.1276}, {'name': 'Berlin', 'lat': 52.52, 'lng': 13.405}, {'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964}, ]; int currentDestination = 0; @override void initState() { super.initState(); _animController = AnimationController( duration: Duration(seconds: 2), vsync: this, ); _setAnimationToNextDestination(); _animController.addListener(() { setState(() {}); }); _animController.addStatusListener((status) { if (status == AnimationStatus.completed) { // Move to next destination setState(() { currentDestination = (currentDestination + 1) % destinations.length; }); _setAnimationToNextDestination(); _animController.forward(from: 0.0); } }); _animController.forward(); } void _setAnimationToNextDestination() { final from = destinations[currentDestination]; final to = destinations[(currentDestination + 1) % destinations.length]; _latAnimation = Tween( begin: from['lat'], end: to['lat'], ).animate(CurvedAnimation( parent: _animController, curve: Curves.easeInOut, )); _lngAnimation = Tween( begin: from['lng'], end: to['lng'], ).animate(CurvedAnimation( parent: _animController, curve: Curves.easeInOut, )); } @override Widget build(BuildContext context) { final currentPosition = LatLng( _latAnimation.value, _lngAnimation.value, ); final destName = destinations[ (currentDestination + 1) % destinations.length]['name']; return Scaffold( appBar: AppBar(title: Text('Moving Point')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), markers: { // Destination markers ...destinations.map((d) => Marker( markerId: MarkerId(d['name']), position: LatLng(d['lat'], d['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueOrange), infoWindow: InfoWindow(title: d['name']), )), // Moving point Marker( markerId: MarkerId('moving'), position: currentPosition, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), ), }, ), // Status bar Positioned( top: 16, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Text( 'Moving to $destName...', textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.bold), ), ), ), ), ], ), ); } @override void dispose() { _animController.dispose(); super.dispose(); } } ``` ## Animation Options | Type | Use Case | Flutter Class | |------|----------|---------------| | Bounce | Draw attention to a marker | `AnimationController` + `Curves.easeInOut` | | Pulse | Show live location or alerts | `AnimationController` + `repeat()` | | Move | Transition between locations | `Tween` for lat/lng | | Rotate | Compass or direction indicator | `Tween` for bearing | ## Next Steps - [Animate Point Along Route](./flutter-animate-point-along-route) — Move along a path - [Animate a Marker](./flutter-animate-marker) — More marker animations - [Animate a Line](./flutter-animate-a-line) — Progressive line drawing --- **Tip**: Use `SingleTickerProviderStateMixin` for a single animation or `TickerProviderStateMixin` if your screen has multiple independent animations running simultaneously. --- # Arc Layer — Flight Routes in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-arc-layer # Arc Layer — Flight Routes in Flutter This tutorial shows how to draw curved arc lines between locations — perfect for visualizing flight paths, connections between cities, or network maps. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Flight Arc Draw a curved arc between two cities by computing intermediate points on a great circle: ```dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FlightArcScreen extends StatefulWidget { @override _FlightArcScreenState createState() => _FlightArcScreenState(); } class _FlightArcScreenState extends State { MapMetricsController? mapController; /// Generate arc points between two locations List _generateArc(LatLng from, LatLng to, {int segments = 50}) { final points = []; for (int i = 0; i <= segments; i++) { final t = i / segments; // Linear interpolation of lat/lng final lat = from.latitude + (to.latitude - from.latitude) * t; final lng = from.longitude + (to.longitude - from.longitude) * t; // Add altitude curve (parabolic) final altFactor = sin(t * pi) * 2.0; final curvedLat = lat + altFactor * (to.longitude - from.longitude) * 0.05; points.add(LatLng(curvedLat, lng)); } return points; } @override Widget build(BuildContext context) { final parisToNY = _generateArc( LatLng(48.8566, 2.3522), // Paris LatLng(40.7128, -74.0060), // New York ); return Scaffold( appBar: AppBar(title: Text('Flight Route')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(50.0, -35.0), zoom: 2.5, ), polylines: { Polyline( polylineId: PolylineId('flight_paris_ny'), points: parisToNY, color: Colors.blue, width: 3, ), }, markers: { Marker( markerId: MarkerId('paris'), position: LatLng(48.8566, 2.3522), infoWindow: InfoWindow(title: 'Paris (CDG)'), ), Marker( markerId: MarkerId('new_york'), position: LatLng(40.7128, -74.0060), infoWindow: InfoWindow(title: 'New York (JFK)'), ), }, ), ); } } ``` ## Multi-Route Flight Network Display a hub-and-spoke flight network from a single airport: ```dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FlightNetworkScreen extends StatefulWidget { @override _FlightNetworkScreenState createState() => _FlightNetworkScreenState(); } class _FlightNetworkScreenState extends State { MapMetricsController? mapController; final LatLng hub = LatLng(48.8566, 2.3522); // Paris hub final List> destinations = [ {'name': 'New York', 'lat': 40.7128, 'lng': -74.0060, 'color': Colors.blue}, {'name': 'Tokyo', 'lat': 35.6762, 'lng': 139.6503, 'color': Colors.red}, {'name': 'Dubai', 'lat': 25.2048, 'lng': 55.2708, 'color': Colors.orange}, {'name': 'Sao Paulo', 'lat': -23.5505, 'lng': -46.6333, 'color': Colors.green}, {'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'color': Colors.purple}, {'name': 'Singapore', 'lat': 1.3521, 'lng': 103.8198, 'color': Colors.teal}, {'name': 'Cairo', 'lat': 30.0444, 'lng': 31.2357, 'color': Colors.amber}, ]; List _generateArc(LatLng from, LatLng to, {int segments = 60}) { final points = []; for (int i = 0; i <= segments; i++) { final t = i / segments; final lat = from.latitude + (to.latitude - from.latitude) * t; final lng = from.longitude + (to.longitude - from.longitude) * t; // Calculate distance for arc height final dist = sqrt(pow(to.latitude - from.latitude, 2) + pow(to.longitude - from.longitude, 2)); final altFactor = sin(t * pi) * dist * 0.15; points.add(LatLng(lat + altFactor, lng)); } return points; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Flight Network')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(30.0, 20.0), zoom: 1.8, ), polylines: destinations.map((dest) { final to = LatLng(dest['lat'], dest['lng']); return Polyline( polylineId: PolylineId('flight_${dest['name']}'), points: _generateArc(hub, to), color: (dest['color'] as Color).withOpacity(0.7), width: 2, ); }).toSet(), markers: { // Hub marker Marker( markerId: MarkerId('hub'), position: hub, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'Paris (Hub)'), ), // Destination markers ...destinations.map((dest) => Marker( markerId: MarkerId(dest['name']), position: LatLng(dest['lat'], dest['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: dest['name']), )), }, ), // Flight count badge Positioned( top: 16, right: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(20), ), child: Text( '${destinations.length} routes from Paris', style: TextStyle(color: Colors.white, fontSize: 13), ), ), ), ], ), ); } } ``` ## Animated Flight Path Animate a plane icon moving along a flight arc: ```dart import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AnimatedFlightScreen extends StatefulWidget { @override _AnimatedFlightScreenState createState() => _AnimatedFlightScreenState(); } class _AnimatedFlightScreenState extends State { MapMetricsController? mapController; Timer? flightTimer; int currentIndex = 0; bool isFlying = false; late List flightPath; @override void initState() { super.initState(); flightPath = _generateArc( LatLng(48.8566, 2.3522), // Paris LatLng(40.7128, -74.0060), // New York segments: 200, ); } List _generateArc(LatLng from, LatLng to, {int segments = 100}) { final points = []; for (int i = 0; i <= segments; i++) { final t = i / segments; final lat = from.latitude + (to.latitude - from.latitude) * t; final lng = from.longitude + (to.longitude - from.longitude) * t; final dist = sqrt(pow(to.latitude - from.latitude, 2) + pow(to.longitude - from.longitude, 2)); final alt = sin(t * pi) * dist * 0.15; points.add(LatLng(lat + alt, lng)); } return points; } @override Widget build(BuildContext context) { final progress = flightPath.isEmpty ? 0.0 : currentIndex / (flightPath.length - 1); return Scaffold( appBar: AppBar(title: Text('Animated Flight')), body: Column( children: [ // Flight info bar Container( padding: EdgeInsets.all(12), color: Colors.blue[50], child: Row( children: [ Text('CDG', style: TextStyle(fontWeight: FontWeight.bold)), Expanded( child: Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: LinearProgressIndicator(value: progress), ), ), Text('JFK', style: TextStyle(fontWeight: FontWeight.bold)), ], ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(50.0, -35.0), zoom: 2.5, ), polylines: { // Full route (faded) Polyline( polylineId: PolylineId('full_route'), points: flightPath, color: Colors.blue.withOpacity(0.3), width: 2, ), // Traveled portion if (currentIndex > 0) Polyline( polylineId: PolylineId('traveled'), points: flightPath.sublist(0, currentIndex + 1), color: Colors.blue, width: 3, ), }, markers: { Marker( markerId: MarkerId('paris'), position: flightPath.first, infoWindow: InfoWindow(title: 'Paris (CDG)'), ), Marker( markerId: MarkerId('new_york'), position: flightPath.last, infoWindow: InfoWindow(title: 'New York (JFK)'), ), Marker( markerId: MarkerId('plane'), position: flightPath[currentIndex], icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueAzure), ), }, ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: isFlying ? _stopFlight : _startFlight, child: Icon(isFlying ? Icons.pause : Icons.flight_takeoff), ), ); } void _startFlight() { setState(() { isFlying = true; if (currentIndex >= flightPath.length - 1) currentIndex = 0; }); flightTimer = Timer.periodic(Duration(milliseconds: 30), (_) { if (currentIndex >= flightPath.length - 1) { _stopFlight(); return; } setState(() => currentIndex++); }); } void _stopFlight() { flightTimer?.cancel(); setState(() => isFlying = false); } @override void dispose() { flightTimer?.cancel(); super.dispose(); } } ``` ## Next Steps - [Gradient Line](./flutter-gradient-line) — Color arcs by distance - [Animate Point Along Route](./flutter-animate-point-along-route) — Moving markers - [Multiple Geometries](./flutter-multiple-geometries) — Combine lines and markers --- **Tip**: For realistic great-circle arcs on a flat map, increase the `segments` count for long-distance routes. The parabolic altitude offset (`sin(t * pi)`) creates the visual curve — adjust the multiplier to control arc height. --- # Basic Map with Flutter and MapMetrics https://docs.mapatlas.xyz/sdk/examples/flutter-basic-map # Basic Map with Flutter and MapMetrics This tutorial will show you how to create a basic interactive map using Flutter and MapMetrics Atlas API. ## Basic Map Implementation Here's a complete example of a basic map with common interactions: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BasicMapScreen extends StatefulWidget { @override _BasicMapScreenState createState() => _BasicMapScreenState(); } class _BasicMapScreenState extends State { MapMetricsController? mapController; LatLng? lastTappedLocation; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Basic MapMetrics Map'), actions: [ IconButton( icon: Icon(Icons.my_location), onPressed: _goToUserLocation, ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { setState(() { mapController = controller; }); }, onMapClick: (Point point, LatLng coordinates) { setState(() { lastTappedLocation = coordinates; }); _showLocationInfo(coordinates); }, onStyleLoaded: () { print('Map style loaded successfully!'); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), // New York City zoom: 10.0, ), myLocationEnabled: true, myLocationTrackingMode: MyLocationTrackingMode.Tracking, myLocationRenderMode: MyLocationRenderMode.COMPASS, ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( heroTag: "zoomIn", onPressed: _zoomIn, child: Icon(Icons.add), ), SizedBox(height: 8), FloatingActionButton( heroTag: "zoomOut", onPressed: _zoomOut, child: Icon(Icons.remove), ), ], ), ); } void _zoomIn() { mapController?.animateCamera( CameraUpdate.zoomIn(), ); } void _zoomOut() { mapController?.animateCamera( CameraUpdate.zoomOut(), ); } void _goToUserLocation() { mapController?.animateCamera( CameraUpdate.zoomTo(15.0), ); } void _showLocationInfo(LatLng coordinates) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Location Information'), content: Text( 'Latitude: ${coordinates.latitude.toStringAsFixed(6)}\n' 'Longitude: ${coordinates.longitude.toStringAsFixed(6)}', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('OK'), ), ], ); }, ); } } ``` ## Map Configuration Options ### Camera Position Control the initial view of your map: ```dart initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), // Center point zoom: 10.0, // Zoom level (0-22) bearing: 0.0, // Rotation in degrees tilt: 0.0, // Tilt in degrees ), ``` ### User Location Enable user location tracking: ```dart myLocationEnabled: true, myLocationTrackingMode: MyLocationTrackingMode.Tracking, myLocationRenderMode: MyLocationRenderMode.COMPASS, ``` ### Map Interactions Handle various map events: ```dart onMapCreated: (MapMetricsController controller) { // Called when map is created }, onMapClick: (Point point, LatLng coordinates) { // Called when user taps the map }, onMapLongClick: (Point point, LatLng coordinates) { // Called when user long-presses the map }, onStyleLoaded: () { // Called when map style is loaded }, onCameraIdle: () { // Called when camera stops moving }, ``` ## Camera Controls ### Programmatic Camera Movement ```dart // Zoom to specific level mapController?.animateCamera(CameraUpdate.zoomTo(15.0)); // Zoom in/out mapController?.animateCamera(CameraUpdate.zoomIn()); mapController?.animateCamera(CameraUpdate.zoomOut()); // Move to specific location mapController?.animateCamera( CameraUpdate.newLatLng(LatLng(37.7749, -122.4194)), ); // Move with zoom mapController?.animateCamera( CameraUpdate.newLatLngZoom(LatLng(37.7749, -122.4194), 15.0), ); // Fit bounds mapController?.animateCamera( CameraUpdate.newLatLngBounds( LatLngBounds( southwest: LatLng(37.7, -122.5), northeast: LatLng(37.8, -122.4), ), 50.0, // padding ), ); ``` ### Camera Position Listeners ```dart onCameraMove: (CameraPosition position) { print('Camera moving: ${position.zoom}'); }, onCameraIdle: () { print('Camera stopped moving'); }, ``` ## Map State Management ### Get Current Camera Position ```dart void _getCurrentPosition() async { CameraPosition? position = await mapController?.getCameraPosition(); if (position != null) { print('Current zoom: ${position.zoom}'); print('Current center: ${position.target}'); } } ``` ### Check Map State ```dart void _checkMapState() async { bool? isMapReady = await mapController?.isMapReady(); if (isMapReady == true) { print('Map is ready for interactions'); } } ``` ## Error Handling Add error handling for better user experience: ```dart MapMetrics( styleUrl: 'your_style_url', onMapCreated: (controller) { mapController = controller; }, onMapClick: (point, coordinates) { // Handle clicks }, onError: (String error) { print('Map error: $error'); // Show error message to user }, onStyleLoaded: () { print('Style loaded successfully'); }, ) ``` ## Performance Tips 1. **Reuse Controller**: Store the `MapMetricsController` in a variable to avoid recreating it 2. **Debounce Events**: Use debouncing for frequent events like `onCameraMove` 3. **Lazy Loading**: Load map data only when needed 4. **Memory Management**: Dispose of controllers when not needed ```dart @override void dispose() { mapController?.dispose(); super.dispose(); } ``` ## Custom Map Controls Create custom floating action buttons for map controls: ```dart Widget _buildMapControls() { return Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton.small( heroTag: "zoomIn", onPressed: () => mapController?.animateCamera(CameraUpdate.zoomIn()), child: Icon(Icons.add), ), SizedBox(height: 8), FloatingActionButton.small( heroTag: "zoomOut", onPressed: () => mapController?.animateCamera(CameraUpdate.zoomOut()), child: Icon(Icons.remove), ), SizedBox(height: 8), FloatingActionButton.small( heroTag: "location", onPressed: _goToUserLocation, child: Icon(Icons.my_location), ), ], ); } ``` ## Next Steps Now that you have a basic map working, try: - [Adding Markers and Annotations](./flutter-markers) - [Custom Map Styling](./flutter-custom-styling) - [Handling Map Interactions](./flutter-interactions) --- **Pro Tip**: Use the MapMetrics Portal to create custom map styles that match your app's design. You can customize colors, fonts, and which map features are displayed. --- # Change Building Color Based on Zoom Level in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-building-color-zoom # Change Building Color Based on Zoom Level in Flutter This tutorial shows how to dynamically change the color of 3D buildings as the user zooms in and out — useful for data visualization, theming, or emphasizing detail at different scales. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Zoom-Dependent Building Colors Change 3D building colors as the user zooms in: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BuildingColorZoomScreen extends StatefulWidget { @override _BuildingColorZoomScreenState createState() => _BuildingColorZoomScreenState(); } class _BuildingColorZoomScreenState extends State { MapMetricsController? mapController; double currentZoom = 15.0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Building Color by Zoom')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8606, 2.3376), // Louvre area zoom: 15.0, tilt: 55.0, bearing: 30.0, ), onStyleLoaded: () { _add3DBuildings(); }, onCameraMove: (CameraPosition position) { final newZoom = position.zoom; // Update color when zoom changes significantly if ((newZoom - currentZoom).abs() > 0.5) { setState(() { currentZoom = newZoom; }); _updateBuildingColor(newZoom); } }, onCameraIdle: () async { final pos = await mapController?.getCameraPosition(); if (pos != null) { setState(() => currentZoom = pos.zoom); _updateBuildingColor(pos.zoom); } }, ), // Zoom level indicator Positioned( top: 16, left: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: _getColorForZoom(currentZoom), borderRadius: BorderRadius.circular(20), ), child: Text( 'Zoom: ${currentZoom.toStringAsFixed(1)}', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ), // Color legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Zoom Levels', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12)), SizedBox(height: 4), _zoomRow(Colors.grey, '< 14 (far)'), _zoomRow(Colors.blue, '14-15 (district)'), _zoomRow(Colors.teal, '15-16 (neighborhood)'), _zoomRow(Colors.orange, '16-17 (block)'), _zoomRow(Colors.deepOrange, '> 17 (building)'), ], ), ), ), ), ], ), ); } Widget _zoomRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container(width: 14, height: 14, color: color), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 11)), ], ), ); } Color _getColorForZoom(double zoom) { if (zoom < 14) return Colors.grey; if (zoom < 15) return Colors.blue; if (zoom < 16) return Colors.teal; if (zoom < 17) return Colors.orange; return Colors.deepOrange; } String _getHexForZoom(double zoom) { if (zoom < 14) return '#9e9e9e'; if (zoom < 15) return '#2196f3'; if (zoom < 16) return '#009688'; if (zoom < 17) return '#ff9800'; return '#ff5722'; } void _add3DBuildings() { mapController?.addFillExtrusionLayer( '3d-buildings', 'composite', sourceLayer: 'building', fillExtrusionColor: _getHexForZoom(currentZoom), fillExtrusionOpacity: 0.7, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], minZoom: 13.0, ); } void _updateBuildingColor(double zoom) { final hexColor = _getHexForZoom(zoom); mapController?.setPaintProperty( '3d-buildings', 'fill-extrusion-color', hexColor, ); } } ``` ## Theme-Based Building Colors Let users switch between color themes for buildings: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BuildingThemeScreen extends StatefulWidget { @override _BuildingThemeScreenState createState() => _BuildingThemeScreenState(); } class _BuildingThemeScreenState extends State { MapMetricsController? mapController; String activeTheme = 'default'; final Map> themes = { 'default': {'color': '#aaaaaa', 'name': 'Default'}, 'night': {'color': '#1a237e', 'name': 'Night Mode'}, 'warm': {'color': '#e65100', 'name': 'Warm Sunset'}, 'forest': {'color': '#1b5e20', 'name': 'Forest'}, 'ice': {'color': '#b3e5fc', 'name': 'Ice Blue'}, }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Building Themes')), body: Column( children: [ Container( padding: EdgeInsets.all(8), child: Wrap( spacing: 6, children: themes.entries.map((entry) { final hexColor = entry.value['color']!; final color = Color( int.parse(hexColor.replaceFirst('#', '0xFF'))); return ChoiceChip( avatar: Container( width: 16, height: 16, decoration: BoxDecoration( color: color, shape: BoxShape.circle, ), ), label: Text(entry.value['name']!), selected: activeTheme == entry.key, onSelected: (selected) { if (selected) { setState(() => activeTheme = entry.key); mapController?.setPaintProperty( '3d-buildings', 'fill-extrusion-color', hexColor, ); } }, ); }).toList(), ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8606, 2.3376), zoom: 16.0, tilt: 55.0, bearing: -20.0, ), onStyleLoaded: () { mapController?.addFillExtrusionLayer( '3d-buildings', 'composite', sourceLayer: 'building', fillExtrusionColor: themes[activeTheme]!['color']!, fillExtrusionOpacity: 0.7, fillExtrusionHeight: ['get', 'height'], fillExtrusionBase: ['get', 'min_height'], minZoom: 13.0, ); }, ), ), ], ), ); } } ``` ## Next Steps - [3D Buildings](./flutter-3d-buildings) — Basic 3D building setup - [Change Layer Color](./flutter-change-layer-color) — Dynamic layer color changes - [Custom Map Styling](./flutter-custom-styling) — Full style customization --- **Tip**: Use `onCameraIdle` instead of `onCameraMove` for paint property updates to avoid excessive calls during zoom animations. The idle callback fires only after the camera stops moving. --- # Change the Case of Labels in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-change-label-case # Change the Case of Labels in Flutter This tutorial shows how to change the text case of map labels — converting to uppercase, lowercase, or title case at runtime. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Change Label Text Transform Use the `text-transform` layout property to change label casing: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LabelCaseScreen extends StatefulWidget { @override _LabelCaseScreenState createState() => _LabelCaseScreenState(); } class _LabelCaseScreenState extends State { MapMetricsController? mapController; String currentCase = 'none'; final List> caseOptions = [ {'value': 'none', 'label': 'Default'}, {'value': 'uppercase', 'label': 'UPPERCASE'}, {'value': 'lowercase', 'label': 'lowercase'}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Label Case')), body: Column( children: [ // Case selector Container( padding: EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: caseOptions.map((option) { return ChoiceChip( label: Text(option['label']!), selected: currentCase == option['value'], onSelected: (selected) { if (selected) { setState(() => currentCase = option['value']!); _applyTextTransform(option['value']!); } }, ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), ), ], ), ); } void _applyTextTransform(String textCase) { // Apply to common label layers in the style final labelLayers = [ 'place-city-label', 'place-town-label', 'road-label', 'poi-label', ]; for (final layer in labelLayers) { mapController?.setLayoutProperty(layer, 'text-transform', textCase); } } } ``` ## Custom Label Styling Combine text case changes with font size and color adjustments: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LabelStyleScreen extends StatefulWidget { @override _LabelStyleScreenState createState() => _LabelStyleScreenState(); } class _LabelStyleScreenState extends State { MapMetricsController? mapController; String activePreset = 'default'; final Map> presets = { 'default': { 'name': 'Default', 'textTransform': 'none', 'textSize': 1.0, 'textColor': '#333333', }, 'bold_caps': { 'name': 'Bold Caps', 'textTransform': 'uppercase', 'textSize': 1.2, 'textColor': '#1a1a1a', }, 'subtle': { 'name': 'Subtle', 'textTransform': 'lowercase', 'textSize': 0.8, 'textColor': '#999999', }, 'highlight': { 'name': 'Highlight', 'textTransform': 'uppercase', 'textSize': 1.1, 'textColor': '#1565c0', }, }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Label Styling')), body: Column( children: [ Container( padding: EdgeInsets.all(8), child: Wrap( spacing: 8, children: presets.entries.map((entry) { return ChoiceChip( label: Text(entry.value['name']), selected: activePreset == entry.key, onSelected: (selected) { if (selected) { setState(() => activePreset = entry.key); _applyPreset(entry.value); } }, ); }).toList(), ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), ), ), ], ), ); } void _applyPreset(Map preset) { final layers = ['place-city-label', 'place-town-label', 'road-label']; for (final layer in layers) { mapController?.setLayoutProperty( layer, 'text-transform', preset['textTransform'], ); mapController?.setPaintProperty( layer, 'text-color', preset['textColor'], ); } } } ``` ## Text Transform Options | Value | Input | Output | |-------|-------|--------| | `none` | Paris | Paris | | `uppercase` | Paris | PARIS | | `lowercase` | Paris | paris | ## Next Steps - [Custom Map Styling](./flutter-custom-styling) — Full style customization - [Change Layer Color](./flutter-change-layer-color) — Dynamic color changes - [Building Color by Zoom](./flutter-building-color-zoom) — Zoom-dependent styling --- **Tip**: Uppercase labels look great on maps at low zoom levels (country/state names), while default casing is better at street level where readability matters more. --- # Change a Layer's Color with Buttons in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-change-layer-color # Change a Layer's Color with Buttons in Flutter This tutorial shows how to dynamically change the color of a map layer at runtime using buttons — no need to reload the map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Change Fill Layer Color Add a polygon and change its color by tapping buttons: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ChangeLayerColorScreen extends StatefulWidget { @override _ChangeLayerColorScreenState createState() => _ChangeLayerColorScreenState(); } class _ChangeLayerColorScreenState extends State { MapMetricsController? mapController; String currentColor = '#3b82f6'; final List> colorOptions = [ {'name': 'Blue', 'hex': '#3b82f6', 'color': Colors.blue}, {'name': 'Red', 'hex': '#ef4444', 'color': Colors.red}, {'name': 'Green', 'hex': '#22c55e', 'color': Colors.green}, {'name': 'Purple', 'hex': '#8b5cf6', 'color': Colors.purple}, {'name': 'Orange', 'hex': '#f59e0b', 'color': Colors.orange}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Change Layer Color')), body: Column( children: [ // Color buttons Container( padding: EdgeInsets.all(12), child: Wrap( spacing: 8, children: colorOptions.map((option) { return ElevatedButton( onPressed: () => _changeColor(option['hex']), style: ElevatedButton.styleFrom( backgroundColor: option['color'], foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text(option['name']), ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 4.0, ), onStyleLoaded: () { _addRegionLayer(); }, ), ), ], ), ); } void _addRegionLayer() { final geoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [-1.75, 43.3], [3.0, 43.3], [7.7, 43.8], [8.2, 48.9], [2.5, 51.1], [-4.8, 48.5], [-1.75, 43.3], ] ], }, }; mapController?.addGeoJsonSource('region', geoJson); mapController?.addFillLayer( 'region-fill', 'region', fillColor: currentColor, fillOpacity: 0.4, ); mapController?.addLineLayer( 'region-outline', 'region', lineColor: currentColor, lineWidth: 2.0, ); } void _changeColor(String hexColor) { setState(() { currentColor = hexColor; }); // Update the fill layer color mapController?.setPaintProperty('region-fill', 'fill-color', hexColor); // Update the outline color too mapController?.setPaintProperty('region-outline', 'line-color', hexColor); } } ``` ## Change Line Layer Color Change the color of a route line dynamically: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ChangeLineColorScreen extends StatefulWidget { @override _ChangeLineColorScreenState createState() => _ChangeLineColorScreenState(); } class _ChangeLineColorScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Change Line Color')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 5.0), zoom: 4.0, ), onStyleLoaded: () { _addRouteLine(); }, ), // Floating color picker Positioned( bottom: 24, left: 16, right: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 6)], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Route Color', style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _colorCircle(Colors.blue, '#3b82f6'), _colorCircle(Colors.red, '#ef4444'), _colorCircle(Colors.green, '#22c55e'), _colorCircle(Colors.purple, '#8b5cf6'), _colorCircle(Colors.orange, '#f59e0b'), _colorCircle(Colors.teal, '#14b8a6'), ], ), ], ), ), ), ], ), ); } Widget _colorCircle(Color color, String hex) { return GestureDetector( onTap: () { mapController?.setPaintProperty('route-line', 'line-color', hex); }, child: Container( width: 36, height: 36, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 2), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 2)], ), ), ); } void _addRouteLine() { final geoJson = { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': [ [-3.7038, 40.4168], // Madrid [2.349902, 48.853], // Paris [13.405, 52.52], // Berlin [16.3738, 48.2082], // Vienna [12.4964, 41.9028], // Rome ], }, }; mapController?.addGeoJsonSource('route', geoJson); mapController?.addLineLayer( 'route-line', 'route', lineColor: '#3b82f6', lineWidth: 4.0, lineJoin: 'round', lineCap: 'round', ); } } ``` ## Key Method | Method | Description | |--------|-------------| | `setPaintProperty(layerId, property, value)` | Update any paint property of a layer at runtime | Common paint properties: | Layer Type | Property | Values | |------------|----------|--------| | Fill | `fill-color` | Hex color string | | Fill | `fill-opacity` | `0.0` to `1.0` | | Line | `line-color` | Hex color string | | Line | `line-width` | Number (pixels) | | Circle | `circle-color` | Hex color string | | Circle | `circle-radius` | Number (pixels) | ## Next Steps - [Custom Map Styling](./flutter-custom-styling) — Apply full custom styles - [Add a GeoJSON Polygon](./flutter-add-geojson-polygon) — Draw filled areas - [Toggle Interactions](./flutter-toggle-interactions) — Control map behavior --- **Tip**: Use `setPaintProperty` for real-time theming — for example, change all layer colors at once when the user switches between light and dark mode. --- # Custom Map Styling with Flutter and MapMetrics https://docs.mapatlas.xyz/sdk/examples/flutter-custom-styling # Custom Map Styling with Flutter and MapMetrics This tutorial will show you how to create custom map styles and integrate them with your Flutter MapMetrics applications. ## Using MapMetrics Portal for Custom Styles The MapMetrics Portal provides an intuitive interface for creating custom map styles that work seamlessly with Flutter applications. ### Step 1: Create a Custom Style 1. **Visit MapMetrics Portal**: Go to [portal.mapmetrics.org](https://portal.mapmetrics.org) 2. **Navigate to Styles**: Click on the "Styles" section 3. **Create New Style**: Click "New Style" and choose a template 4. **Customize Your Style**: Use the visual editor to modify: - Colors and themes - Fonts and typography - Map features (roads, buildings, water, etc.) - Icons and symbols 5. **Save and Get URL**: Save your style and copy the style URL ### Step 2: Use Custom Style in Flutter ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CustomStyledMapScreen extends StatefulWidget { @override _CustomStyledMapScreenState createState() => _CustomStyledMapScreenState(); } class _CustomStyledMapScreenState extends State { MapMetricsController? mapController; String currentStyleUrl = ''; // Different style URLs from MapMetrics Portal final Map styleOptions = { 'Dark Theme': 'https://gateway.mapmetrics.org/styles/YOUR_DARK_STYLE_ID?token=YOUR_API_KEY', 'Light Theme': 'https://gateway.mapmetrics.org/styles/YOUR_LIGHT_STYLE_ID?token=YOUR_API_KEY', 'Satellite': 'https://gateway.mapmetrics.org/styles/YOUR_SATELLITE_STYLE_ID?token=YOUR_API_KEY', 'Custom Brand': 'https://gateway.mapmetrics.org/styles/YOUR_CUSTOM_STYLE_ID?token=YOUR_API_KEY', }; @override void initState() { super.initState(); currentStyleUrl = styleOptions.values.first; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Custom Styled Map'), actions: [ PopupMenuButton( onSelected: _changeStyle, itemBuilder: (context) => styleOptions.keys.map((String key) { return PopupMenuItem( value: key, child: Text(key), ); }).toList(), child: Padding( padding: EdgeInsets.all(16.0), child: Icon(Icons.style), ), ), ], ), body: MapMetrics( styleUrl: currentStyleUrl, onMapCreated: (MapMetricsController controller) { setState(() { mapController = controller; }); }, onStyleLoaded: () { print('Custom style loaded successfully!'); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), ); } void _changeStyle(String styleName) { setState(() { currentStyleUrl = styleOptions[styleName] ?? currentStyleUrl; }); } } ``` ## Dynamic Style Switching ### Smooth Style Transitions ```dart class DynamicStyleScreen extends StatefulWidget { @override _DynamicStyleScreenState createState() => _DynamicStyleScreenState(); } class _DynamicStyleScreenState extends State { MapMetricsController? mapController; bool isDarkMode = false; String get currentStyleUrl => isDarkMode ? 'https://gateway.mapmetrics.org/styles/YOUR_DARK_STYLE_ID?token=YOUR_API_KEY' : 'https://gateway.mapmetrics.org/styles/YOUR_LIGHT_STYLE_ID?token=YOUR_API_KEY'; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Dynamic Style Switching'), actions: [ Switch( value: isDarkMode, onChanged: (value) { setState(() { isDarkMode = value; }); _updateMapStyle(); }, ), ], ), body: MapMetrics( styleUrl: currentStyleUrl, onMapCreated: (controller) => mapController = controller, onStyleLoaded: () => print('Style loaded'), initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), ); } void _updateMapStyle() { mapController?.setStyleString(currentStyleUrl); } } ``` ## Custom Map Layers ### Adding Custom Overlays ```dart class CustomLayersScreen extends StatefulWidget { @override _CustomLayersScreenState createState() => _CustomLayersScreenState(); } class _CustomLayersScreenState extends State { MapMetricsController? mapController; Set customLayers = {}; @override void initState() { super.initState(); _initializeCustomLayers(); } void _initializeCustomLayers() { customLayers = { Circle( circleId: CircleId('highlight_area'), center: LatLng(40.7128, -74.0060), radius: 2000, strokeWidth: 3, strokeColor: Colors.blue, fillColor: Colors.blue.withOpacity(0.1), ), Circle( circleId: CircleId('restricted_zone'), center: LatLng(40.7589, -73.9851), radius: 1000, strokeWidth: 2, strokeColor: Colors.red, fillColor: Colors.red.withOpacity(0.2), ), }; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Custom Layers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), circles: customLayers, ), ); } } ``` ## Branded Map Styles ### Creating Brand-Consistent Maps ```dart class BrandedMapScreen extends StatefulWidget { @override _BrandedMapScreenState createState() => _BrandedMapScreenState(); } class _BrandedMapScreenState extends State { MapMetricsController? mapController; // Brand colors final Color primaryColor = Color(0xFF1E88E5); final Color secondaryColor = Color(0xFF42A5F5); final Color accentColor = Color(0xFFFF5722); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Branded Map'), backgroundColor: primaryColor, ), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_BRANDED_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), // Custom branded overlay Positioned( top: 20, right: 20, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: primaryColor.withOpacity(0.9), borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black26, blurRadius: 4, offset: Offset(0, 2), ), ], ), child: Text( 'Your Brand', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ), ], ), ); } } ``` ## Style Configuration Options ### Advanced Style Settings ```dart class AdvancedStyleScreen extends StatefulWidget { @override _AdvancedStyleScreenState createState() => _AdvancedStyleScreenState(); } class _AdvancedStyleScreenState extends State { MapMetricsController? mapController; bool showBuildings = true; bool showLabels = true; bool showRoads = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Advanced Style Options')), body: Column( children: [ // Style controls Container( padding: EdgeInsets.all(16), color: Colors.grey[100], child: Column( children: [ SwitchListTile( title: Text('Show Buildings'), value: showBuildings, onChanged: (value) { setState(() { showBuildings = value; }); _updateMapStyle(); }, ), SwitchListTile( title: Text('Show Labels'), value: showLabels, onChanged: (value) { setState(() { showLabels = value; }); _updateMapStyle(); }, ), SwitchListTile( title: Text('Show Roads'), value: showRoads, onChanged: (value) { setState(() { showRoads = value; }); _updateMapStyle(); }, ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 15.0, ), ), ), ], ), ); } void _updateMapStyle() { // You can programmatically modify style layers here // This would require additional MapMetrics GL JS functionality print('Style updated: Buildings=$showBuildings, Labels=$showLabels, Roads=$showRoads'); } } ``` ## Performance Optimization ### Style Loading Optimization ```dart class OptimizedStyleScreen extends StatefulWidget { @override _OptimizedStyleScreenState createState() => _OptimizedStyleScreenState(); } class _OptimizedStyleScreenState extends State { MapMetricsController? mapController; bool isStyleLoaded = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Optimized Style Loading')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onStyleLoaded: () { setState(() { isStyleLoaded = true; }); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), // Loading indicator if (!isStyleLoaded) Container( color: Colors.white, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Loading custom map style...'), ], ), ), ), ], ), ); } } ``` ## Best Practices ### Style Design Tips 1. **Consistency**: Use consistent colors and fonts across your app and map 2. **Accessibility**: Ensure sufficient contrast for text and important features 3. **Performance**: Keep style complexity reasonable for smooth rendering 4. **Branding**: Integrate your brand colors and elements naturally 5. **Testing**: Test your styles on different devices and screen sizes ### Style Management ```dart class StyleManager { static const Map predefinedStyles = { 'default': 'https://gateway.mapmetrics.org/styles/DEFAULT_STYLE_ID?token=YOUR_API_KEY', 'dark': 'https://gateway.mapmetrics.org/styles/DARK_STYLE_ID?token=YOUR_API_KEY', 'satellite': 'https://gateway.mapmetrics.org/styles/SATELLITE_STYLE_ID?token=YOUR_API_KEY', 'minimal': 'https://gateway.mapmetrics.org/styles/MINIMAL_STYLE_ID?token=YOUR_API_KEY', }; static String getStyleUrl(String styleName) { return predefinedStyles[styleName] ?? predefinedStyles['default']!; } static bool isValidStyleUrl(String url) { return url.startsWith('https://gateway.mapmetrics.org/styles/') && url.contains('token='); } } ``` ## Next Steps Now that you understand custom styling, try: - [Handling Map Interactions](./flutter-interactions) --- **Pro Tip**: Use the MapMetrics Portal's style editor to create multiple variations of your map style for different use cases (dark mode, minimal view, detailed view, etc.). --- # Customize Camera Animations in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-customize-camera-animations # Customize Camera Animations in Flutter This tutorial shows how to create custom camera animations — zooming, tilting, rotating, and combining multiple camera movements for cinematic map experiences. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Camera Animations Different types of camera movements with buttons: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CameraAnimationsScreen extends StatefulWidget { @override _CameraAnimationsScreenState createState() => _CameraAnimationsScreenState(); } class _CameraAnimationsScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Camera Animations')), body: Column( children: [ // Animation buttons Container( padding: EdgeInsets.all(8), child: Wrap( spacing: 8, runSpacing: 8, children: [ _animButton('Zoom In', Icons.zoom_in, _zoomIn), _animButton('Zoom Out', Icons.zoom_out, _zoomOut), _animButton('Tilt', Icons.panorama_horizontal, _tilt), _animButton('Rotate', Icons.rotate_right, _rotate), _animButton('Bird\'s Eye', Icons.flight, _birdsEye), _animButton('Reset', Icons.refresh, _reset), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 15.0, ), ), ), ], ), ); } Widget _animButton(String label, IconData icon, VoidCallback onPressed) { return ElevatedButton.icon( onPressed: onPressed, icon: Icon(icon, size: 18), label: Text(label), style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), ); } void _zoomIn() { mapController?.animateCamera( CameraUpdate.zoomTo(18.0), ); } void _zoomOut() { mapController?.animateCamera( CameraUpdate.zoomTo(10.0), ); } void _tilt() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 16.0, tilt: 60.0, bearing: 0.0, ), ), ); } void _rotate() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 16.0, tilt: 45.0, bearing: 180.0, ), ), ); } void _birdsEye() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 17.0, tilt: 75.0, bearing: 45.0, ), ), ); } void _reset() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 15.0, tilt: 0.0, bearing: 0.0, ), ), ); } } ``` ## Cinematic City Tour Automatically fly through a sequence of locations with different camera angles: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CityTourScreen extends StatefulWidget { @override _CityTourScreenState createState() => _CityTourScreenState(); } class _CityTourScreenState extends State { MapMetricsController? mapController; bool isTouring = false; int currentStop = 0; final List> tourStops = [ { 'name': 'Eiffel Tower', 'position': CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 17.0, tilt: 60.0, bearing: 45.0, ), }, { 'name': 'Arc de Triomphe', 'position': CameraPosition( target: LatLng(48.8738, 2.2950), zoom: 17.0, tilt: 55.0, bearing: 135.0, ), }, { 'name': 'Louvre Museum', 'position': CameraPosition( target: LatLng(48.8606, 2.3376), zoom: 16.5, tilt: 50.0, bearing: 220.0, ), }, { 'name': 'Notre-Dame', 'position': CameraPosition( target: LatLng(48.8530, 2.3499), zoom: 17.0, tilt: 65.0, bearing: 310.0, ), }, { 'name': 'Sacre-Coeur', 'position': CameraPosition( target: LatLng(48.8867, 2.3431), zoom: 16.0, tilt: 70.0, bearing: 180.0, ), }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('City Tour')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3200), zoom: 12.0, ), ), // Tour info bar Positioned( top: 16, left: 16, right: 16, child: Card( elevation: 4, child: Padding( padding: EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( isTouring ? tourStops[currentStop]['name'] : 'Paris City Tour', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), if (isTouring) Padding( padding: EdgeInsets.only(top: 8), child: LinearProgressIndicator( value: (currentStop + 1) / tourStops.length, ), ), ], ), ), ), ), // Tour control Positioned( bottom: 24, left: 0, right: 0, child: Center( child: ElevatedButton.icon( onPressed: isTouring ? null : _startTour, icon: Icon(isTouring ? Icons.pause : Icons.play_arrow), label: Text(isTouring ? 'Touring...' : 'Start Tour'), style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), ), ), ), ), ], ), ); } Future _startTour() async { setState(() { isTouring = true; currentStop = 0; }); for (int i = 0; i < tourStops.length; i++) { if (!mounted) return; setState(() { currentStop = i; }); mapController?.animateCamera( CameraUpdate.newCameraPosition( tourStops[i]['position'] as CameraPosition, ), ); // Wait at each stop await Future.delayed(Duration(seconds: 4)); } if (mounted) { setState(() { isTouring = false; }); } } } ``` ## Smooth Zoom with Duration Control animation speed using `moveCamera` (instant) vs `animateCamera` (smooth): ```dart void _smoothZoomToLocation() { // Smooth animated transition (default ~300ms) mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 18.0, tilt: 60.0, bearing: 30.0, ), ), ); } void _instantJumpToLocation() { // Instant jump — no animation mapController?.moveCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 18.0, tilt: 60.0, bearing: 30.0, ), ), ); } ``` ## Camera Update Methods | Method | Animation | Use Case | |--------|-----------|----------| | `animateCamera` | Smooth transition | User-facing navigation | | `moveCamera` | Instant jump | Loading, resetting | | `CameraUpdate.zoomTo(level)` | Zoom only | Quick zoom change | | `CameraUpdate.newLatLng(pos)` | Pan only | Move without zoom change | | `CameraUpdate.newLatLngZoom(pos, zoom)` | Pan + zoom | Navigate to a location | | `CameraUpdate.newCameraPosition(...)` | Full control | Tilt, bearing, zoom, pan | | `CameraUpdate.newLatLngBounds(bounds, padding)` | Fit area | Show all markers | ## Next Steps - [Fly to a Location](./flutter-fly-to-location) — Basic fly-to animation - [Slowly Fly to Location](./flutter-slowly-fly-to-location) — Slow cinematic flight - [Animate Camera Around Point](./flutter-animate-camera-around-point) — Orbit animation --- **Tip**: Combine `tilt` (0-60) and `bearing` (0-360) for dramatic 3D views. Higher tilt values give a more ground-level perspective, which works best at zoom levels 15+. --- # Data-Driven Line Styling in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-data-driven-lines # Data-Driven Line Styling in Flutter This tutorial shows how to style polylines based on data properties — useful for showing traffic speed, elevation, route type, or any varying attribute along a path. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Lines Colored by Category Style multiple route lines based on their transport type: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DataDrivenLinesScreen extends StatefulWidget { @override _DataDrivenLinesScreenState createState() => _DataDrivenLinesScreenState(); } class _DataDrivenLinesScreenState extends State { MapMetricsController? mapController; final List> routes = [ { 'name': 'Highway A1', 'type': 'highway', 'color': Colors.red, 'width': 5, 'points': [ LatLng(48.8566, 2.3522), LatLng(49.2583, 2.0833), LatLng(49.8941, 2.2958), LatLng(50.6292, 3.0573), ], }, { 'name': 'Railway TGV', 'type': 'rail', 'color': Colors.blue, 'width': 3, 'points': [ LatLng(48.8766, 2.3822), LatLng(49.2100, 2.1300), LatLng(49.8500, 2.3500), LatLng(50.6300, 3.0700), ], }, { 'name': 'Cycling Path', 'type': 'bike', 'color': Colors.green, 'width': 2, 'points': [ LatLng(48.8400, 2.3200), LatLng(49.1800, 2.0500), LatLng(49.7500, 2.2000), LatLng(50.6000, 3.0300), ], }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Data-Driven Lines')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(49.5, 2.5), zoom: 7.0, ), polylines: routes.map((route) { return Polyline( polylineId: PolylineId(route['name']), points: route['points'] as List, color: route['color'] as Color, width: route['width'] as int, ); }).toSet(), ), // Legend Positioned( top: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Transport', style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(height: 6), _legendLine(Colors.red, 5, 'Highway'), _legendLine(Colors.blue, 3, 'Railway'), _legendLine(Colors.green, 2, 'Cycling'), ], ), ), ), ), ], ), ); } Widget _legendLine(Color color, int width, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 3), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 24, height: width.toDouble(), color: color, ), SizedBox(width: 8), Text(label, style: TextStyle(fontSize: 13)), ], ), ); } } ``` ## Traffic Speed Lines Color route segments based on traffic speed: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TrafficSpeedScreen extends StatefulWidget { @override _TrafficSpeedScreenState createState() => _TrafficSpeedScreenState(); } class _TrafficSpeedScreenState extends State { MapMetricsController? mapController; // Route segments with speed data (km/h) final List> segments = [ { 'from': LatLng(48.8566, 2.3522), 'to': LatLng(48.8700, 2.3300), 'speed': 15, // slow — traffic jam }, { 'from': LatLng(48.8700, 2.3300), 'to': LatLng(48.8800, 2.3100), 'speed': 35, // moderate }, { 'from': LatLng(48.8800, 2.3100), 'to': LatLng(48.8950, 2.2800), 'speed': 60, // fast }, { 'from': LatLng(48.8950, 2.2800), 'to': LatLng(48.9100, 2.2500), 'speed': 80, // very fast }, { 'from': LatLng(48.9100, 2.2500), 'to': LatLng(48.9200, 2.2200), 'speed': 25, // slow }, { 'from': LatLng(48.9200, 2.2200), 'to': LatLng(48.9350, 2.1900), 'speed': 55, // moderate-fast }, ]; /// Map speed to color (red = slow, yellow = moderate, green = fast) Color _speedColor(int speed) { if (speed < 20) return Colors.red[700]!; if (speed < 40) return Colors.orange; if (speed < 60) return Colors.yellow[700]!; return Colors.green; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Traffic Speed')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8900, 2.2700), zoom: 12.0, ), polylines: segments.asMap().entries.map((entry) { final i = entry.key; final seg = entry.value; return Polyline( polylineId: PolylineId('seg_$i'), points: [seg['from'] as LatLng, seg['to'] as LatLng], color: _speedColor(seg['speed'] as int), width: 6, ); }).toSet(), ), // Speed legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Speed', style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(height: 4), _speedRow(Colors.red[700]!, '< 20 km/h'), _speedRow(Colors.orange, '20-40 km/h'), _speedRow(Colors.yellow[700]!, '40-60 km/h'), _speedRow(Colors.green, '> 60 km/h'), ], ), ), ), ), ], ), ); } Widget _speedRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 2), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container(width: 20, height: 4, color: color), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 12)), ], ), ); } } ``` ## Line Width by Data Vary line width based on a data property like passenger volume: ```dart Set _buildVolumeLines() { final routes = [ { 'name': 'Main Line', 'volume': 50000, // daily passengers 'points': [LatLng(48.85, 2.35), LatLng(49.00, 2.20)], }, { 'name': 'Branch A', 'volume': 15000, 'points': [LatLng(49.00, 2.20), LatLng(49.10, 2.00)], }, { 'name': 'Branch B', 'volume': 5000, 'points': [LatLng(49.00, 2.20), LatLng(49.05, 2.40)], }, ]; return routes.map((route) { // Map volume to width (2-10 pixels) final volume = route['volume'] as int; final width = ((volume / 50000) * 8 + 2).clamp(2, 10).toInt(); return Polyline( polylineId: PolylineId(route['name'] as String), points: route['points'] as List, color: Colors.blue, width: width, ); }).toSet(); } ``` ## Data Mapping Helpers | Data Type | Visual Property | Mapping Function | |-----------|----------------|------------------| | Speed | Color | Red (slow) -> Green (fast) | | Elevation | Color | Green (low) -> Red (high) | | Volume | Width | Thin (few) -> Thick (many) | | Type | Color + Pattern | Category -> fixed style | | Priority | Opacity | Low -> 0.3, High -> 1.0 | ## Next Steps - [Gradient Line](./flutter-gradient-line) — Color gradient along a line - [Add a Polyline](./flutter-add-a-polyline) — Basic polyline drawing - [Add a GeoJSON Line](./flutter-add-geojson-line) — GeoJSON-based lines --- **Tip**: For real-time data like traffic, update the polylines in `setState()` when new speed data arrives. Split long routes into short segments so each segment can have its own color based on current conditions. --- # Disable Map Rotation in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-disable-map-rotation # Disable Map Rotation in Flutter This tutorial shows how to prevent users from rotating the map — useful for simple navigation apps or when a fixed north-up orientation is required. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Disable Rotation Set `rotateGesturesEnabled` to `false` to lock the map bearing: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class NoRotationMapScreen extends StatefulWidget { @override _NoRotationMapScreenState createState() => _NoRotationMapScreenState(); } class _NoRotationMapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('No Rotation Map')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), rotateGesturesEnabled: false, // Disable rotation ), ); } } ``` ## Disable Rotation and Tilt Lock both rotation and tilt for a strictly 2D map view: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class Flat2DMapScreen extends StatefulWidget { @override _Flat2DMapScreenState createState() => _Flat2DMapScreenState(); } class _Flat2DMapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Flat 2D Map')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, bearing: 0.0, tilt: 0.0, ), rotateGesturesEnabled: false, // No rotation tiltGesturesEnabled: false, // No tilt ), ); } } ``` ## Toggle Rotation with a Button Let users turn rotation on and off: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ToggleRotationScreen extends StatefulWidget { @override _ToggleRotationScreenState createState() => _ToggleRotationScreenState(); } class _ToggleRotationScreenState extends State { MapMetricsController? mapController; bool rotationEnabled = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Toggle Rotation'), actions: [ Row( children: [ Text(rotationEnabled ? 'Rotation ON' : 'Rotation OFF'), Switch( value: rotationEnabled, onChanged: (value) { setState(() { rotationEnabled = value; }); }, activeColor: Colors.white, ), ], ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), rotateGesturesEnabled: rotationEnabled, ), floatingActionButton: FloatingActionButton( onPressed: () { // Reset bearing to north mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, bearing: 0.0, ), ), ); }, child: Icon(Icons.north), tooltip: 'Reset to North', ), ); } } ``` ## Gesture Control Properties | Property | Type | Default | Description | |----------|------|---------|-------------| | `rotateGesturesEnabled` | `bool` | `true` | Allow two-finger rotation | | `tiltGesturesEnabled` | `bool` | `true` | Allow two-finger tilt | | `scrollGesturesEnabled` | `bool` | `true` | Allow panning | | `zoomGesturesEnabled` | `bool` | `true` | Allow pinch-to-zoom | ## Next Steps - [Toggle Interactions](./flutter-toggle-interactions) — Enable/disable all gestures - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Control camera angle - [Restrict Map Panning](./flutter-restrict-map-panning) — Limit the viewable area --- **Tip**: Disabling rotation is common for turn-by-turn navigation apps where you want the map to always face north, or for embedded maps where simplicity is important. --- # Disable Scroll Zoom in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-disable-scroll-zoom # Disable Scroll Zoom in Flutter This tutorial shows how to disable or control zoom gestures on your MapMetrics Flutter map. This is useful for maps embedded in scrollable pages where pinch-to-zoom conflicts with page scrolling. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Disable Zoom Gestures Disable pinch-to-zoom and double-tap-to-zoom at initialization: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DisableZoomScreen extends StatefulWidget { @override _DisableZoomScreenState createState() => _DisableZoomScreenState(); } class _DisableZoomScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Zoom Disabled')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), zoomGesturesEnabled: false, // Disables pinch-to-zoom ), ); } } ``` The map displays normally but users cannot zoom with gestures. You can still zoom programmatically with buttons. ## Disable All Gestures Lock the map completely — no pan, zoom, or rotation: ```dart MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), zoomGesturesEnabled: false, scrollGesturesEnabled: false, rotateGesturesEnabled: false, tiltGesturesEnabled: false, ) ``` ## Toggle Zoom On/Off Let users enable or disable zoom with a switch: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ToggleZoomScreen extends StatefulWidget { @override _ToggleZoomScreenState createState() => _ToggleZoomScreenState(); } class _ToggleZoomScreenState extends State { MapMetricsController? mapController; bool zoomEnabled = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Toggle Zoom'), actions: [ Row( children: [ Text( zoomEnabled ? 'Zoom ON' : 'Zoom OFF', style: TextStyle(color: Colors.white), ), Switch( value: zoomEnabled, onChanged: (value) { setState(() { zoomEnabled = value; }); }, activeColor: Colors.white, ), ], ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), zoomGesturesEnabled: zoomEnabled, ), ); } } ``` ## Embedded Map in Scrollable Page A common pattern: disable gestures on an embedded map, then provide a "tap to interact" overlay: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class EmbeddedMapScreen extends StatefulWidget { @override _EmbeddedMapScreenState createState() => _EmbeddedMapScreenState(); } class _EmbeddedMapScreenState extends State { MapMetricsController? mapController; bool isMapActive = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Embedded Map')), body: SingleChildScrollView( child: Column( children: [ // Some content above the map Container( padding: EdgeInsets.all(20), child: Text( 'Scroll down to see the map. Tap the map to interact with it.', style: TextStyle(fontSize: 16), ), ), // Embedded map with tap-to-activate Container( height: 300, margin: EdgeInsets.all(16), clipBehavior: Clip.hardEdge, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[300]!), ), child: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), zoomGesturesEnabled: isMapActive, scrollGesturesEnabled: isMapActive, rotateGesturesEnabled: isMapActive, ), // Overlay: tap to activate if (!isMapActive) GestureDetector( onTap: () { setState(() { isMapActive = true; }); }, child: Container( color: Colors.transparent, alignment: Alignment.center, child: Container( padding: EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(8), ), child: Text( 'Tap to interact with map', style: TextStyle(color: Colors.white), ), ), ), ), ], ), ), // More content below Container( padding: EdgeInsets.all(20), child: Text( 'More content below the map...', style: TextStyle(fontSize: 16), ), ), ], ), ), ); } } ``` ## Gesture Properties | Property | Type | Default | Description | |----------|------|---------|-------------| | `zoomGesturesEnabled` | `bool` | `true` | Pinch-to-zoom and double-tap zoom | | `scrollGesturesEnabled` | `bool` | `true` | Pan/drag to move the map | | `rotateGesturesEnabled` | `bool` | `true` | Two-finger rotation | | `tiltGesturesEnabled` | `bool` | `true` | Two-finger tilt for 3D perspective | ## Next Steps - [Toggle Interactions](./flutter-toggle-interactions) — Fine-grained control of all gestures - [Navigation Controls](./flutter-navigation-controls) — Add zoom buttons when gestures are disabled - [Map Interactions](./flutter-interactions) — Full interaction handling guide --- **Tip**: When embedding a map in a scrollable page, always disable scroll gestures so the page scroll works normally. Provide a "tap to interact" overlay to let users opt in. --- # Display a Popup in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-display-popup # Display a Popup in Flutter This tutorial shows different ways to display popups and info overlays on your MapMetrics Flutter map — from simple info windows to custom bottom sheets. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Info Window Popup The simplest popup uses the built-in `InfoWindow` on markers: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BasicPopupScreen extends StatefulWidget { @override _BasicPopupScreenState createState() => _BasicPopupScreenState(); } class _BasicPopupScreenState extends State { MapMetricsController? mapController; final Set markers = { Marker( markerId: MarkerId('eiffel'), position: LatLng(48.8584, 2.2945), infoWindow: InfoWindow( title: 'Eiffel Tower', snippet: 'Built in 1889 — 330m tall', ), ), Marker( markerId: MarkerId('louvre'), position: LatLng(48.8606, 2.3376), infoWindow: InfoWindow( title: 'Louvre Museum', snippet: 'Home of the Mona Lisa', ), ), Marker( markerId: MarkerId('notre_dame'), position: LatLng(48.8530, 2.3499), infoWindow: InfoWindow( title: 'Notre-Dame Cathedral', snippet: 'Gothic masterpiece since 1163', ), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Basic Popup')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3200), zoom: 13.0, ), markers: markers, ), ); } } ``` ## Custom Popup Overlay Show a floating card popup when tapping a marker: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CustomPopupScreen extends StatefulWidget { @override _CustomPopupScreenState createState() => _CustomPopupScreenState(); } class _CustomPopupScreenState extends State { MapMetricsController? mapController; Map? selectedPlace; final List> places = [ { 'id': 'eiffel', 'name': 'Eiffel Tower', 'description': 'Iconic iron lattice tower on the Champ de Mars.', 'lat': 48.8584, 'lng': 2.2945, 'rating': 4.7, }, { 'id': 'louvre', 'name': 'Louvre Museum', 'description': 'World\'s largest art museum and historic monument.', 'lat': 48.8606, 'lng': 2.3376, 'rating': 4.8, }, { 'id': 'sacre_coeur', 'name': 'Sacre-Coeur', 'description': 'White-domed basilica atop Montmartre hill.', 'lat': 48.8867, 'lng': 2.3431, 'rating': 4.6, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Custom Popup')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8600, 2.3200), zoom: 13.0, ), markers: _buildMarkers(), onMapClick: (Point point, LatLng coordinates) { // Dismiss popup when tapping the map setState(() { selectedPlace = null; }); }, ), // Custom popup card if (selectedPlace != null) Positioned( bottom: 24, left: 16, right: 16, child: _buildPopupCard(), ), ], ), ); } Set _buildMarkers() { return places.map((place) { return Marker( markerId: MarkerId(place['id']), position: LatLng(place['lat'], place['lng']), onTap: () { setState(() { selectedPlace = place; }); // Center the map on the tapped marker mapController?.animateCamera( CameraUpdate.newLatLng(LatLng(place['lat'], place['lng'])), ); }, ); }).toSet(); } Widget _buildPopupCard() { return Card( elevation: 8, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( selectedPlace!['name'], style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: Icon(Icons.close), onPressed: () { setState(() { selectedPlace = null; }); }, ), ], ), SizedBox(height: 4), Row( children: [ Icon(Icons.star, color: Colors.amber, size: 18), SizedBox(width: 4), Text('${selectedPlace!['rating']}'), ], ), SizedBox(height: 8), Text( selectedPlace!['description'], style: TextStyle(color: Colors.grey[700]), ), SizedBox(height: 12), Row( children: [ ElevatedButton.icon( onPressed: () { // Handle directions action }, icon: Icon(Icons.directions, size: 18), label: Text('Directions'), ), SizedBox(width: 8), OutlinedButton.icon( onPressed: () { // Handle share action }, icon: Icon(Icons.share, size: 18), label: Text('Share'), ), ], ), ], ), ), ); } } ``` ## Bottom Sheet Popup Use a bottom sheet for more detailed information: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BottomSheetPopupScreen extends StatefulWidget { @override _BottomSheetPopupScreenState createState() => _BottomSheetPopupScreenState(); } class _BottomSheetPopupScreenState extends State { MapMetricsController? mapController; final List> landmarks = [ { 'id': 'eiffel', 'name': 'Eiffel Tower', 'address': 'Champ de Mars, 5 Av. Anatole France', 'hours': 'Open 9:30 AM - 11:45 PM', 'lat': 48.8584, 'lng': 2.2945, }, { 'id': 'arc', 'name': 'Arc de Triomphe', 'address': 'Place Charles de Gaulle', 'hours': 'Open 10:00 AM - 10:30 PM', 'lat': 48.8738, 'lng': 2.2950, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Bottom Sheet Popup')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8600, 2.3000), zoom: 13.0, ), markers: landmarks.map((landmark) { return Marker( markerId: MarkerId(landmark['id']), position: LatLng(landmark['lat'], landmark['lng']), onTap: () => _showBottomSheet(landmark), ); }).toSet(), ), ); } void _showBottomSheet(Map landmark) { mapController?.animateCamera( CameraUpdate.newLatLng(LatLng(landmark['lat'], landmark['lng'])), ); showModalBottomSheet( context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (context) { return Padding( padding: EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), ), SizedBox(height: 16), Text( landmark['name'], style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Row( children: [ Icon(Icons.location_on, size: 16, color: Colors.grey), SizedBox(width: 4), Text(landmark['address'], style: TextStyle(color: Colors.grey[600])), ], ), SizedBox(height: 4), Row( children: [ Icon(Icons.access_time, size: 16, color: Colors.green), SizedBox(width: 4), Text(landmark['hours'], style: TextStyle(color: Colors.green)), ], ), SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.pop(context), child: Text('Get Directions'), ), ), ], ), ); }, ); } } ``` ## Next Steps - [Add a Popup](./flutter-add-a-popup) — Simple marker popups - [Popup on Click](./flutter-popup-on-click) — Show popups on map tap - [Markers and Annotations](./flutter-markers) — Full marker guide --- **Tip**: For production apps, use the bottom sheet approach — it feels native on mobile and provides more space for content. Use simple `InfoWindow` for quick prototypes. --- # Display the Whole World in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-display-whole-world # Display the Whole World in Flutter This tutorial shows how to display a zoomed-out view of the entire world — perfect for global dashboards, flight maps, or selecting a region. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic World View Show the entire world with a low zoom level: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class WholeWorldScreen extends StatefulWidget { @override _WholeWorldScreenState createState() => _WholeWorldScreenState(); } class _WholeWorldScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('World View')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(20.0, 0.0), // Center on equator zoom: 1.0, // Zoomed out to see the whole world ), ), ); } } ``` ## World Map with Global Markers Display markers for major world cities on a global view: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GlobalMarkersScreen extends StatefulWidget { @override _GlobalMarkersScreenState createState() => _GlobalMarkersScreenState(); } class _GlobalMarkersScreenState extends State { MapMetricsController? mapController; final List> worldCities = [ {'name': 'New York', 'lat': 40.7128, 'lng': -74.006, 'continent': 'NA'}, {'name': 'Los Angeles', 'lat': 34.0522, 'lng': -118.2437, 'continent': 'NA'}, {'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'continent': 'EU'}, {'name': 'Paris', 'lat': 48.8566, 'lng': 2.3522, 'continent': 'EU'}, {'name': 'Tokyo', 'lat': 35.6762, 'lng': 139.6503, 'continent': 'AS'}, {'name': 'Shanghai', 'lat': 31.2304, 'lng': 121.4737, 'continent': 'AS'}, {'name': 'Dubai', 'lat': 25.2048, 'lng': 55.2708, 'continent': 'AS'}, {'name': 'Mumbai', 'lat': 19.076, 'lng': 72.8777, 'continent': 'AS'}, {'name': 'Sydney', 'lat': -33.8688, 'lng': 151.2093, 'continent': 'OC'}, {'name': 'Sao Paulo', 'lat': -23.5505, 'lng': -46.6333, 'continent': 'SA'}, {'name': 'Cairo', 'lat': 30.0444, 'lng': 31.2357, 'continent': 'AF'}, {'name': 'Lagos', 'lat': 6.5244, 'lng': 3.3792, 'continent': 'AF'}, {'name': 'Singapore', 'lat': 1.3521, 'lng': 103.8198, 'continent': 'AS'}, {'name': 'Moscow', 'lat': 55.7558, 'lng': 37.6173, 'continent': 'EU'}, {'name': 'Mexico City', 'lat': 19.4326, 'lng': -99.1332, 'continent': 'NA'}, ]; final Map continentHues = { 'NA': BitmapDescriptor.hueBlue, 'SA': BitmapDescriptor.hueGreen, 'EU': BitmapDescriptor.hueRed, 'AF': BitmapDescriptor.hueOrange, 'AS': BitmapDescriptor.hueViolet, 'OC': BitmapDescriptor.hueCyan, }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Global Cities')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(20.0, 0.0), zoom: 1.5, ), markers: worldCities.map((city) { return Marker( markerId: MarkerId(city['name']), position: LatLng(city['lat'], city['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( continentHues[city['continent']]!, ), infoWindow: InfoWindow(title: city['name']), onTap: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(city['lat'], city['lng']), 10.0, ), ); }, ); }).toSet(), ), // Legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ _legendRow(Colors.blue, 'North America'), _legendRow(Colors.green, 'South America'), _legendRow(Colors.red, 'Europe'), _legendRow(Colors.orange, 'Africa'), _legendRow(Colors.purple, 'Asia'), _legendRow(Colors.cyan, 'Oceania'), ], ), ), ), ), // Zoom out button Positioned( top: 16, right: 16, child: FloatingActionButton.small( onPressed: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom(LatLng(20.0, 0.0), 1.5), ); }, child: Icon(Icons.public), tooltip: 'Show World', ), ), ], ), ); } Widget _legendRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.circle, color: color, size: 10), SizedBox(width: 4), Text(label, style: TextStyle(fontSize: 11)), ], ), ); } } ``` ## Region Selector Let users tap a continent to zoom in: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RegionSelectorScreen extends StatefulWidget { @override _RegionSelectorScreenState createState() => _RegionSelectorScreenState(); } class _RegionSelectorScreenState extends State { MapMetricsController? mapController; final List> regions = [ {'name': 'Europe', 'lat': 50.0, 'lng': 10.0, 'zoom': 4.0}, {'name': 'Asia', 'lat': 35.0, 'lng': 100.0, 'zoom': 3.0}, {'name': 'N. America', 'lat': 40.0, 'lng': -100.0, 'zoom': 3.0}, {'name': 'S. America', 'lat': -15.0, 'lng': -60.0, 'zoom': 3.0}, {'name': 'Africa', 'lat': 5.0, 'lng': 20.0, 'zoom': 3.0}, {'name': 'Oceania', 'lat': -25.0, 'lng': 140.0, 'zoom': 3.5}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Region Selector')), body: Column( children: [ Container( padding: EdgeInsets.all(8), child: Wrap( spacing: 6, runSpacing: 6, children: [ ActionChip( avatar: Icon(Icons.public, size: 16), label: Text('World'), onPressed: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom(LatLng(20.0, 0.0), 1.0), ); }, ), ...regions.map((r) => ActionChip( label: Text(r['name']), onPressed: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(r['lat'], r['lng']), r['zoom'], ), ); }, )), ], ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(20.0, 0.0), zoom: 1.0, ), ), ), ], ), ); } } ``` ## World View Zoom Levels | Zoom | View | |------|------| | 0 - 1 | Full globe / world | | 2 - 3 | Continent | | 4 - 6 | Country | | 7 - 10 | Region / State | | 11 - 14 | City | | 15 - 18 | Street / Building | ## Next Steps - [Restrict Map Panning](./flutter-restrict-map-panning) — Lock to a specific region - [Jump to Locations](./flutter-jump-to-locations) — Navigate between locations - [Arc Layer](./flutter-arc-layer) — Draw flight routes across the globe --- **Tip**: At zoom level 1, the map shows the whole world. Use `LatLng(20.0, 0.0)` as the center for a balanced view that shows all continents. For global dashboards, disable tilt and rotation to keep the view clean. --- # Draggable Marker in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-draggable-marker # Draggable Marker in Flutter This tutorial shows how to create markers that users can drag to a new position on the map. This is useful for letting users pick a location, adjust a pin, or reposition points of interest. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Draggable Marker Set `draggable: true` and use `onDragEnd` to get the new position: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DraggableMarkerScreen extends StatefulWidget { @override _DraggableMarkerScreenState createState() => _DraggableMarkerScreenState(); } class _DraggableMarkerScreenState extends State { MapMetricsController? mapController; LatLng markerPosition = LatLng(48.8566, 2.3522); Set get markers => { Marker( markerId: MarkerId('draggable'), position: markerPosition, draggable: true, onDragEnd: (LatLng newPosition) { setState(() { markerPosition = newPosition; }); }, infoWindow: InfoWindow( title: 'Drag me!', snippet: 'Long press and drag to move', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Draggable Marker')), body: Column( children: [ // Coordinate display Container( width: double.infinity, padding: EdgeInsets.all(12), color: Colors.grey[100], child: Text( 'Position: ${markerPosition.latitude.toStringAsFixed(6)}, ' '${markerPosition.longitude.toStringAsFixed(6)}', style: TextStyle(fontFamily: 'monospace', fontSize: 14), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 14.0, ), markers: markers, ), ), ], ), ); } } ``` Long-press the marker and drag it to a new location. The coordinate display updates in real time. ## Drag Events Track all stages of the drag — start, move, and end: ```dart Marker( markerId: MarkerId('tracked_drag'), position: markerPosition, draggable: true, onDragStart: (LatLng startPosition) { print('Drag started at: $startPosition'); // You could show a visual indicator like a shadow }, onDrag: (LatLng currentPosition) { // Called continuously while dragging // Useful for updating a real-time coordinate display setState(() { markerPosition = currentPosition; }); }, onDragEnd: (LatLng endPosition) { print('Drag ended at: $endPosition'); setState(() { markerPosition = endPosition; }); }, ) ``` ## Complete Example: Location Picker A practical example where the user drags a marker to select a delivery address: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LocationPickerScreen extends StatefulWidget { @override _LocationPickerScreenState createState() => _LocationPickerScreenState(); } class _LocationPickerScreenState extends State { MapMetricsController? mapController; LatLng selectedPosition = LatLng(48.8566, 2.3522); bool isDragging = false; Set get markers => { Marker( markerId: MarkerId('picker'), position: selectedPosition, draggable: true, onDragStart: (LatLng position) { setState(() { isDragging = true; }); }, onDragEnd: (LatLng newPosition) { setState(() { selectedPosition = newPosition; isDragging = false; }); }, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Pick a Location')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: selectedPosition, zoom: 15.0, ), markers: markers, ), // Bottom card with confirm button Positioned( bottom: 24, left: 16, right: 16, child: Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( isDragging ? 'Release to select...' : 'Drag the pin to your location', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), ), SizedBox(height: 8), Text( 'Lat: ${selectedPosition.latitude.toStringAsFixed(6)}\n' 'Lng: ${selectedPosition.longitude.toStringAsFixed(6)}', style: TextStyle( fontFamily: 'monospace', fontSize: 13, color: Colors.grey[600], ), ), SizedBox(height: 12), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: isDragging ? null : () { // Use selectedPosition for your app logic ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Location confirmed: ' '${selectedPosition.latitude.toStringAsFixed(4)}, ' '${selectedPosition.longitude.toStringAsFixed(4)}', ), ), ); }, child: Text('Confirm Location'), ), ), ], ), ), ), ), ], ), ); } } ``` ## Marker Drag Properties | Property | Type | Description | |----------|------|-------------| | `draggable` | `bool` | Enable/disable dragging (default: `false`) | | `onDragStart` | `ValueChanged` | Called when the user starts dragging | | `onDrag` | `ValueChanged` | Called continuously while dragging | | `onDragEnd` | `ValueChanged` | Called when the user releases the marker | ## Next Steps - [Add a Popup](./flutter-add-a-popup) — Show info when tapping markers - [Markers and Annotations](./flutter-markers) — Learn more about marker customization - [Map Interactions](./flutter-interactions) — Handle all types of user interaction --- **Tip**: Long-press on the marker to start dragging it. A regular tap will open the info window if one is set. --- # Create a Draggable Point in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-draggable-point # Create a Draggable Point in Flutter This tutorial shows how to create a draggable point that draws a line or shape as you drag it — useful for route planning, area selection, or interactive drawing tools. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Draggable Point with Trail Drag a point on the map and leave a trail line behind: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DraggablePointTrailScreen extends StatefulWidget { @override _DraggablePointTrailScreenState createState() => _DraggablePointTrailScreenState(); } class _DraggablePointTrailScreenState extends State { MapMetricsController? mapController; LatLng currentPosition = LatLng(48.8566, 2.3522); List trail = [LatLng(48.8566, 2.3522)]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Draggable Point with Trail'), actions: [ IconButton( icon: Icon(Icons.clear), tooltip: 'Clear trail', onPressed: () { setState(() { trail = [currentPosition]; }); }, ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), markers: { Marker( markerId: MarkerId('draggable'), position: currentPosition, draggable: true, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Drag me!'), onDragEnd: (LatLng newPosition) { setState(() { currentPosition = newPosition; trail.add(newPosition); }); }, ), }, polylines: trail.length > 1 ? { Polyline( polylineId: PolylineId('trail'), points: trail, color: Colors.blue, width: 3, patterns: [PatternItem.dash(10), PatternItem.gap(5)], ), } : {}, ), ); } } ``` ## Two-Point Distance Measurer Drag two points to measure the distance between them: ```dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TwoPointDragScreen extends StatefulWidget { @override _TwoPointDragScreenState createState() => _TwoPointDragScreenState(); } class _TwoPointDragScreenState extends State { MapMetricsController? mapController; LatLng pointA = LatLng(48.8584, 2.2945); // Eiffel Tower LatLng pointB = LatLng(48.8606, 2.3376); // Louvre /// Haversine distance in km double _distanceKm(LatLng a, LatLng b) { const R = 6371.0; final dLat = _toRad(b.latitude - a.latitude); final dLon = _toRad(b.longitude - a.longitude); final sinLat = sin(dLat / 2); final sinLon = sin(dLon / 2); final h = sinLat * sinLat + cos(_toRad(a.latitude)) * cos(_toRad(b.latitude)) * sinLon * sinLon; return R * 2 * atan2(sqrt(h), sqrt(1 - h)); } double _toRad(double deg) => deg * pi / 180; @override Widget build(BuildContext context) { final distance = _distanceKm(pointA, pointB); return Scaffold( appBar: AppBar(title: Text('Distance Measurer')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8590, 2.3160), zoom: 14.0, ), markers: { Marker( markerId: MarkerId('pointA'), position: pointA, draggable: true, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueGreen), infoWindow: InfoWindow(title: 'Point A'), onDragEnd: (pos) => setState(() => pointA = pos), ), Marker( markerId: MarkerId('pointB'), position: pointB, draggable: true, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'Point B'), onDragEnd: (pos) => setState(() => pointB = pos), ), }, polylines: { Polyline( polylineId: PolylineId('measurement'), points: [pointA, pointB], color: Colors.orange, width: 3, ), }, ), // Distance display Positioned( top: 16, left: 16, right: 16, child: Card( elevation: 4, child: Padding( padding: EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( '${distance.toStringAsFixed(2)} km', style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: Colors.blue, ), ), Text( '(${(distance * 1000).toStringAsFixed(0)} meters)', style: TextStyle(color: Colors.grey[600]), ), SizedBox(height: 4), Text( 'Drag the markers to measure', style: TextStyle(color: Colors.grey, fontSize: 12), ), ], ), ), ), ), ], ), ); } } ``` ## Polygon Drawing with Draggable Vertices Create a polygon by tapping, then adjust vertices by dragging: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DraggablePolygonScreen extends StatefulWidget { @override _DraggablePolygonScreenState createState() => _DraggablePolygonScreenState(); } class _DraggablePolygonScreenState extends State { MapMetricsController? mapController; List vertices = [ LatLng(48.860, 2.330), LatLng(48.860, 2.360), LatLng(48.845, 2.360), LatLng(48.845, 2.330), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Draggable Polygon')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.852, 2.345), zoom: 14.0, ), // Draggable vertex markers markers: vertices.asMap().entries.map((entry) { final i = entry.key; return Marker( markerId: MarkerId('vertex_$i'), position: entry.value, draggable: true, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), onDragEnd: (newPos) { setState(() { vertices[i] = newPos; }); }, ); }).toSet(), // Polygon shape polygons: { Polygon( polygonId: PolygonId('editable'), points: vertices, fillColor: Colors.blue.withOpacity(0.2), strokeColor: Colors.blue, strokeWidth: 2, ), }, ), ); } } ``` ## Drag Event Callbacks | Callback | When it Fires | |----------|---------------| | `onDragStart` | User begins dragging the marker | | `onDrag` | Marker position updates during drag | | `onDragEnd` | User releases the marker | ## Next Steps - [Draggable Marker](./flutter-draggable-marker) — Basic draggable marker - [Measure Distances](./flutter-measure-distances) — Full measurement tool - [Get Coordinates on Tap](./flutter-get-coordinates-on-tap) — Tap for coordinates --- **Tip**: Use `onDrag` for live updates during dragging (like showing a live distance measurement) and `onDragEnd` for final actions (like saving the position). Be mindful that `onDrag` fires very frequently — debounce expensive operations. --- # Draw a Circle in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-draw-a-circle # Draw a Circle in Flutter This tutorial shows how to draw circles on your MapMetrics Flutter map. Circles are useful for showing a radius around a point — like a search area, delivery zone, or coverage range. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Circle Draw a circle by specifying a center point and a radius in meters: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CircleExampleScreen extends StatefulWidget { @override _CircleExampleScreenState createState() => _CircleExampleScreenState(); } class _CircleExampleScreenState extends State { MapMetricsController? mapController; final Set circles = { Circle( circleId: CircleId('search_radius'), center: LatLng(48.8566, 2.3522), radius: 2000, // 2 km radius strokeWidth: 2, strokeColor: Colors.blue, fillColor: Colors.blue.withOpacity(0.15), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Circle Example')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), circles: circles, ), ); } } ``` ## Multiple Circles with Different Radii Show concentric circles or multiple zones: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultipleCirclesScreen extends StatefulWidget { @override _MultipleCirclesScreenState createState() => _MultipleCirclesScreenState(); } class _MultipleCirclesScreenState extends State { MapMetricsController? mapController; final LatLng center = LatLng(40.7128, -74.0060); Set get circles => { // Inner zone - 1 km Circle( circleId: CircleId('inner'), center: center, radius: 1000, strokeWidth: 2, strokeColor: Colors.green, fillColor: Colors.green.withOpacity(0.2), ), // Middle zone - 3 km Circle( circleId: CircleId('middle'), center: center, radius: 3000, strokeWidth: 2, strokeColor: Colors.orange, fillColor: Colors.orange.withOpacity(0.1), ), // Outer zone - 5 km Circle( circleId: CircleId('outer'), center: center, radius: 5000, strokeWidth: 2, strokeColor: Colors.red, fillColor: Colors.red.withOpacity(0.05), ), }; // Center marker Set get markers => { Marker( markerId: MarkerId('center'), position: center, infoWindow: InfoWindow( title: 'Center Point', snippet: '1 km / 3 km / 5 km zones', ), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Coverage Zones')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: center, zoom: 12.0, ), circles: circles, markers: markers, ), ); } } ``` ## Dynamic Circle: Tap to Place Let users tap the map to place a circle at any location: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class DynamicCircleScreen extends StatefulWidget { @override _DynamicCircleScreenState createState() => _DynamicCircleScreenState(); } class _DynamicCircleScreenState extends State { MapMetricsController? mapController; Set circles = {}; Set markers = {}; double radiusInMeters = 1000; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Tap to Draw Circle')), body: Column( children: [ // Radius slider Padding( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ Text('Radius: ${radiusInMeters.toInt()} m'), Expanded( child: Slider( value: radiusInMeters, min: 200, max: 5000, divisions: 24, onChanged: (value) { setState(() { radiusInMeters = value; }); }, ), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onMapClick: (Point point, LatLng coordinates) { _addCircle(coordinates); }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), circles: circles, markers: markers, ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { circles.clear(); markers.clear(); }); }, child: Icon(Icons.clear_all), ), ); } void _addCircle(LatLng position) { final String id = 'circle_${DateTime.now().millisecondsSinceEpoch}'; setState(() { circles.add( Circle( circleId: CircleId(id), center: position, radius: radiusInMeters, strokeWidth: 2, strokeColor: Colors.blue, fillColor: Colors.blue.withOpacity(0.15), ), ); markers.add( Marker( markerId: MarkerId(id), position: position, infoWindow: InfoWindow( title: 'Circle', snippet: 'Radius: ${radiusInMeters.toInt()} m', ), ), ); }); } } ``` ## Circle Properties | Property | Type | Description | |----------|------|-------------| | `circleId` | `CircleId` | Unique identifier for the circle | | `center` | `LatLng` | Center point of the circle | | `radius` | `double` | Radius in **meters** | | `strokeWidth` | `int` | Border width in pixels | | `strokeColor` | `Color` | Border color | | `fillColor` | `Color` | Fill color (use `withOpacity` for transparency) | | `visible` | `bool` | Whether the circle is visible | | `zIndex` | `int` | Drawing order relative to other overlays | | `consumeTapEvents` | `bool` | If `true`, tap events are consumed by the circle | ## Next Steps - [Add a Polygon](./flutter-add-a-polygon) — Draw custom shapes on the map - [Add a Polyline](./flutter-add-a-polyline) — Draw lines and routes - [Markers and Annotations](./flutter-markers) — Add markers with popups --- **Tip**: The radius is always in meters and the circle scales correctly at all zoom levels — it represents a real geographic area on the map. --- # Draw GeoJSON Points in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-draw-geojson-points # Draw GeoJSON Points in Flutter This tutorial shows how to render multiple points from GeoJSON data on your MapMetrics Flutter map — efficient for displaying large datasets of locations. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic GeoJSON Points Render European capital cities as circle markers from a GeoJSON FeatureCollection: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GeoJsonPointsScreen extends StatefulWidget { @override _GeoJsonPointsScreenState createState() => _GeoJsonPointsScreenState(); } class _GeoJsonPointsScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('GeoJSON Points')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(50.0, 10.0), zoom: 3.0, ), onStyleLoaded: () { _addGeoJsonPoints(); }, ), ); } void _addGeoJsonPoints() { final geoJson = { 'type': 'FeatureCollection', 'features': [ { 'type': 'Feature', 'properties': {'name': 'Paris', 'population': 2161000}, 'geometry': { 'type': 'Point', 'coordinates': [2.349902, 48.852966], }, }, { 'type': 'Feature', 'properties': {'name': 'London', 'population': 8982000}, 'geometry': { 'type': 'Point', 'coordinates': [-0.1276, 51.5074], }, }, { 'type': 'Feature', 'properties': {'name': 'Berlin', 'population': 3645000}, 'geometry': { 'type': 'Point', 'coordinates': [13.405, 52.52], }, }, { 'type': 'Feature', 'properties': {'name': 'Rome', 'population': 2873000}, 'geometry': { 'type': 'Point', 'coordinates': [12.4964, 41.9028], }, }, { 'type': 'Feature', 'properties': {'name': 'Madrid', 'population': 3223000}, 'geometry': { 'type': 'Point', 'coordinates': [-3.7038, 40.4168], }, }, { 'type': 'Feature', 'properties': {'name': 'Vienna', 'population': 1897000}, 'geometry': { 'type': 'Point', 'coordinates': [16.3738, 48.2082], }, }, { 'type': 'Feature', 'properties': {'name': 'Amsterdam', 'population': 872680}, 'geometry': { 'type': 'Point', 'coordinates': [4.9041, 52.3676], }, }, ], }; // Add the GeoJSON source mapController?.addGeoJsonSource('cities', geoJson); // Add a circle layer to render points mapController?.addCircleLayer( 'cities-circles', 'cities', circleRadius: 8.0, circleColor: '#3b82f6', circleStrokeColor: '#ffffff', circleStrokeWidth: 2.0, ); } } ``` ## Points with Labels Add text labels next to each point: ```dart void _addPointsWithLabels() { final geoJson = { 'type': 'FeatureCollection', 'features': [ { 'type': 'Feature', 'properties': {'name': 'Paris'}, 'geometry': { 'type': 'Point', 'coordinates': [2.349902, 48.852966], }, }, { 'type': 'Feature', 'properties': {'name': 'London'}, 'geometry': { 'type': 'Point', 'coordinates': [-0.1276, 51.5074], }, }, { 'type': 'Feature', 'properties': {'name': 'Berlin'}, 'geometry': { 'type': 'Point', 'coordinates': [13.405, 52.52], }, }, ], }; mapController?.addGeoJsonSource('labeled-cities', geoJson); // Circle layer mapController?.addCircleLayer( 'labeled-cities-circles', 'labeled-cities', circleRadius: 6.0, circleColor: '#ef4444', circleStrokeColor: '#ffffff', circleStrokeWidth: 2.0, ); // Text label layer mapController?.addSymbolLayer( 'labeled-cities-labels', 'labeled-cities', textField: '{name}', textSize: 12.0, textColor: '#1f2937', textOffset: [0.0, 1.5], textAnchor: 'top', ); } ``` ## Styled Points with Different Sizes Use Flutter markers alongside GeoJSON data for richer styling: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class StyledPointsScreen extends StatefulWidget { @override _StyledPointsScreenState createState() => _StyledPointsScreenState(); } class _StyledPointsScreenState extends State { MapMetricsController? mapController; final List> cities = [ {'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'pop': 8982000}, {'name': 'Berlin', 'lat': 52.52, 'lng': 13.405, 'pop': 3645000}, {'name': 'Madrid', 'lat': 40.4168, 'lng': -3.7038, 'pop': 3223000}, {'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964, 'pop': 2873000}, {'name': 'Paris', 'lat': 48.853, 'lng': 2.3499, 'pop': 2161000}, {'name': 'Vienna', 'lat': 48.2082, 'lng': 16.3738, 'pop': 1897000}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Styled Points')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), markers: _buildMarkers(), ), // Legend Positioned( bottom: 16, left: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Population', style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(height: 4), Row(children: [ Container(width: 10, height: 10, decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle)), SizedBox(width: 6), Text('> 5M'), ]), Row(children: [ Container(width: 8, height: 8, decoration: BoxDecoration(color: Colors.orange, shape: BoxShape.circle)), SizedBox(width: 6), Text('2M - 5M'), ]), Row(children: [ Container(width: 6, height: 6, decoration: BoxDecoration(color: Colors.blue, shape: BoxShape.circle)), SizedBox(width: 6), Text('< 2M'), ]), ], ), ), ), ], ), ); } Set _buildMarkers() { return cities.map((city) { return Marker( markerId: MarkerId(city['name']), position: LatLng(city['lat'], city['lng']), infoWindow: InfoWindow( title: city['name'], snippet: 'Pop: ${(city['pop'] / 1000000).toStringAsFixed(1)}M', ), ); }).toSet(); } } ``` ## GeoJSON Circle Layer Properties | Property | Type | Description | |----------|------|-------------| | `circleRadius` | `double` | Radius of the circle in pixels | | `circleColor` | `String` | Fill color of the circle | | `circleOpacity` | `double` | Fill opacity from 0.0 to 1.0 | | `circleStrokeColor` | `String` | Border color of the circle | | `circleStrokeWidth` | `double` | Border width in pixels | ## Next Steps - [Add a GeoJSON Line](./flutter-add-geojson-line) — Draw lines from GeoJSON - [Add a GeoJSON Polygon](./flutter-add-geojson-polygon) — Draw filled areas - [Add Clusters](./flutter-add-a-cluster) — Cluster many points together --- **Tip**: GeoJSON circle layers are more efficient than individual markers when displaying hundreds or thousands of points. Use them for large datasets and switch to Flutter markers only when you need custom widgets. --- # Filter Markers by Text Input in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-filter-by-text-input # Filter Markers by Text Input in Flutter This tutorial shows how to filter map markers in real-time as the user types in a search field — great for location search, store finders, or point-of-interest lookup. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Text Filter Type to filter city markers on the map: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TextFilterScreen extends StatefulWidget { @override _TextFilterScreenState createState() => _TextFilterScreenState(); } class _TextFilterScreenState extends State { MapMetricsController? mapController; String searchQuery = ''; final List> allPlaces = [ {'name': 'Paris', 'lat': 48.8566, 'lng': 2.3522, 'country': 'France'}, {'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'country': 'UK'}, {'name': 'Berlin', 'lat': 52.52, 'lng': 13.405, 'country': 'Germany'}, {'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964, 'country': 'Italy'}, {'name': 'Madrid', 'lat': 40.4168, 'lng': -3.7038, 'country': 'Spain'}, {'name': 'Vienna', 'lat': 48.2082, 'lng': 16.3738, 'country': 'Austria'}, {'name': 'Amsterdam', 'lat': 52.3676, 'lng': 4.9041, 'country': 'Netherlands'}, {'name': 'Prague', 'lat': 50.0755, 'lng': 14.4378, 'country': 'Czech Republic'}, {'name': 'Brussels', 'lat': 50.8503, 'lng': 4.3517, 'country': 'Belgium'}, {'name': 'Lisbon', 'lat': 38.7223, 'lng': -9.1393, 'country': 'Portugal'}, {'name': 'Barcelona', 'lat': 41.3851, 'lng': 2.1734, 'country': 'Spain'}, {'name': 'Budapest', 'lat': 47.4979, 'lng': 19.0402, 'country': 'Hungary'}, ]; List> get filteredPlaces { if (searchQuery.isEmpty) return allPlaces; final query = searchQuery.toLowerCase(); return allPlaces.where((place) { return place['name'].toString().toLowerCase().contains(query) || place['country'].toString().toLowerCase().contains(query); }).toList(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Filter by Search')), body: Column( children: [ // Search bar Container( padding: EdgeInsets.all(12), color: Colors.white, child: TextField( decoration: InputDecoration( hintText: 'Search cities or countries...', prefixIcon: Icon(Icons.search), suffixIcon: searchQuery.isNotEmpty ? IconButton( icon: Icon(Icons.clear), onPressed: () { setState(() { searchQuery = ''; }); }, ) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), contentPadding: EdgeInsets.symmetric(horizontal: 16), ), onChanged: (value) { setState(() { searchQuery = value; }); }, ), ), // Results count Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), color: Colors.grey[100], width: double.infinity, child: Text( '${filteredPlaces.length} of ${allPlaces.length} places shown', style: TextStyle(color: Colors.grey[600], fontSize: 12), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), markers: filteredPlaces.map((place) { return Marker( markerId: MarkerId(place['name']), position: LatLng(place['lat'], place['lng']), infoWindow: InfoWindow( title: place['name'], snippet: place['country'], ), ); }).toSet(), ), ), ], ), ); } } ``` ## Search with Results List Show a scrollable list below the search that also highlights on the map: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SearchWithListScreen extends StatefulWidget { @override _SearchWithListScreenState createState() => _SearchWithListScreenState(); } class _SearchWithListScreenState extends State { MapMetricsController? mapController; String searchQuery = ''; String? selectedId; final List> places = [ {'id': 'paris', 'name': 'Paris', 'lat': 48.8566, 'lng': 2.3522, 'type': 'Capital'}, {'id': 'london', 'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'type': 'Capital'}, {'id': 'berlin', 'name': 'Berlin', 'lat': 52.52, 'lng': 13.405, 'type': 'Capital'}, {'id': 'rome', 'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964, 'type': 'Capital'}, {'id': 'madrid', 'name': 'Madrid', 'lat': 40.4168, 'lng': -3.7038, 'type': 'Capital'}, {'id': 'barcelona', 'name': 'Barcelona', 'lat': 41.3851, 'lng': 2.1734, 'type': 'City'}, {'id': 'munich', 'name': 'Munich', 'lat': 48.1351, 'lng': 11.582, 'type': 'City'}, {'id': 'milan', 'name': 'Milan', 'lat': 45.4642, 'lng': 9.19, 'type': 'City'}, {'id': 'lyon', 'name': 'Lyon', 'lat': 45.764, 'lng': 4.8357, 'type': 'City'}, {'id': 'porto', 'name': 'Porto', 'lat': 41.1579, 'lng': -8.6291, 'type': 'City'}, ]; List> get filtered { if (searchQuery.isEmpty) return places; final q = searchQuery.toLowerCase(); return places.where((p) => p['name'].toString().toLowerCase().contains(q) || p['type'].toString().toLowerCase().contains(q)).toList(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Search & List')), body: Column( children: [ // Search field Padding( padding: EdgeInsets.all(12), child: TextField( decoration: InputDecoration( hintText: 'Search places...', prefixIcon: Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12)), ), onChanged: (v) => setState(() => searchQuery = v), ), ), // Map (takes 60% of space) Expanded( flex: 6, child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.0, 6.0), zoom: 4.0, ), markers: filtered.map((place) { final isSelected = place['id'] == selectedId; return Marker( markerId: MarkerId(place['id']), position: LatLng(place['lat'], place['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( isSelected ? BitmapDescriptor.hueBlue : BitmapDescriptor.hueRed, ), infoWindow: InfoWindow(title: place['name']), ); }).toSet(), ), ), // Results list (takes 40% of space) Expanded( flex: 4, child: ListView.builder( itemCount: filtered.length, itemBuilder: (context, index) { final place = filtered[index]; final isSelected = place['id'] == selectedId; return ListTile( leading: Icon( Icons.location_on, color: isSelected ? Colors.blue : Colors.grey, ), title: Text( place['name'], style: TextStyle( fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), subtitle: Text(place['type']), selected: isSelected, selectedTileColor: Colors.blue[50], onTap: () { setState(() { selectedId = place['id']; }); mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(place['lat'], place['lng']), 10.0, ), ); }, ); }, ), ), ], ), ); } } ``` ## Next Steps - [Filter Markers](./flutter-filter-markers) — Filter by category toggles - [Add Clusters](./flutter-add-a-cluster) — Group many markers - [Popup on Click](./flutter-popup-on-click) — Show details on tap --- **Tip**: For large datasets (100+ places), debounce the search input using a `Timer` so the map doesn't rebuild on every keystroke. A 300ms delay feels responsive while avoiding unnecessary work. --- # Filter Features by Toggle List in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-filter-by-toggle-list # Filter Features by Toggle List in Flutter This tutorial shows how to filter map markers using a toggle list of categories — like a checkbox or chip filter panel. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Chip-Based Category Filter Toggle categories on/off with filter chips: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ToggleFilterScreen extends StatefulWidget { @override _ToggleFilterScreenState createState() => _ToggleFilterScreenState(); } class _ToggleFilterScreenState extends State { MapMetricsController? mapController; final Map activeFilters = { 'Restaurant': true, 'Hotel': true, 'Museum': true, 'Park': true, }; final Map categoryColors = { 'Restaurant': Colors.red, 'Hotel': Colors.blue, 'Museum': Colors.purple, 'Park': Colors.green, }; final Map categoryHues = { 'Restaurant': BitmapDescriptor.hueRed, 'Hotel': BitmapDescriptor.hueBlue, 'Museum': BitmapDescriptor.hueViolet, 'Park': BitmapDescriptor.hueGreen, }; final List> places = [ {'name': 'Le Jules Verne', 'category': 'Restaurant', 'lat': 48.8580, 'lng': 2.2945}, {'name': 'Cafe de Flore', 'category': 'Restaurant', 'lat': 48.8540, 'lng': 2.3326}, {'name': 'Chez Janou', 'category': 'Restaurant', 'lat': 48.8570, 'lng': 2.3650}, {'name': 'Hotel Ritz', 'category': 'Hotel', 'lat': 48.8682, 'lng': 2.3285}, {'name': 'Hotel Lutetia', 'category': 'Hotel', 'lat': 48.8510, 'lng': 2.3268}, {'name': 'Hotel Plaza', 'category': 'Hotel', 'lat': 48.8716, 'lng': 2.3044}, {'name': 'Louvre', 'category': 'Museum', 'lat': 48.8606, 'lng': 2.3376}, {'name': 'Musee d\'Orsay', 'category': 'Museum', 'lat': 48.8600, 'lng': 2.3266}, {'name': 'Rodin Museum', 'category': 'Museum', 'lat': 48.8554, 'lng': 2.3158}, {'name': 'Luxembourg Gardens', 'category': 'Park', 'lat': 48.8462, 'lng': 2.3372}, {'name': 'Tuileries Garden', 'category': 'Park', 'lat': 48.8634, 'lng': 2.3275}, {'name': 'Champ de Mars', 'category': 'Park', 'lat': 48.8557, 'lng': 2.2986}, ]; List> get visiblePlaces { return places.where((p) => activeFilters[p['category']] == true).toList(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Filter by Category')), body: Column( children: [ // Filter chips Container( padding: EdgeInsets.all(12), color: Colors.white, child: Wrap( spacing: 8, children: activeFilters.keys.map((category) { final isActive = activeFilters[category]!; final color = categoryColors[category]!; return FilterChip( label: Text(category), selected: isActive, selectedColor: color.withOpacity(0.2), checkmarkColor: color, onSelected: (selected) { setState(() { activeFilters[category] = selected; }); }, ); }).toList(), ), ), // Count bar Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), color: Colors.grey[100], width: double.infinity, child: Text( '${visiblePlaces.length} of ${places.length} places shown', style: TextStyle(color: Colors.grey[600], fontSize: 12), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3300), zoom: 13.0, ), markers: visiblePlaces.map((place) { return Marker( markerId: MarkerId(place['name']), position: LatLng(place['lat'], place['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( categoryHues[place['category']]!, ), infoWindow: InfoWindow( title: place['name'], snippet: place['category'], ), ); }).toSet(), ), ), ], ), ); } } ``` ## Checkbox Toggle List with Counts Show categories in a collapsible drawer with checkbox toggles and counts: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CheckboxFilterScreen extends StatefulWidget { @override _CheckboxFilterScreenState createState() => _CheckboxFilterScreenState(); } class _CheckboxFilterScreenState extends State { MapMetricsController? mapController; bool showFilters = true; final Map filters = { 'Restaurant': true, 'Hotel': true, 'Museum': true, 'Park': true, 'Shop': true, }; final Map categoryIcons = { 'Restaurant': Icons.restaurant, 'Hotel': Icons.hotel, 'Museum': Icons.museum, 'Park': Icons.park, 'Shop': Icons.shopping_bag, }; final List> allPlaces = [ {'name': 'Bistro A', 'category': 'Restaurant', 'lat': 48.858, 'lng': 2.294}, {'name': 'Bistro B', 'category': 'Restaurant', 'lat': 48.854, 'lng': 2.333}, {'name': 'Hotel A', 'category': 'Hotel', 'lat': 48.868, 'lng': 2.328}, {'name': 'Hotel B', 'category': 'Hotel', 'lat': 48.851, 'lng': 2.327}, {'name': 'Louvre', 'category': 'Museum', 'lat': 48.861, 'lng': 2.338}, {'name': 'Orsay', 'category': 'Museum', 'lat': 48.860, 'lng': 2.327}, {'name': 'Pompidou', 'category': 'Museum', 'lat': 48.861, 'lng': 2.352}, {'name': 'Luxembourg', 'category': 'Park', 'lat': 48.846, 'lng': 2.337}, {'name': 'Tuileries', 'category': 'Park', 'lat': 48.863, 'lng': 2.328}, {'name': 'Galeries Lafayette', 'category': 'Shop', 'lat': 48.874, 'lng': 2.332}, {'name': 'Le Marais Shops', 'category': 'Shop', 'lat': 48.857, 'lng': 2.362}, ]; int _countCategory(String category) { return allPlaces.where((p) => p['category'] == category).length; } @override Widget build(BuildContext context) { final visible = allPlaces.where((p) => filters[p['category']]!).toList(); return Scaffold( appBar: AppBar( title: Text('Toggle Filter List'), actions: [ IconButton( icon: Icon(showFilters ? Icons.filter_list_off : Icons.filter_list), onPressed: () => setState(() => showFilters = !showFilters), ), TextButton( onPressed: () { setState(() { filters.updateAll((key, value) => true); }); }, child: Text('All', style: TextStyle(color: Colors.white)), ), TextButton( onPressed: () { setState(() { filters.updateAll((key, value) => false); }); }, child: Text('None', style: TextStyle(color: Colors.white)), ), ], ), body: Row( children: [ // Filter panel if (showFilters) Container( width: 180, color: Colors.white, child: ListView( children: filters.keys.map((category) { return CheckboxListTile( value: filters[category], onChanged: (val) { setState(() { filters[category] = val!; }); }, title: Row( children: [ Icon(categoryIcons[category], size: 18), SizedBox(width: 6), Expanded(child: Text(category, style: TextStyle(fontSize: 13))), ], ), subtitle: Text('${_countCategory(category)} places', style: TextStyle(fontSize: 11)), dense: true, controlAffinity: ListTileControlAffinity.leading, ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.858, 2.335), zoom: 13.5, ), markers: visible.map((place) { return Marker( markerId: MarkerId(place['name']), position: LatLng(place['lat'], place['lng']), infoWindow: InfoWindow( title: place['name'], snippet: place['category'], ), ); }).toSet(), ), ), ], ), ); } } ``` ## Next Steps - [Filter by Text Input](./flutter-filter-by-text-input) — Search-based filtering - [Filter Markers](./flutter-filter-markers) — Simple marker filtering - [Add Custom Icons with Markers](./flutter-add-custom-icons-markers) — Category-colored markers --- **Tip**: Use `FilterChip` for mobile-friendly category toggles, and `CheckboxListTile` for desktop-style sidebar filters. Combine both with `setState()` to instantly show/hide markers. --- # Filter Markers in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-filter-markers # Filter Markers in Flutter This tutorial shows how to filter which markers are displayed on the map based on categories, search text, or toggle buttons. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Filter by Category Toggle different categories of markers on and off: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FilterMarkersScreen extends StatefulWidget { @override _FilterMarkersScreenState createState() => _FilterMarkersScreenState(); } class _FilterMarkersScreenState extends State { MapMetricsController? mapController; // Active category filters Set activeFilters = {'restaurant', 'hotel', 'museum', 'park'}; // All places with categories final List> places = [ {'id': '1', 'name': 'Le Bistrot', 'category': 'restaurant', 'position': LatLng(48.858, 2.340)}, {'id': '2', 'name': 'Café de Flore', 'category': 'restaurant', 'position': LatLng(48.854, 2.332)}, {'id': '3', 'name': 'Grand Hotel', 'category': 'hotel', 'position': LatLng(48.870, 2.330)}, {'id': '4', 'name': 'Hotel Paris', 'category': 'hotel', 'position': LatLng(48.862, 2.350)}, {'id': '5', 'name': 'Louvre', 'category': 'museum', 'position': LatLng(48.861, 2.338)}, {'id': '6', 'name': 'Musée d\'Orsay', 'category': 'museum', 'position': LatLng(48.860, 2.326)}, {'id': '7', 'name': 'Luxembourg Gardens', 'category': 'park', 'position': LatLng(48.846, 2.337)}, {'id': '8', 'name': 'Tuileries Garden', 'category': 'park', 'position': LatLng(48.863, 2.327)}, ]; final Map categoryHues = { 'restaurant': BitmapDescriptor.hueOrange, 'hotel': BitmapDescriptor.hueBlue, 'museum': BitmapDescriptor.hueViolet, 'park': BitmapDescriptor.hueGreen, }; final Map categoryIcons = { 'restaurant': Icons.restaurant, 'hotel': Icons.hotel, 'museum': Icons.museum, 'park': Icons.park, }; Set get filteredMarkers => places .where((p) => activeFilters.contains(p['category'])) .map((place) => Marker( markerId: MarkerId(place['id']), position: place['position'], icon: BitmapDescriptor.defaultMarkerWithHue( categoryHues[place['category']] ?? BitmapDescriptor.hueRed, ), infoWindow: InfoWindow( title: place['name'], snippet: place['category'], ), )) .toSet(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Filter Markers')), body: Column( children: [ // Filter chips Container( padding: EdgeInsets.all(8), color: Colors.grey[100], child: Wrap( spacing: 8, children: categoryHues.keys.map((category) { final isActive = activeFilters.contains(category); return FilterChip( avatar: Icon( categoryIcons[category], size: 18, color: isActive ? Colors.white : Colors.grey, ), label: Text( '${category[0].toUpperCase()}${category.substring(1)}', ), selected: isActive, selectedColor: Colors.blue, labelStyle: TextStyle( color: isActive ? Colors.white : Colors.black87, ), onSelected: (selected) { setState(() { if (selected) { activeFilters.add(category); } else { activeFilters.remove(category); } }); }, ); }).toList(), ), ), // Count Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), color: Colors.grey[50], child: Row( children: [ Text( 'Showing ${filteredMarkers.length} of ${places.length} places', style: TextStyle(fontSize: 13, color: Colors.grey[600]), ), Spacer(), TextButton( onPressed: () => setState(() => activeFilters = {'restaurant', 'hotel', 'museum', 'park'}), child: Text('Show All'), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.858, 2.340), zoom: 14.0, ), markers: filteredMarkers, ), ), ], ), ); } } ``` ## Filter by Search Text Search markers by name in real time: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SearchFilterScreen extends StatefulWidget { @override _SearchFilterScreenState createState() => _SearchFilterScreenState(); } class _SearchFilterScreenState extends State { MapMetricsController? mapController; String searchQuery = ''; final List> places = [ {'id': '1', 'name': 'Eiffel Tower', 'position': LatLng(48.8584, 2.2945)}, {'id': '2', 'name': 'Louvre Museum', 'position': LatLng(48.8606, 2.3376)}, {'id': '3', 'name': 'Notre-Dame', 'position': LatLng(48.8530, 2.3499)}, {'id': '4', 'name': 'Sacré-Cœur', 'position': LatLng(48.8867, 2.3431)}, {'id': '5', 'name': 'Arc de Triomphe', 'position': LatLng(48.8738, 2.2950)}, {'id': '6', 'name': 'Luxembourg Gardens', 'position': LatLng(48.8462, 2.3372)}, {'id': '7', 'name': 'Moulin Rouge', 'position': LatLng(48.8841, 2.3322)}, {'id': '8', 'name': 'Musée d\'Orsay', 'position': LatLng(48.8600, 2.3266)}, ]; List> get filteredPlaces => searchQuery.isEmpty ? places : places .where((p) => p['name'].toLowerCase().contains(searchQuery.toLowerCase())) .toList(); Set get markers => filteredPlaces .map((place) => Marker( markerId: MarkerId(place['id']), position: place['position'], infoWindow: InfoWindow(title: place['name']), )) .toSet(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Search Places')), body: Column( children: [ // Search bar Padding( padding: EdgeInsets.all(8), child: TextField( decoration: InputDecoration( hintText: 'Search places...', prefixIcon: Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), ), contentPadding: EdgeInsets.symmetric(horizontal: 12), suffixIcon: searchQuery.isNotEmpty ? IconButton( icon: Icon(Icons.clear), onPressed: () => setState(() => searchQuery = ''), ) : null, ), onChanged: (value) => setState(() => searchQuery = value), ), ), // Results count Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Text( '${filteredPlaces.length} results', style: TextStyle(fontSize: 13, color: Colors.grey), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.860, 2.330), zoom: 13.0, ), markers: markers, ), ), ], ), ); } } ``` ## Next Steps - [Add Clusters](./flutter-add-a-cluster) — Group filtered markers into clusters - [Popup on Click](./flutter-popup-on-click) — Show details when tapping filtered markers - [Markers and Annotations](./flutter-markers) — Basic marker features --- **Tip**: For large datasets, use `Set` instead of `List` for markers and filter with `.where()` — Flutter's `MapMetrics` widget efficiently diffs the marker set on each rebuild. --- # Filter Features Within a Layer in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-filter-within-layer # Filter Features Within a Layer in Flutter This tutorial shows how to filter features within a single GeoJSON layer using expressions — showing or hiding features based on their properties without removing and re-adding the layer. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Filter by Property Value Show only features matching a specific property value using layer filters: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FilterWithinLayerScreen extends StatefulWidget { @override _FilterWithinLayerScreenState createState() => _FilterWithinLayerScreenState(); } class _FilterWithinLayerScreenState extends State { MapMetricsController? mapController; String selectedType = 'all'; final List filterOptions = ['all', 'capital', 'city', 'town']; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Filter Within Layer')), body: Column( children: [ // Filter chips Container( padding: EdgeInsets.all(12), child: Wrap( spacing: 8, children: filterOptions.map((option) { return ChoiceChip( label: Text(option == 'all' ? 'Show All' : option[0].toUpperCase() + option.substring(1)), selected: selectedType == option, onSelected: (selected) { if (selected) { setState(() => selectedType = option); _applyFilter(); } }, ); }).toList(), ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), onStyleLoaded: () { _addCitiesLayer(); }, ), ), ], ), ); } void _addCitiesLayer() { final geoJson = { 'type': 'FeatureCollection', 'features': [ {'type': 'Feature', 'properties': {'name': 'Paris', 'type': 'capital', 'pop': 2161}, 'geometry': {'type': 'Point', 'coordinates': [2.3522, 48.8566]}}, {'type': 'Feature', 'properties': {'name': 'London', 'type': 'capital', 'pop': 8982}, 'geometry': {'type': 'Point', 'coordinates': [-0.1276, 51.5074]}}, {'type': 'Feature', 'properties': {'name': 'Berlin', 'type': 'capital', 'pop': 3645}, 'geometry': {'type': 'Point', 'coordinates': [13.405, 52.52]}}, {'type': 'Feature', 'properties': {'name': 'Rome', 'type': 'capital', 'pop': 2873}, 'geometry': {'type': 'Point', 'coordinates': [12.4964, 41.9028]}}, {'type': 'Feature', 'properties': {'name': 'Barcelona', 'type': 'city', 'pop': 1621}, 'geometry': {'type': 'Point', 'coordinates': [2.1734, 41.3851]}}, {'type': 'Feature', 'properties': {'name': 'Munich', 'type': 'city', 'pop': 1472}, 'geometry': {'type': 'Point', 'coordinates': [11.582, 48.1351]}}, {'type': 'Feature', 'properties': {'name': 'Milan', 'type': 'city', 'pop': 1352}, 'geometry': {'type': 'Point', 'coordinates': [9.19, 45.4642]}}, {'type': 'Feature', 'properties': {'name': 'Lyon', 'type': 'city', 'pop': 516}, 'geometry': {'type': 'Point', 'coordinates': [4.8357, 45.764]}}, {'type': 'Feature', 'properties': {'name': 'Bruges', 'type': 'town', 'pop': 118}, 'geometry': {'type': 'Point', 'coordinates': [3.2247, 51.2093]}}, {'type': 'Feature', 'properties': {'name': 'Salzburg', 'type': 'town', 'pop': 155}, 'geometry': {'type': 'Point', 'coordinates': [13.055, 47.8095]}}, {'type': 'Feature', 'properties': {'name': 'Siena', 'type': 'town', 'pop': 54}, 'geometry': {'type': 'Point', 'coordinates': [11.3308, 43.3188]}}, ], }; mapController?.addGeoJsonSource('cities', geoJson); mapController?.addCircleLayer( 'cities-layer', 'cities', circleRadius: 8.0, circleColor: '#3b82f6', circleStrokeColor: '#ffffff', circleStrokeWidth: 2.0, ); mapController?.addSymbolLayer( 'cities-labels', 'cities', textField: '{name}', textSize: 12.0, textOffset: [0.0, 1.5], textAnchor: 'top', ); } void _applyFilter() { if (selectedType == 'all') { // Remove filter — show all features mapController?.setFilter('cities-layer', null); mapController?.setFilter('cities-labels', null); } else { // Apply property filter final filter = ['==', ['get', 'type'], selectedType]; mapController?.setFilter('cities-layer', filter); mapController?.setFilter('cities-labels', filter); } } } ``` ## Filter by Numeric Range Filter features by a numeric property like population: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class NumericFilterScreen extends StatefulWidget { @override _NumericFilterScreenState createState() => _NumericFilterScreenState(); } class _NumericFilterScreenState extends State { MapMetricsController? mapController; RangeValues populationRange = RangeValues(0, 10000); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Population Filter')), body: Column( children: [ // Range slider Container( padding: EdgeInsets.all(16), child: Column( children: [ Text( 'Population: ${populationRange.start.toInt()}K - ${populationRange.end.toInt()}K', style: TextStyle(fontWeight: FontWeight.bold), ), RangeSlider( values: populationRange, min: 0, max: 10000, divisions: 100, labels: RangeLabels( '${populationRange.start.toInt()}K', '${populationRange.end.toInt()}K', ), onChanged: (values) { setState(() => populationRange = values); _applyPopulationFilter(); }, ), ], ), ), Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), onStyleLoaded: () { _addCitiesWithPopulation(); }, ), ), ], ), ); } void _addCitiesWithPopulation() { final geoJson = { 'type': 'FeatureCollection', 'features': [ {'type': 'Feature', 'properties': {'name': 'London', 'pop': 8982}, 'geometry': {'type': 'Point', 'coordinates': [-0.1276, 51.5074]}}, {'type': 'Feature', 'properties': {'name': 'Berlin', 'pop': 3645}, 'geometry': {'type': 'Point', 'coordinates': [13.405, 52.52]}}, {'type': 'Feature', 'properties': {'name': 'Madrid', 'pop': 3223}, 'geometry': {'type': 'Point', 'coordinates': [-3.7038, 40.4168]}}, {'type': 'Feature', 'properties': {'name': 'Rome', 'pop': 2873}, 'geometry': {'type': 'Point', 'coordinates': [12.4964, 41.9028]}}, {'type': 'Feature', 'properties': {'name': 'Paris', 'pop': 2161}, 'geometry': {'type': 'Point', 'coordinates': [2.3522, 48.8566]}}, {'type': 'Feature', 'properties': {'name': 'Vienna', 'pop': 1897}, 'geometry': {'type': 'Point', 'coordinates': [16.3738, 48.2082]}}, {'type': 'Feature', 'properties': {'name': 'Barcelona', 'pop': 1621}, 'geometry': {'type': 'Point', 'coordinates': [2.1734, 41.3851]}}, {'type': 'Feature', 'properties': {'name': 'Munich', 'pop': 1472}, 'geometry': {'type': 'Point', 'coordinates': [11.582, 48.1351]}}, {'type': 'Feature', 'properties': {'name': 'Amsterdam', 'pop': 873}, 'geometry': {'type': 'Point', 'coordinates': [4.9041, 52.3676]}}, {'type': 'Feature', 'properties': {'name': 'Bruges', 'pop': 118}, 'geometry': {'type': 'Point', 'coordinates': [3.2247, 51.2093]}}, ], }; mapController?.addGeoJsonSource('pop-cities', geoJson); mapController?.addCircleLayer( 'pop-cities-layer', 'pop-cities', circleRadius: 8.0, circleColor: '#ef4444', circleStrokeColor: '#ffffff', circleStrokeWidth: 2.0, ); mapController?.addSymbolLayer( 'pop-cities-labels', 'pop-cities', textField: '{name}', textSize: 11.0, textOffset: [0.0, 1.5], textAnchor: 'top', ); } void _applyPopulationFilter() { final filter = [ 'all', ['>=', ['get', 'pop'], populationRange.start.toInt()], ['<=', ['get', 'pop'], populationRange.end.toInt()], ]; mapController?.setFilter('pop-cities-layer', filter); mapController?.setFilter('pop-cities-labels', filter); } } ``` ## Common Filter Expressions | Expression | Description | |------------|-------------| | `['==', ['get', 'type'], 'capital']` | Equals | | `['!=', ['get', 'type'], 'town']` | Not equals | | `['>=', ['get', 'pop'], 1000]` | Greater than or equal | | `['in', 'capital', ['get', 'type']]` | Value in list | | `['all', filter1, filter2]` | AND (both must match) | | `['any', filter1, filter2]` | OR (either matches) | | `null` | Remove filter (show all) | ## Next Steps - [Filter by Toggle List](./flutter-filter-by-toggle-list) — Category checkbox filters - [Filter by Text Input](./flutter-filter-by-text-input) — Search-based filtering - [Draw GeoJSON Points](./flutter-draw-geojson-points) — GeoJSON point rendering --- **Tip**: Layer filters are more performant than removing/re-adding features because the data stays in memory and the map only needs to re-render — not re-parse — the GeoJSON. --- # Fit to Bounding Box in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-fit-to-bounding-box # Fit to Bounding Box in Flutter This tutorial shows how to adjust the map camera to fit a set of coordinates or a bounding box within the visible viewport. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Fit to Bounds Fit the camera to show a specific rectangular area: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FitBoundsScreen extends StatefulWidget { @override _FitBoundsScreenState createState() => _FitBoundsScreenState(); } class _FitBoundsScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Fit to Bounds')), body: Column( children: [ // Region buttons SingleChildScrollView( scrollDirection: Axis.horizontal, padding: EdgeInsets.all(8), child: Row( children: [ _regionButton('Paris', LatLng(48.815, 2.225), LatLng(48.902, 2.470)), SizedBox(width: 8), _regionButton('Manhattan', LatLng(40.700, -74.020), LatLng(40.800, -73.930)), SizedBox(width: 8), _regionButton('Central London', LatLng(51.490, -0.180), LatLng(51.530, -0.070)), SizedBox(width: 8), _regionButton('Tokyo Center', LatLng(35.650, 139.700), LatLng(35.700, 139.780)), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 5.0, ), ), ), ], ), ); } Widget _regionButton(String label, LatLng southwest, LatLng northeast) { return ElevatedButton( onPressed: () => _fitToBounds(southwest, northeast), child: Text(label), ); } void _fitToBounds(LatLng southwest, LatLng northeast) { mapController?.animateCamera( CameraUpdate.newLatLngBounds( LatLngBounds(southwest: southwest, northeast: northeast), 50.0, // padding in pixels ), ); } } ``` ## Fit to Markers Automatically calculate bounds from a set of markers and zoom to show them all: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FitToMarkersScreen extends StatefulWidget { @override _FitToMarkersScreenState createState() => _FitToMarkersScreenState(); } class _FitToMarkersScreenState extends State { MapMetricsController? mapController; final List markerPositions = [ LatLng(48.8584, 2.2945), // Eiffel Tower LatLng(48.8606, 2.3376), // Louvre LatLng(48.8530, 2.3499), // Notre-Dame LatLng(48.8867, 2.3431), // Sacré-Cœur LatLng(48.8738, 2.2950), // Arc de Triomphe ]; Set get markers => markerPositions.asMap().entries.map((entry) { return Marker( markerId: MarkerId('marker_${entry.key}'), position: entry.value, infoWindow: InfoWindow(title: 'Point ${entry.key + 1}'), ); }).toSet(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Fit to Markers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) { mapController = controller; // Fit to all markers after map loads _fitToAllMarkers(); }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 10.0, ), markers: markers, ), floatingActionButton: FloatingActionButton( onPressed: _fitToAllMarkers, child: Icon(Icons.fit_screen), tooltip: 'Fit All Markers', ), ); } void _fitToAllMarkers() { if (markerPositions.isEmpty) return; // Calculate bounds from all marker positions double minLat = markerPositions.first.latitude; double maxLat = markerPositions.first.latitude; double minLng = markerPositions.first.longitude; double maxLng = markerPositions.first.longitude; for (final position in markerPositions) { if (position.latitude < minLat) minLat = position.latitude; if (position.latitude > maxLat) maxLat = position.latitude; if (position.longitude < minLng) minLng = position.longitude; if (position.longitude > maxLng) maxLng = position.longitude; } mapController?.animateCamera( CameraUpdate.newLatLngBounds( LatLngBounds( southwest: LatLng(minLat, minLng), northeast: LatLng(maxLat, maxLng), ), 60.0, // padding ), ); } } ``` ## Fit Bounds with Custom Padding Use different padding on each side: ```dart // Uniform padding mapController?.animateCamera( CameraUpdate.newLatLngBounds(bounds, 50.0), ); // If your SDK version supports asymmetric padding: mapController?.animateCamera( CameraUpdate.newLatLngBounds( bounds, 50.0, // This is applied equally on all sides ), ); ``` ## LatLngBounds Properties | Property | Type | Description | |----------|------|-------------| | `southwest` | `LatLng` | Bottom-left corner of the bounding box | | `northeast` | `LatLng` | Top-right corner of the bounding box | ## Next Steps - [Restrict Map Panning](./flutter-restrict-map-panning) — Prevent users from panning outside bounds - [Fly to a Location](./flutter-fly-to-location) — Animate camera to a single point - [Jump to Locations](./flutter-jump-to-locations) — Navigate through a series of locations --- **Tip**: Always add some padding (40–80 pixels) when fitting bounds so markers at the edges are not clipped by the screen border. --- # Fit Map to a LineString in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-fit-to-linestring # Fit Map to a LineString in Flutter This tutorial shows how to automatically zoom and pan the map so that an entire route or LineString fits within the visible area. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Fit to a Route Calculate the bounding box of a polyline and fit the camera to it: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:math'; class FitToLineStringScreen extends StatefulWidget { @override _FitToLineStringScreenState createState() => _FitToLineStringScreenState(); } class _FitToLineStringScreenState extends State { MapMetricsController? mapController; final List routePoints = [ LatLng(40.4168, -3.7038), // Madrid LatLng(41.3851, 2.1734), // Barcelona LatLng(43.2965, 5.3698), // Marseille LatLng(45.764, 4.8357), // Lyon LatLng(48.8566, 2.3522), // Paris ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Fit to LineString')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(44.0, 2.0), zoom: 4.0, ), onStyleLoaded: () { _fitToRoute(); }, polylines: { Polyline( polylineId: PolylineId('route'), points: routePoints, color: Colors.blue, width: 4, ), }, markers: { Marker( markerId: MarkerId('start'), position: routePoints.first, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueGreen), infoWindow: InfoWindow(title: 'Start: Madrid'), ), Marker( markerId: MarkerId('end'), position: routePoints.last, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueRed), infoWindow: InfoWindow(title: 'End: Paris'), ), }, ), floatingActionButton: FloatingActionButton( onPressed: _fitToRoute, child: Icon(Icons.fit_screen), tooltip: 'Fit to Route', ), ); } void _fitToRoute() { final bounds = _calculateBounds(routePoints); mapController?.animateCamera( CameraUpdate.newLatLngBounds(bounds, 60.0), // 60px padding ); } /// Calculate the bounding box for a list of points LatLngBounds _calculateBounds(List points) { double minLat = double.infinity; double maxLat = -double.infinity; double minLng = double.infinity; double maxLng = -double.infinity; for (final point in points) { minLat = min(minLat, point.latitude); maxLat = max(maxLat, point.latitude); minLng = min(minLng, point.longitude); maxLng = max(maxLng, point.longitude); } return LatLngBounds( southwest: LatLng(minLat, minLng), northeast: LatLng(maxLat, maxLng), ); } } ``` ## Multiple Routes with Fit Show several routes and fit the camera to the selected one: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:math'; class MultiRouteFitScreen extends StatefulWidget { @override _MultiRouteFitScreenState createState() => _MultiRouteFitScreenState(); } class _MultiRouteFitScreenState extends State { MapMetricsController? mapController; int selectedRoute = 0; final List> routes = [ { 'name': 'Spain to France', 'color': Colors.blue, 'points': [ LatLng(40.4168, -3.7038), // Madrid LatLng(41.3851, 2.1734), // Barcelona LatLng(43.2965, 5.3698), // Marseille LatLng(48.8566, 2.3522), // Paris ], }, { 'name': 'Germany to Italy', 'color': Colors.red, 'points': [ LatLng(52.52, 13.405), // Berlin LatLng(48.1351, 11.582), // Munich LatLng(47.2692, 11.4041), // Innsbruck LatLng(45.4642, 9.19), // Milan LatLng(41.9028, 12.4964), // Rome ], }, { 'name': 'UK to Scandinavia', 'color': Colors.green, 'points': [ LatLng(51.5074, -0.1276), // London LatLng(52.3676, 4.9041), // Amsterdam LatLng(53.5511, 9.9937), // Hamburg LatLng(55.6761, 12.5683), // Copenhagen LatLng(59.3293, 18.0686), // Stockholm ], }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Multi-Route Fit')), body: Column( children: [ // Route selector chips Container( padding: EdgeInsets.all(12), child: Wrap( spacing: 8, children: routes.asMap().entries.map((entry) { final i = entry.key; final route = entry.value; return ChoiceChip( label: Text(route['name']), selected: selectedRoute == i, selectedColor: (route['color'] as Color).withOpacity(0.3), onSelected: (selected) { if (selected) { setState(() => selectedRoute = i); _fitToSelectedRoute(); } }, ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 8.0), zoom: 4.0, ), onStyleLoaded: () { _fitToSelectedRoute(); }, polylines: routes.asMap().entries.map((entry) { final i = entry.key; final route = entry.value; return Polyline( polylineId: PolylineId('route_$i'), points: route['points'] as List, color: i == selectedRoute ? route['color'] as Color : (route['color'] as Color).withOpacity(0.3), width: i == selectedRoute ? 5 : 2, ); }).toSet(), ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: _fitToAll, child: Icon(Icons.zoom_out_map), tooltip: 'Fit All Routes', ), ); } void _fitToSelectedRoute() { final points = routes[selectedRoute]['points'] as List; final bounds = _calculateBounds(points); mapController?.animateCamera( CameraUpdate.newLatLngBounds(bounds, 60.0), ); } void _fitToAll() { final allPoints = []; for (final route in routes) { allPoints.addAll(route['points'] as List); } final bounds = _calculateBounds(allPoints); mapController?.animateCamera( CameraUpdate.newLatLngBounds(bounds, 60.0), ); } LatLngBounds _calculateBounds(List points) { double minLat = double.infinity; double maxLat = -double.infinity; double minLng = double.infinity; double maxLng = -double.infinity; for (final point in points) { minLat = min(minLat, point.latitude); maxLat = max(maxLat, point.latitude); minLng = min(minLng, point.longitude); maxLng = max(maxLng, point.longitude); } return LatLngBounds( southwest: LatLng(minLat, minLng), northeast: LatLng(maxLat, maxLng), ); } } ``` ## Next Steps - [Fit to Bounding Box](./flutter-fit-to-bounding-box) — Fit to a defined area - [Fly to a Location](./flutter-fly-to-location) — Smooth camera transitions - [Add a Polyline](./flutter-add-a-polyline) — Draw routes on the map --- **Tip**: Add padding (the second parameter in `newLatLngBounds`) to keep route endpoints visible and not hidden behind UI elements like bottom sheets or floating buttons. --- # Fly to a Location in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-fly-to-location # Fly to a Location in Flutter Smoothly animate the map camera to fly to any location. This is great for navigation UIs where you want the map to glide to a destination. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Fly To Use `animateCamera` with `CameraUpdate.newLatLngZoom` to fly to a location: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FlyToLocationScreen extends StatefulWidget { @override _FlyToLocationScreenState createState() => _FlyToLocationScreenState(); } class _FlyToLocationScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Fly to a Location')), body: Column( children: [ // Buttons row SingleChildScrollView( scrollDirection: Axis.horizontal, padding: EdgeInsets.all(8), child: Row( children: [ _buildLocationButton('New York', LatLng(40.7128, -74.0060)), SizedBox(width: 8), _buildLocationButton('London', LatLng(51.5074, -0.1276)), SizedBox(width: 8), _buildLocationButton('Tokyo', LatLng(35.6895, 139.6917)), SizedBox(width: 8), _buildLocationButton('Paris', LatLng(48.8566, 2.3522)), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 3.0, ), ), ), ], ), ); } Widget _buildLocationButton(String label, LatLng target) { return ElevatedButton( onPressed: () => _flyTo(target), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text(label), ); } void _flyTo(LatLng target) { mapController?.animateCamera( CameraUpdate.newLatLngZoom(target, 12.0), ); } } ``` Tap any button and the map will smoothly fly to that city. ## Fly To with Bearing and Tilt For a more dramatic fly-to effect, change the bearing (rotation) and tilt at the same time: ```dart void _flyToWithPerspective(LatLng target) { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: target, zoom: 15.0, bearing: 45.0, // Rotate 45 degrees tilt: 50.0, // Tilt for a 3D perspective ), ), ); } ``` ## Fly To with Custom Duration Control the animation speed by providing a duration: ```dart void _slowFlyTo(LatLng target) { mapController?.animateCamera( CameraUpdate.newLatLngZoom(target, 14.0), duration: Duration(seconds: 3), // Slow, cinematic flight ); } void _fastFlyTo(LatLng target) { mapController?.animateCamera( CameraUpdate.newLatLngZoom(target, 14.0), duration: Duration(milliseconds: 500), // Quick snap ); } ``` ## Complete Example: City Tour Build a city tour that automatically cycles through locations: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:async'; class CityTourScreen extends StatefulWidget { @override _CityTourScreenState createState() => _CityTourScreenState(); } class _CityTourScreenState extends State { MapMetricsController? mapController; Timer? tourTimer; int currentIndex = 0; bool isTourRunning = false; final List> cities = [ {'name': 'Paris', 'position': LatLng(48.8566, 2.3522)}, {'name': 'New York', 'position': LatLng(40.7128, -74.0060)}, {'name': 'Tokyo', 'position': LatLng(35.6895, 139.6917)}, {'name': 'Sydney', 'position': LatLng(-33.8688, 151.2093)}, {'name': 'Dubai', 'position': LatLng(25.2048, 55.2708)}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('City Tour'), actions: [ TextButton.icon( onPressed: _toggleTour, icon: Icon( isTourRunning ? Icons.stop : Icons.play_arrow, color: Colors.white, ), label: Text( isTourRunning ? 'Stop Tour' : 'Start Tour', style: TextStyle(color: Colors.white), ), ), ], ), body: Column( children: [ // City buttons SingleChildScrollView( scrollDirection: Axis.horizontal, padding: EdgeInsets.all(8), child: Row( children: cities.map((city) { final isActive = cities[currentIndex]['name'] == city['name']; return Padding( padding: EdgeInsets.only(right: 8), child: ElevatedButton( onPressed: () { setState(() { currentIndex = cities.indexOf(city); }); _flyTo(city['position']); }, style: ElevatedButton.styleFrom( backgroundColor: isActive ? Colors.blue : Colors.grey[300], foregroundColor: isActive ? Colors.white : Colors.black87, ), child: Text(city['name']), ), ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 3.0, ), ), ), ], ), ); } void _flyTo(LatLng target) { mapController?.animateCamera( CameraUpdate.newLatLngZoom(target, 12.0), ); } void _toggleTour() { if (isTourRunning) { tourTimer?.cancel(); setState(() { isTourRunning = false; }); } else { setState(() { isTourRunning = true; }); _flyTo(cities[currentIndex]['position']); tourTimer = Timer.periodic(Duration(seconds: 4), (timer) { setState(() { currentIndex = (currentIndex + 1) % cities.length; }); _flyTo(cities[currentIndex]['position']); }); } } @override void dispose() { tourTimer?.cancel(); mapController?.dispose(); super.dispose(); } } ``` ## Camera Update Methods | Method | Description | |--------|-------------| | `CameraUpdate.newLatLng(latlng)` | Move to a location, keep current zoom | | `CameraUpdate.newLatLngZoom(latlng, zoom)` | Move to a location with a specific zoom | | `CameraUpdate.newCameraPosition(position)` | Move with full control (target, zoom, bearing, tilt) | | `CameraUpdate.zoomIn()` | Zoom in by 1 level | | `CameraUpdate.zoomOut()` | Zoom out by 1 level | | `CameraUpdate.zoomTo(zoom)` | Zoom to a specific level | ## Next Steps - [Jump to Locations](./flutter-jump-to-locations) — Navigate through a series of locations - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Control 3D perspective - [Locate the User](./flutter-locate-user) — Fly to the user's current GPS position --- **Tip**: Use `animateCamera` for smooth transitions and `moveCamera` for instant jumps without animation. --- # Fly to Location on List Scroll in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-fly-to-on-list-scroll # Fly to Location on List Scroll in Flutter This tutorial shows how to sync the map with a scrollable list — as the user scrolls through locations, the map automatically flies to each one. Perfect for property listings, tour guides, and story maps. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Scroll-Synced Map Map flies to each location card as it becomes the active card: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ScrollSyncMapScreen extends StatefulWidget { @override _ScrollSyncMapScreenState createState() => _ScrollSyncMapScreenState(); } class _ScrollSyncMapScreenState extends State { MapMetricsController? mapController; final PageController _pageController = PageController(viewportFraction: 0.85); int activePage = 0; final List> locations = [ { 'name': 'Eiffel Tower', 'description': 'Iconic iron lattice tower built in 1889.', 'lat': 48.8584, 'lng': 2.2945, 'zoom': 16.0, 'color': Colors.blue, }, { 'name': 'Louvre Museum', 'description': 'World\'s largest art museum, home of the Mona Lisa.', 'lat': 48.8606, 'lng': 2.3376, 'zoom': 16.0, 'color': Colors.purple, }, { 'name': 'Notre-Dame', 'description': 'Medieval Catholic cathedral, a masterpiece of Gothic architecture.', 'lat': 48.8530, 'lng': 2.3499, 'zoom': 16.5, 'color': Colors.orange, }, { 'name': 'Sacre-Coeur', 'description': 'White-domed basilica atop Montmartre hill.', 'lat': 48.8867, 'lng': 2.3431, 'zoom': 16.0, 'color': Colors.red, }, { 'name': 'Arc de Triomphe', 'description': 'Monumental arch honoring those who fought for France.', 'lat': 48.8738, 'lng': 2.2950, 'zoom': 16.5, 'color': Colors.green, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Scroll-Synced Map')), body: Stack( children: [ // Map MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng( locations[0]['lat'], locations[0]['lng'], ), zoom: locations[0]['zoom'], ), markers: locations.asMap().entries.map((entry) { final i = entry.key; final loc = entry.value; return Marker( markerId: MarkerId(loc['name']), position: LatLng(loc['lat'], loc['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( i == activePage ? BitmapDescriptor.hueBlue : BitmapDescriptor.hueRed, ), infoWindow: InfoWindow(title: loc['name']), ); }).toSet(), ), // Progress indicator Positioned( top: 16, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(locations.length, (i) { return Container( width: i == activePage ? 24 : 8, height: 8, margin: EdgeInsets.symmetric(horizontal: 3), decoration: BoxDecoration( color: i == activePage ? Colors.blue : Colors.grey[400], borderRadius: BorderRadius.circular(4), ), ); }), ), ), // Scrollable cards at bottom Positioned( bottom: 24, left: 0, right: 0, height: 160, child: PageView.builder( controller: _pageController, itemCount: locations.length, onPageChanged: (index) { setState(() => activePage = index); final loc = locations[index]; mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(loc['lat'], loc['lng']), loc['zoom'], ), ); }, itemBuilder: (context, index) { final loc = locations[index]; final isActive = index == activePage; return AnimatedContainer( duration: Duration(milliseconds: 300), margin: EdgeInsets.symmetric( horizontal: 8, vertical: isActive ? 0 : 12, ), child: Card( elevation: isActive ? 8 : 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 12, height: 12, decoration: BoxDecoration( color: loc['color'], shape: BoxShape.circle, ), ), SizedBox(width: 8), Text( loc['name'], style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), SizedBox(height: 8), Expanded( child: Text( loc['description'], style: TextStyle(color: Colors.grey[600]), overflow: TextOverflow.ellipsis, maxLines: 3, ), ), Text( '${index + 1} of ${locations.length}', style: TextStyle( color: Colors.grey, fontSize: 12), ), ], ), ), ), ); }, ), ), ], ), ); } @override void dispose() { _pageController.dispose(); super.dispose(); } } ``` ## Vertical List Scroll Sync Use a vertical scrollable list instead of horizontal cards: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class VerticalScrollMapScreen extends StatefulWidget { @override _VerticalScrollMapScreenState createState() => _VerticalScrollMapScreenState(); } class _VerticalScrollMapScreenState extends State { MapMetricsController? mapController; int activeIndex = 0; final List> chapters = [ { 'title': 'Chapter 1: Arrival', 'text': 'Our journey begins at the Eiffel Tower, the symbol of Paris.', 'lat': 48.8584, 'lng': 2.2945, 'zoom': 16.0, 'bearing': 30.0, }, { 'title': 'Chapter 2: Art', 'text': 'Next, we visit the Louvre to see the world\'s greatest art collection.', 'lat': 48.8606, 'lng': 2.3376, 'zoom': 16.0, 'bearing': 120.0, }, { 'title': 'Chapter 3: History', 'text': 'Notre-Dame stands as a testament to medieval architecture.', 'lat': 48.8530, 'lng': 2.3499, 'zoom': 16.5, 'bearing': 220.0, }, { 'title': 'Chapter 4: Heights', 'text': 'We climb to Montmartre and Sacre-Coeur for a panoramic view.', 'lat': 48.8867, 'lng': 2.3431, 'zoom': 15.5, 'bearing': 0.0, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Story Map')), body: Row( children: [ // Story panel Container( width: MediaQuery.of(context).size.width * 0.4, child: ListView.builder( padding: EdgeInsets.all(16), itemCount: chapters.length, itemBuilder: (context, i) { final ch = chapters[i]; final isActive = i == activeIndex; return GestureDetector( onTap: () { setState(() => activeIndex = i); _flyToChapter(i); }, child: Container( margin: EdgeInsets.only(bottom: 16), padding: EdgeInsets.all(16), decoration: BoxDecoration( color: isActive ? Colors.blue[50] : Colors.white, border: Border.all( color: isActive ? Colors.blue : Colors.grey[300]!, width: isActive ? 2 : 1, ), borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(ch['title'], style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: isActive ? Colors.blue : Colors.black, )), SizedBox(height: 8), Text(ch['text'], style: TextStyle(color: Colors.grey[700])), ], ), ), ); }, ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(chapters[0]['lat'], chapters[0]['lng']), zoom: chapters[0]['zoom'], bearing: chapters[0]['bearing'], tilt: 45.0, ), markers: chapters.asMap().entries.map((e) { return Marker( markerId: MarkerId('ch_${e.key}'), position: LatLng(e.value['lat'], e.value['lng']), infoWindow: InfoWindow(title: e.value['title']), ); }).toSet(), ), ), ], ), ); } void _flyToChapter(int index) { final ch = chapters[index]; mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(ch['lat'], ch['lng']), zoom: ch['zoom'], bearing: ch['bearing'], tilt: 45.0, ), ), ); } } ``` ## Next Steps - [Jump to Locations](./flutter-jump-to-locations) — Quick location switching - [Fly to a Location](./flutter-fly-to-location) — Smooth camera transitions - [Customize Camera Animations](./flutter-customize-camera-animations) — Camera control --- **Tip**: Use `PageView` with `viewportFraction: 0.85` for horizontal cards — it shows a peek of the next card, hinting that users can swipe. For story-driven maps, use a vertical list with `tilt` and `bearing` changes for each chapter to create a cinematic experience. --- # Fullscreen Map in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-fullscreen-map # Fullscreen Map in Flutter This tutorial shows how to create a fullscreen map that fills the entire screen, removing the app bar and status bar for an immersive experience. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Fullscreen Map The simplest way — just use the `MapMetrics` widget as the entire body without an `AppBar`: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FullscreenMapScreen extends StatefulWidget { @override _FullscreenMapScreenState createState() => _FullscreenMapScreenState(); } class _FullscreenMapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), ); } } ``` ## True Fullscreen (Hide Status Bar) For a fully immersive experience, hide the system status bar and navigation bar: ```dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ImmersiveMapScreen extends StatefulWidget { @override _ImmersiveMapScreenState createState() => _ImmersiveMapScreenState(); } class _ImmersiveMapScreenState extends State { MapMetricsController? mapController; @override void initState() { super.initState(); // Hide status bar and navigation bar for true fullscreen SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } @override void dispose() { // Restore system UI when leaving the screen SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); mapController?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), ); } } ``` ## Toggle Fullscreen Mode Let users switch between normal and fullscreen views: ```dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ToggleFullscreenScreen extends StatefulWidget { @override _ToggleFullscreenScreenState createState() => _ToggleFullscreenScreenState(); } class _ToggleFullscreenScreenState extends State { MapMetricsController? mapController; bool isFullscreen = false; void _toggleFullscreen() { setState(() { isFullscreen = !isFullscreen; }); if (isFullscreen) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } else { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } } @override void dispose() { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); mapController?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: isFullscreen ? null : AppBar(title: Text('Map View')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), // Fullscreen toggle button Positioned( top: isFullscreen ? 40 : 8, right: 8, child: FloatingActionButton.small( onPressed: _toggleFullscreen, backgroundColor: Colors.white, child: Icon( isFullscreen ? Icons.fullscreen_exit : Icons.fullscreen, color: Colors.black87, ), ), ), ], ), ); } } ``` ## System UI Modes | Mode | Description | |------|-------------| | `SystemUiMode.edgeToEdge` | Normal mode — status bar and nav bar visible | | `SystemUiMode.immersive` | Fullscreen — swipe from edge to reveal bars | | `SystemUiMode.immersiveSticky` | Fullscreen — bars appear briefly on swipe, then fade | | `SystemUiMode.leanBack` | Fullscreen — tap anywhere to reveal bars | ## Next Steps - [Basic Map](./flutter-basic-map) — Map with standard UI controls - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — 3D perspective for an immersive look - [Fly to a Location](./flutter-fly-to-location) — Smooth camera animations --- **Tip**: Always restore `SystemUiMode.edgeToEdge` in `dispose()` so other screens in your app are not affected by the fullscreen mode. --- # Game-Style Map Controls in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-game-controls # Game-Style Map Controls in Flutter This tutorial shows how to create game-like controls for navigating the map — using on-screen buttons to pan, zoom, and rotate like a virtual joystick. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## D-Pad Controls Arrow buttons to pan the map in all directions plus zoom and rotate: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:async'; class GameControlsScreen extends StatefulWidget { @override _GameControlsScreenState createState() => _GameControlsScreenState(); } class _GameControlsScreenState extends State { MapMetricsController? mapController; CameraPosition? currentCamera; Timer? moveTimer; final double panStep = 0.002; // How far to move per step final double rotateStep = 5.0; // Degrees per step final double zoomStep = 0.5; @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ // Map (disable touch gestures for pure game-control feel) MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onCameraMove: (position) { currentCamera = position; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 15.0, tilt: 45.0, ), ), // D-Pad (bottom-left) Positioned( bottom: 40, left: 20, child: _buildDPad(), ), // Zoom & Rotate (bottom-right) Positioned( bottom: 40, right: 20, child: _buildActionButtons(), ), // Info overlay (top) Positioned( top: 50, left: 16, right: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.black.withOpacity(0.6), borderRadius: BorderRadius.circular(8), ), child: Text( 'Use the controls to navigate the map', textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 13), ), ), ), ], ), ); } Widget _buildDPad() { return Container( width: 150, height: 150, child: Stack( children: [ // Up Positioned( top: 0, left: 50, child: _dPadButton(Icons.arrow_drop_up, () => _pan(0, panStep)), ), // Down Positioned( bottom: 0, left: 50, child: _dPadButton(Icons.arrow_drop_down, () => _pan(0, -panStep)), ), // Left Positioned( top: 50, left: 0, child: _dPadButton(Icons.arrow_left, () => _pan(-panStep, 0)), ), // Right Positioned( top: 50, right: 0, child: _dPadButton(Icons.arrow_right, () => _pan(panStep, 0)), ), // Center dot Positioned( top: 55, left: 55, child: Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.grey[700], shape: BoxShape.circle, ), ), ), ], ), ); } Widget _dPadButton(IconData icon, VoidCallback action) { return GestureDetector( onTapDown: (_) { action(); // Continuous movement while held moveTimer = Timer.periodic(Duration(milliseconds: 100), (_) => action()); }, onTapUp: (_) => moveTimer?.cancel(), onTapCancel: () => moveTimer?.cancel(), child: Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.black.withOpacity(0.6), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: Colors.white, size: 30), ), ); } Widget _buildActionButtons() { return Column( children: [ // Zoom in _actionButton(Icons.add, Colors.blue, () => _zoom(zoomStep)), SizedBox(height: 8), // Zoom out _actionButton(Icons.remove, Colors.blue, () => _zoom(-zoomStep)), SizedBox(height: 16), // Rotate left _actionButton(Icons.rotate_left, Colors.orange, () => _rotate(-rotateStep)), SizedBox(height: 8), // Rotate right _actionButton(Icons.rotate_right, Colors.orange, () => _rotate(rotateStep)), SizedBox(height: 16), // Reset _actionButton(Icons.home, Colors.green, _resetView), ], ); } Widget _actionButton(IconData icon, Color color, VoidCallback onPressed) { return GestureDetector( onTapDown: (_) { onPressed(); moveTimer = Timer.periodic(Duration(milliseconds: 150), (_) => onPressed()); }, onTapUp: (_) => moveTimer?.cancel(), onTapCancel: () => moveTimer?.cancel(), child: Container( width: 48, height: 48, decoration: BoxDecoration( color: color.withOpacity(0.8), shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: Icon(icon, color: Colors.white), ), ); } void _pan(double dLng, double dLat) { if (currentCamera == null) return; final target = currentCamera!.target; mapController?.moveCamera( CameraUpdate.newLatLng( LatLng(target.latitude + dLat, target.longitude + dLng), ), ); } void _zoom(double delta) { if (currentCamera == null) return; final newZoom = (currentCamera!.zoom + delta).clamp(1.0, 20.0); mapController?.moveCamera(CameraUpdate.zoomTo(newZoom)); } void _rotate(double deltaBearing) { if (currentCamera == null) return; final newBearing = currentCamera!.bearing + deltaBearing; mapController?.moveCamera( CameraUpdate.newCameraPosition( CameraPosition( target: currentCamera!.target, zoom: currentCamera!.zoom, bearing: newBearing, tilt: currentCamera!.tilt, ), ), ); } void _resetView() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 15.0, bearing: 0, tilt: 45.0, ), ), ); } @override void dispose() { moveTimer?.cancel(); mapController?.dispose(); super.dispose(); } } ``` ## Controls Reference | Button | Action | |--------|--------| | Arrow Up | Pan north | | Arrow Down | Pan south | | Arrow Left | Pan west | | Arrow Right | Pan east | | + | Zoom in | | - | Zoom out | | Rotate Left | Rotate counter-clockwise | | Rotate Right | Rotate clockwise | | Home | Reset to starting view | ## Next Steps - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Manual camera perspective - [Navigation Controls](./flutter-navigation-controls) — Standard map controls - [Animate Camera Around Point](./flutter-animate-camera-around-point) — Automatic orbiting --- **Tip**: Use `GestureDetector.onTapDown` with a repeating `Timer` for continuous movement while the button is held down, giving a smooth game-like feel. --- # Get Coordinates on Tap in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-get-coordinates-on-tap # Get Coordinates on Tap in Flutter This tutorial shows how to get and display the latitude and longitude coordinates when the user taps on the map — the Flutter equivalent of getting mouse coordinates on web. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Tap Coordinates Display coordinates in a bar at the bottom of the screen: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TapCoordinatesScreen extends StatefulWidget { @override _TapCoordinatesScreenState createState() => _TapCoordinatesScreenState(); } class _TapCoordinatesScreenState extends State { MapMetricsController? mapController; LatLng? tappedPosition; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Tap for Coordinates')), body: Column( children: [ Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 5.0, ), onMapClick: (Point point, LatLng coordinates) { setState(() { tappedPosition = coordinates; }); }, ), ), // Coordinates bar Container( padding: EdgeInsets.all(16), color: Colors.grey[900], width: double.infinity, child: Text( tappedPosition != null ? 'Lat: ${tappedPosition!.latitude.toStringAsFixed(6)} ' 'Lng: ${tappedPosition!.longitude.toStringAsFixed(6)}' : 'Tap the map to get coordinates', style: TextStyle( color: Colors.white, fontFamily: 'monospace', fontSize: 14, ), textAlign: TextAlign.center, ), ), ], ), ); } } ``` ## Tap Coordinates with Marker Place a marker at the tapped location and show coordinates: ```dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TapMarkerCoordinatesScreen extends StatefulWidget { @override _TapMarkerCoordinatesScreenState createState() => _TapMarkerCoordinatesScreenState(); } class _TapMarkerCoordinatesScreenState extends State { MapMetricsController? mapController; LatLng? tappedPosition; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Tap Marker Coordinates')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 10.0, ), onMapClick: (Point point, LatLng coordinates) { setState(() { tappedPosition = coordinates; }); }, markers: tappedPosition != null ? { Marker( markerId: MarkerId('tapped'), position: tappedPosition!, infoWindow: InfoWindow( title: 'Tapped Location', snippet: '${tappedPosition!.latitude.toStringAsFixed(6)}, ' '${tappedPosition!.longitude.toStringAsFixed(6)}', ), ), } : {}, ), // Floating coordinate card if (tappedPosition != null) Positioned( top: 16, left: 16, right: 16, child: Card( elevation: 4, child: Padding( padding: EdgeInsets.all(12), child: Row( children: [ Icon(Icons.location_on, color: Colors.blue), SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Latitude: ${tappedPosition!.latitude.toStringAsFixed(6)}', style: TextStyle(fontFamily: 'monospace'), ), Text( 'Longitude: ${tappedPosition!.longitude.toStringAsFixed(6)}', style: TextStyle(fontFamily: 'monospace'), ), ], ), ), IconButton( icon: Icon(Icons.copy, size: 20), tooltip: 'Copy coordinates', onPressed: () { final text = '${tappedPosition!.latitude.toStringAsFixed(6)}, ' '${tappedPosition!.longitude.toStringAsFixed(6)}'; Clipboard.setData(ClipboardData(text: text)); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Coordinates copied!')), ); }, ), ], ), ), ), ), ], ), ); } } ``` ## Track Camera Center Coordinates Show the coordinates of the map center as the user pans: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CameraCenterScreen extends StatefulWidget { @override _CameraCenterScreenState createState() => _CameraCenterScreenState(); } class _CameraCenterScreenState extends State { MapMetricsController? mapController; double centerLat = 48.8566; double centerLng = 2.3522; double currentZoom = 10.0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Camera Center Tracker')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 10.0, ), onCameraMove: (CameraPosition position) { setState(() { centerLat = position.target.latitude; centerLng = position.target.longitude; currentZoom = position.zoom; }); }, ), // Crosshair at center Center( child: Icon(Icons.add, color: Colors.red, size: 24), ), // Info overlay Positioned( bottom: 16, left: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Center:', style: TextStyle(color: Colors.white70, fontSize: 11), ), Text( 'Lat: ${centerLat.toStringAsFixed(6)}', style: TextStyle( color: Colors.white, fontFamily: 'monospace', fontSize: 13, ), ), Text( 'Lng: ${centerLng.toStringAsFixed(6)}', style: TextStyle( color: Colors.white, fontFamily: 'monospace', fontSize: 13, ), ), Text( 'Zoom: ${currentZoom.toStringAsFixed(2)}', style: TextStyle( color: Colors.greenAccent, fontFamily: 'monospace', fontSize: 13, ), ), ], ), ), ), ], ), ); } } ``` ## Map Events Reference | Event | Callback | Description | |-------|----------|-------------| | Tap | `onMapClick` | User taps the map | | Long press | `onMapLongClick` | User long-presses the map | | Camera move | `onCameraMove` | Camera position changes | | Camera idle | `onCameraIdle` | Camera stops moving | ## Next Steps - [Locate the User](./flutter-locate-user) — Show user's GPS position - [Draggable Marker](./flutter-draggable-marker) — Drag a marker to get coordinates - [Measure Distances](./flutter-measure-distances) — Measure between tapped points --- **Tip**: Use `onCameraIdle` instead of `onCameraMove` if you only need the final position after panning — this avoids excessive rebuilds during fast panning. --- # Migrate from Google Maps to MapMetrics in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-google-map-migration # Migrate from Google Maps to MapMetrics in Flutter This guide helps you switch your existing Flutter app from `google_maps_flutter` to MapMetrics. The APIs are very similar, so migration is straightforward. ## Step 1: Update Dependencies Replace the Google Maps dependency in your `pubspec.yaml`: ```yaml # Before (Google Maps) dependencies: google_maps_flutter: ^2.5.0 # After (MapMetrics) dependencies: mapmetrics: ^1.0.6 ``` Then run: ```bash flutter pub get ``` ## Step 2: Update Imports ```dart // Before (Google Maps) import 'package:google_maps_flutter/google_maps_flutter.dart'; // After (MapMetrics) import 'package:mapmetrics/mapmetrics.dart'; ``` ## Step 3: Update the Map Widget The widget names and properties are very similar: ```dart // Before (Google Maps) GoogleMap( onMapCreated: (GoogleMapController controller) { _controller = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), markers: _markers, polylines: _polylines, polygons: _polygons, circles: _circles, myLocationEnabled: true, myLocationButtonEnabled: true, ) // After (MapMetrics) MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { _controller = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), markers: _markers, polylines: _polylines, polygons: _polygons, circles: _circles, myLocationEnabled: true, ) ``` **Key differences:** - `GoogleMap` → `MapMetrics` - `GoogleMapController` → `MapMetricsController` - You must provide a `styleUrl` (get it from [MapMetrics Portal](https://portal.mapmetrics.org)) ## Step 4: Get MapMetrics Credentials 1. Go to [portal.mapmetrics.org](https://portal.mapmetrics.org) and create an account 2. Create an **API Key** under the "Keys" section 3. Create a **Map Style** under the "Styles" section 4. Copy your style URL (format: `https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY`) ## API Comparison Table | Feature | Google Maps | MapMetrics | |---------|-------------|------------| | **Widget** | `GoogleMap` | `MapMetrics` | | **Controller** | `GoogleMapController` | `MapMetricsController` | | **Style/Theme** | `mapType: MapType.normal` | `styleUrl: '...'` | | **Camera Position** | `CameraPosition` | `CameraPosition` (same) | | **LatLng** | `LatLng(lat, lng)` | `LatLng(lat, lng)` (same) | | **Markers** | `Set` | `Set` (same) | | **Polylines** | `Set` | `Set` (same) | | **Polygons** | `Set` | `Set` (same) | | **Circles** | `Set` | `Set` (same) | | **Animate camera** | `animateCamera()` | `animateCamera()` (same) | | **Move camera** | `moveCamera()` | `moveCamera()` (same) | | **User location** | `myLocationEnabled` | `myLocationEnabled` (same) | | **Zoom gestures** | `zoomGesturesEnabled` | `zoomGesturesEnabled` (same) | | **API Key** | In AndroidManifest / AppDelegate | In style URL | ## What Stays the Same Most of your existing code can be reused directly: - `CameraPosition`, `LatLng`, `LatLngBounds` — identical - `Marker`, `MarkerId`, `InfoWindow` — identical - `Polyline`, `PolylineId` — identical - `Polygon`, `PolygonId` — identical - `Circle`, `CircleId` — identical - `CameraUpdate` methods — identical - `BitmapDescriptor` — identical - Gesture properties — identical ## What Changes | Change | Details | |--------|---------| | **No Google API key** | Replace with MapMetrics style URL | | **Custom styles** | Use MapMetrics Portal instead of Google Cloud Console | | **Map types** | Use different style URLs instead of `MapType` enum | | **No Android API key in manifest** | Remove `com.google.android.geo.API_KEY` from AndroidManifest | | **No iOS API key in AppDelegate** | Remove `GMSServices.provideAPIKey` from AppDelegate | | **Community data** | MapMetrics uses community-contributed map data | ## Remove Google Maps Config ### Android Remove from `android/app/src/main/AndroidManifest.xml`: ```xml ``` ### iOS Remove from `ios/Runner/AppDelegate.swift`: ```swift // Remove this GMSServices.provideAPIKey("YOUR_GOOGLE_API_KEY") ``` ## Complete Migration Example ```dart // Before: Google Maps import 'package:google_maps_flutter/google_maps_flutter.dart'; class MyMapScreen extends StatefulWidget { @override _MyMapScreenState createState() => _MyMapScreenState(); } class _MyMapScreenState extends State { GoogleMapController? _controller; @override Widget build(BuildContext context) { return GoogleMap( onMapCreated: (controller) => _controller = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13, ), markers: { Marker( markerId: MarkerId('paris'), position: LatLng(48.8566, 2.3522), infoWindow: InfoWindow(title: 'Paris'), ), }, ); } } ``` ```dart // After: MapMetrics (minimal changes!) import 'package:mapmetrics/mapmetrics.dart'; class MyMapScreen extends StatefulWidget { @override _MyMapScreenState createState() => _MyMapScreenState(); } class _MyMapScreenState extends State { MapMetricsController? _controller; @override Widget build(BuildContext context) { return MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => _controller = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13, ), markers: { Marker( markerId: MarkerId('paris'), position: LatLng(48.8566, 2.3522), infoWindow: InfoWindow(title: 'Paris'), ), }, ); } } ``` ## Why Switch to MapMetrics? - **No usage fees** — No per-load or per-request charges - **Custom styles** — Full control over map appearance via the Portal - **Community data** — Maps updated with community contributions - **No vendor lock-in** — Open-source based solution - **Simple setup** — No platform-specific API key configuration ## Next Steps - [Flutter Setup Guide](./flutter-setup) — Full setup walkthrough - [Basic Map](./flutter-basic-map) — Get your first map running - [Custom Styling](./flutter-custom-styling) — Create custom map styles --- **Tip**: The migration is mostly a find-and-replace operation. The biggest change is adding the `styleUrl` parameter and removing Google-specific API key configuration. --- # Draw a Gradient Line in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-gradient-line # Draw a Gradient Line in Flutter This tutorial shows how to draw a line with a color gradient along its length — useful for showing elevation, speed, or progress along a route. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Gradient Line Using Multiple Segments Since Flutter map polylines use a single color, we simulate a gradient by breaking the route into short segments with progressively changing colors: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GradientLineScreen extends StatefulWidget { @override _GradientLineScreenState createState() => _GradientLineScreenState(); } class _GradientLineScreenState extends State { MapMetricsController? mapController; // Route through European capitals final List routePoints = [ LatLng(40.4168, -3.7038), // Madrid LatLng(48.8566, 2.3522), // Paris LatLng(52.52, 13.405), // Berlin LatLng(48.2082, 16.3738), // Vienna LatLng(41.0082, 28.9784), // Istanbul ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Gradient Line')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(47.0, 12.0), zoom: 4.0, ), polylines: _buildGradientPolylines(), ), ); } /// Build a set of polyline segments that form a gradient Set _buildGradientPolylines() { final polylines = {}; final segmentCount = routePoints.length - 1; for (int i = 0; i < segmentCount; i++) { // Calculate color at this position (blue -> purple -> red) final t = i / segmentCount; final color = Color.lerp(Colors.blue, Colors.red, t)!; polylines.add( Polyline( polylineId: PolylineId('gradient_$i'), points: [routePoints[i], routePoints[i + 1]], color: color, width: 5, ), ); } return polylines; } } ``` ## Smooth Gradient with Interpolated Points For a smoother gradient, interpolate extra points between waypoints: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SmoothGradientLineScreen extends StatefulWidget { @override _SmoothGradientLineScreenState createState() => _SmoothGradientLineScreenState(); } class _SmoothGradientLineScreenState extends State { MapMetricsController? mapController; final List waypoints = [ LatLng(40.4168, -3.7038), // Madrid LatLng(48.8566, 2.3522), // Paris LatLng(52.52, 13.405), // Berlin LatLng(48.2082, 16.3738), // Vienna LatLng(41.0082, 28.9784), // Istanbul ]; /// Interpolate extra points between waypoints for a smoother gradient List _interpolate(List points, int stepsPerSegment) { final result = []; for (int i = 0; i < points.length - 1; i++) { final from = points[i]; final to = points[i + 1]; for (int s = 0; s < stepsPerSegment; s++) { final t = s / stepsPerSegment; result.add(LatLng( from.latitude + (to.latitude - from.latitude) * t, from.longitude + (to.longitude - from.longitude) * t, )); } } result.add(points.last); return result; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Smooth Gradient Line')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(47.0, 12.0), zoom: 4.0, ), polylines: _buildSmoothGradient(), ), ); } Set _buildSmoothGradient() { final smoothPoints = _interpolate(waypoints, 20); final polylines = {}; for (int i = 0; i < smoothPoints.length - 1; i++) { final t = i / (smoothPoints.length - 1); final color = Color.lerp(Colors.green, Colors.red, t)!; polylines.add( Polyline( polylineId: PolylineId('smooth_$i'), points: [smoothPoints[i], smoothPoints[i + 1]], color: color, width: 5, ), ); } return polylines; } } ``` ## Elevation-Based Gradient Color the line based on simulated elevation data: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ElevationGradientScreen extends StatefulWidget { @override _ElevationGradientScreenState createState() => _ElevationGradientScreenState(); } class _ElevationGradientScreenState extends State { MapMetricsController? mapController; // Points with simulated elevation data (meters) final List> routeWithElevation = [ {'lat': 48.8584, 'lng': 2.2945, 'elevation': 30}, // Eiffel Tower {'lat': 48.8620, 'lng': 2.3100, 'elevation': 45}, {'lat': 48.8650, 'lng': 2.3200, 'elevation': 80}, {'lat': 48.8700, 'lng': 2.3300, 'elevation': 60}, {'lat': 48.8750, 'lng': 2.3350, 'elevation': 100}, {'lat': 48.8800, 'lng': 2.3400, 'elevation': 130}, // Montmartre hill {'lat': 48.8867, 'lng': 2.3431, 'elevation': 130}, // Sacre-Coeur ]; /// Map elevation to a color (green = low, yellow = medium, red = high) Color _elevationColor(int elevation) { final minElev = 30.0; final maxElev = 130.0; final t = ((elevation - minElev) / (maxElev - minElev)).clamp(0.0, 1.0); if (t < 0.5) { return Color.lerp(Colors.green, Colors.yellow, t * 2)!; } else { return Color.lerp(Colors.yellow, Colors.red, (t - 0.5) * 2)!; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Elevation Gradient')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8700, 2.3200), zoom: 14.0, ), polylines: _buildElevationPolylines(), ), // Legend Positioned( bottom: 16, left: 16, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Elevation', style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(height: 4), _legendRow(Colors.green, 'Low (30m)'), _legendRow(Colors.yellow, 'Medium (80m)'), _legendRow(Colors.red, 'High (130m)'), ], ), ), ), ], ), ); } Widget _legendRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 2), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container(width: 20, height: 4, color: color), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 12)), ], ), ); } Set _buildElevationPolylines() { final polylines = {}; for (int i = 0; i < routeWithElevation.length - 1; i++) { final from = routeWithElevation[i]; final to = routeWithElevation[i + 1]; final avgElevation = ((from['elevation'] + to['elevation']) / 2).round(); polylines.add( Polyline( polylineId: PolylineId('elev_$i'), points: [ LatLng(from['lat'], from['lng']), LatLng(to['lat'], to['lng']), ], color: _elevationColor(avgElevation), width: 6, ), ); } return polylines; } } ``` ## Gradient Techniques Comparison | Technique | Segments | Smoothness | Performance | |-----------|----------|------------|-------------| | Waypoint segments | Few (4-10) | Visible steps | Best | | Interpolated (20/seg) | Many (80-200) | Smooth | Good | | Interpolated (50/seg) | Very many (200+) | Very smooth | Moderate | ## Next Steps - [Add a GeoJSON Line](./flutter-add-geojson-line) — Simple line from GeoJSON - [Add a Polyline](./flutter-add-a-polyline) — Basic polyline drawing - [Animate a Line](./flutter-animate-a-line) — Progressively draw a line --- **Tip**: Use `Color.lerp()` to blend between any two colors. For multi-stop gradients (e.g., green -> yellow -> red), split the `t` value into ranges and lerp between adjacent colors. --- # Hexagon Layer — Data Aggregation in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-hexagon-layer # Hexagon Layer — Data Aggregation in Flutter This tutorial shows how to create a hexagonal grid to aggregate and visualize point data — useful for density maps, analytics dashboards, and spatial analysis. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Hexagon Grid Create a hexagonal grid overlay and color cells by point count: ```dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class HexagonLayerScreen extends StatefulWidget { @override _HexagonLayerScreenState createState() => _HexagonLayerScreenState(); } class _HexagonLayerScreenState extends State { MapMetricsController? mapController; // Sample data points (e.g., reported incidents, sightings) final List dataPoints = [ LatLng(48.860, 2.340), LatLng(48.861, 2.342), LatLng(48.859, 2.341), LatLng(48.862, 2.343), LatLng(48.858, 2.339), LatLng(48.860, 2.341), LatLng(48.855, 2.350), LatLng(48.856, 2.352), LatLng(48.854, 2.349), LatLng(48.870, 2.330), LatLng(48.871, 2.331), LatLng(48.845, 2.360), LatLng(48.846, 2.361), LatLng(48.847, 2.359), LatLng(48.844, 2.362), LatLng(48.845, 2.358), LatLng(48.846, 2.360), LatLng(48.846, 2.363), LatLng(48.843, 2.361), LatLng(48.865, 2.320), LatLng(48.850, 2.310), LatLng(48.851, 2.311), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hexagon Layer')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.855, 2.340), zoom: 13.0, ), polygons: _buildHexagons(), // Show original data points as small markers circles: dataPoints.asMap().entries.map((e) { return Circle( circleId: CircleId('point_${e.key}'), center: e.value, radius: 30, fillColor: Colors.black.withOpacity(0.5), strokeWidth: 0, ); }).toSet(), ), // Legend Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('Density', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12)), SizedBox(height: 4), _legendRow(Colors.green.withOpacity(0.3), '1-2 points'), _legendRow(Colors.yellow.withOpacity(0.4), '3-4 points'), _legendRow(Colors.orange.withOpacity(0.5), '5-6 points'), _legendRow(Colors.red.withOpacity(0.6), '7+ points'), ], ), ), ), ), ], ), ); } Widget _legendRow(Color color, String label) { return Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container(width: 16, height: 16, color: color), SizedBox(width: 6), Text(label, style: TextStyle(fontSize: 11)), ], ), ); } /// Build hexagonal polygons and color them by data density Set _buildHexagons() { final hexSize = 0.005; // Size of hexagon in degrees final minLat = 48.840; final maxLat = 48.875; final minLng = 2.300; final maxLng = 2.370; final hexagons = {}; int hexIndex = 0; for (double lat = minLat; lat < maxLat; lat += hexSize * 1.5) { for (double lng = minLng; lng < maxLng; lng += hexSize * 1.732) { // Offset every other row final rowOffset = ((lat - minLat) / (hexSize * 1.5)).round() % 2 == 1 ? hexSize * 0.866 : 0.0; final centerLat = lat; final centerLng = lng + rowOffset; // Count points in this hexagon final count = _countPointsInHex(centerLat, centerLng, hexSize); if (count == 0) continue; // Generate hexagon vertices final vertices = _hexagonVertices(centerLat, centerLng, hexSize); hexagons.add( Polygon( polygonId: PolygonId('hex_$hexIndex'), points: vertices, fillColor: _densityColor(count), strokeColor: _densityColor(count).withOpacity(0.8), strokeWidth: 1, ), ); hexIndex++; } } return hexagons; } List _hexagonVertices(double lat, double lng, double size) { final vertices = []; for (int i = 0; i < 6; i++) { final angle = (60 * i - 30) * pi / 180; vertices.add(LatLng( lat + size * sin(angle), lng + size * cos(angle), )); } vertices.add(vertices.first); // Close the polygon return vertices; } int _countPointsInHex(double lat, double lng, double size) { int count = 0; for (final point in dataPoints) { final dist = sqrt( pow(point.latitude - lat, 2) + pow(point.longitude - lng, 2), ); if (dist < size) count++; } return count; } Color _densityColor(int count) { if (count <= 2) return Colors.green.withOpacity(0.3); if (count <= 4) return Colors.yellow.withOpacity(0.4); if (count <= 6) return Colors.orange.withOpacity(0.5); return Colors.red.withOpacity(0.6); } } ``` ## Interactive Hexagon with Tap Details Tap a hexagon to see its data count: ```dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class InteractiveHexScreen extends StatefulWidget { @override _InteractiveHexScreenState createState() => _InteractiveHexScreenState(); } class _InteractiveHexScreenState extends State { MapMetricsController? mapController; String? selectedHexId; int selectedCount = 0; final List dataPoints = List.generate(50, (i) { final random = Random(i); return LatLng( 48.840 + random.nextDouble() * 0.035, 2.300 + random.nextDouble() * 0.070, ); }); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Interactive Hexagons')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.855, 2.340), zoom: 13.0, ), polygons: _buildInteractiveHexagons(), ), // Selected hex info if (selectedHexId != null) Positioned( top: 16, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Text( 'Hexagon contains $selectedCount data points', style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), ), ), ), ], ), ); } Set _buildInteractiveHexagons() { final hexSize = 0.005; final hexagons = {}; int idx = 0; for (double lat = 48.840; lat < 48.875; lat += hexSize * 1.5) { for (double lng = 2.300; lng < 2.370; lng += hexSize * 1.732) { final rowOff = ((lat - 48.840) / (hexSize * 1.5)).round() % 2 == 1 ? hexSize * 0.866 : 0.0; final cLat = lat; final cLng = lng + rowOff; int count = 0; for (final p in dataPoints) { if (sqrt(pow(p.latitude - cLat, 2) + pow(p.longitude - cLng, 2)) < hexSize) count++; } if (count == 0) { idx++; continue; } final hexId = 'hex_$idx'; final isSelected = hexId == selectedHexId; final vertices = []; for (int i = 0; i < 6; i++) { final a = (60 * i - 30) * pi / 180; vertices.add(LatLng(cLat + hexSize * sin(a), cLng + hexSize * cos(a))); } vertices.add(vertices.first); hexagons.add(Polygon( polygonId: PolygonId(hexId), points: vertices, fillColor: isSelected ? Colors.blue.withOpacity(0.6) : _densityColor(count), strokeColor: isSelected ? Colors.blue : Colors.grey, strokeWidth: isSelected ? 3 : 1, consumeTapEvents: true, onTap: () { setState(() { selectedHexId = hexId; selectedCount = count; }); }, )); idx++; } } return hexagons; } Color _densityColor(int count) { if (count <= 2) return Colors.green.withOpacity(0.3); if (count <= 5) return Colors.yellow.withOpacity(0.4); return Colors.red.withOpacity(0.5); } } ``` ## Next Steps - [Add a Heatmap](./flutter-add-a-heatmap) — Continuous density visualization - [Add Clusters](./flutter-add-a-cluster) — Point clustering - [Draw GeoJSON Points](./flutter-draw-geojson-points) — Point rendering --- **Tip**: Hexagons are better than squares for spatial aggregation because they have uniform neighbor distances and avoid the visual bias of grid alignment. Adjust `hexSize` based on your zoom level and data density. --- # Map Interactions with Flutter and MapMetrics https://docs.mapatlas.xyz/sdk/examples/flutter-interactions # Map Interactions with Flutter and MapMetrics This tutorial will show you how to handle various map interactions, events, and user gestures in your Flutter MapMetrics applications. ## Basic Map Interactions Here's a comprehensive example of handling different map interactions: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MapInteractionsScreen extends StatefulWidget { @override _MapInteractionsScreenState createState() => _MapInteractionsScreenState(); } class _MapInteractionsScreenState extends State { MapMetricsController? mapController; LatLng? lastTappedLocation; LatLng? lastLongPressedLocation; CameraPosition? currentCameraPosition; bool isMapMoving = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Map Interactions'), actions: [ IconButton( icon: Icon(Icons.info), onPressed: _showInteractionInfo, ), ], ), body: Column( children: [ // Interaction status panel Container( padding: EdgeInsets.all(16), color: Colors.grey[100], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Map Status', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text('Moving: ${isMapMoving ? "Yes" : "No"}'), if (currentCameraPosition != null) Text('Zoom: ${currentCameraPosition!.zoom.toStringAsFixed(2)}'), if (lastTappedLocation != null) Text('Last Tap: ${lastTappedLocation!.latitude.toStringAsFixed(4)}, ${lastTappedLocation!.longitude.toStringAsFixed(4)}'), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { setState(() { mapController = controller; }); }, onMapClick: (Point point, LatLng coordinates) { setState(() { lastTappedLocation = coordinates; }); _handleMapClick(coordinates); }, onMapLongClick: (Point point, LatLng coordinates) { setState(() { lastLongPressedLocation = coordinates; }); _handleMapLongClick(coordinates); }, onCameraMove: (CameraPosition position) { setState(() { currentCameraPosition = position; isMapMoving = true; }); }, onCameraIdle: () { setState(() { isMapMoving = false; }); _handleCameraIdle(); }, onStyleLoaded: () { print('Map style loaded successfully!'); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), ), ], ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton.small( heroTag: "reset", onPressed: _resetMap, child: Icon(Icons.refresh), ), SizedBox(height: 8), FloatingActionButton.small( heroTag: "fit", onPressed: _fitToBounds, child: Icon(Icons.fit_screen), ), ], ), ); } void _handleMapClick(LatLng coordinates) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Map Clicked'), content: Text( 'Latitude: ${coordinates.latitude.toStringAsFixed(6)}\n' 'Longitude: ${coordinates.longitude.toStringAsFixed(6)}', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), TextButton( onPressed: () { Navigator.pop(context); _addMarkerAtLocation(coordinates); }, child: Text('Add Marker'), ), ], ), ); } void _handleMapLongClick(LatLng coordinates) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Map Long Pressed'), content: Text( 'Latitude: ${coordinates.latitude.toStringAsFixed(6)}\n' 'Longitude: ${coordinates.longitude.toStringAsFixed(6)}', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), TextButton( onPressed: () { Navigator.pop(context); _flyToLocation(coordinates); }, child: Text('Fly To'), ), ], ), ); } void _handleCameraIdle() { print('Camera stopped moving'); // You can perform actions when the map stops moving // For example, load data for the current viewport } void _addMarkerAtLocation(LatLng coordinates) { // Implementation for adding a marker print('Adding marker at: $coordinates'); } void _flyToLocation(LatLng coordinates) { mapController?.animateCamera( CameraUpdate.newLatLngZoom(coordinates, 15.0), ); } void _resetMap() { mapController?.animateCamera( CameraUpdate.newLatLngZoom(LatLng(40.7128, -74.0060), 12.0), ); } void _fitToBounds() { mapController?.animateCamera( CameraUpdate.newLatLngBounds( LatLngBounds( southwest: LatLng(40.7, -74.1), northeast: LatLng(40.8, -73.9), ), 50.0, ), ); } void _showInteractionInfo() { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Interaction Guide'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('• Tap: Show location info'), Text('• Long Press: Fly to location'), Text('• Drag: Pan the map'), Text('• Pinch: Zoom in/out'), Text('• Double Tap: Zoom in'), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ), ); } } ``` ## Gesture Handling ### Custom Gesture Recognition ```dart class GestureHandlingScreen extends StatefulWidget { @override _GestureHandlingScreenState createState() => _GestureHandlingScreenState(); } class _GestureHandlingScreenState extends State { MapMetricsController? mapController; bool isGestureEnabled = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Gesture Handling'), actions: [ Switch( value: isGestureEnabled, onChanged: (value) { setState(() { isGestureEnabled = value; }); _toggleGestures(); }, ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onMapClick: isGestureEnabled ? (point, coordinates) { _handleClick(coordinates); } : null, onMapLongClick: isGestureEnabled ? (point, coordinates) { _handleLongClick(coordinates); } : null, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), ); } void _toggleGestures() { if (isGestureEnabled) { print('Gestures enabled'); } else { print('Gestures disabled'); } } void _handleClick(LatLng coordinates) { print('Map clicked at: $coordinates'); } void _handleLongClick(LatLng coordinates) { print('Map long clicked at: $coordinates'); } } ``` ## Camera Controls ### Advanced Camera Operations ```dart class CameraControlsScreen extends StatefulWidget { @override _CameraControlsScreenState createState() => _CameraControlsScreenState(); } class _CameraControlsScreenState extends State { MapMetricsController? mapController; CameraPosition? currentPosition; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Camera Controls')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onCameraMove: (position) { setState(() { currentPosition = position; }); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), // Camera controls overlay Positioned( top: 20, right: 20, child: Column( children: [ FloatingActionButton.small( heroTag: "zoomIn", onPressed: _zoomIn, child: Icon(Icons.add), ), SizedBox(height: 8), FloatingActionButton.small( heroTag: "zoomOut", onPressed: _zoomOut, child: Icon(Icons.remove), ), SizedBox(height: 8), FloatingActionButton.small( heroTag: "rotate", onPressed: _rotateMap, child: Icon(Icons.rotate_right), ), SizedBox(height: 8), FloatingActionButton.small( heroTag: "tilt", onPressed: _tiltMap, child: Icon(Icons.view_in_ar), ), ], ), ), // Camera info overlay Positioned( bottom: 20, left: 20, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (currentPosition != null) ...[ Text('Zoom: ${currentPosition!.zoom.toStringAsFixed(2)}'), Text('Bearing: ${currentPosition!.bearing.toStringAsFixed(1)}°'), Text('Tilt: ${currentPosition!.tilt.toStringAsFixed(1)}°'), ], ], ), ), ), ], ), ); } void _zoomIn() { mapController?.animateCamera(CameraUpdate.zoomIn()); } void _zoomOut() { mapController?.animateCamera(CameraUpdate.zoomOut()); } void _rotateMap() { final currentBearing = currentPosition?.bearing ?? 0.0; final newBearing = (currentBearing + 45) % 360; mapController?.animateCamera( CameraUpdate.rotateBy(newBearing - currentBearing), ); } void _tiltMap() { final currentTilt = currentPosition?.tilt ?? 0.0; final newTilt = currentTilt > 0 ? 0.0 : 45.0; mapController?.animateCamera( CameraUpdate.tiltTo(newTilt), ); } } ``` ## Event Handling ### Comprehensive Event Management ```dart class EventHandlingScreen extends StatefulWidget { @override _EventHandlingScreenState createState() => _EventHandlingScreenState(); } class _EventHandlingScreenState extends State { MapMetricsController? mapController; List eventLog = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Event Handling'), actions: [ IconButton( icon: Icon(Icons.clear), onPressed: _clearEventLog, ), ], ), body: Column( children: [ // Event log Container( height: 200, padding: EdgeInsets.all(16), color: Colors.grey[100], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Event Log', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 8), Expanded( child: ListView.builder( itemCount: eventLog.length, itemBuilder: (context, index) { return Text( eventLog[eventLog.length - 1 - index], style: TextStyle(fontSize: 12), ); }, ), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) { mapController = controller; _logEvent('Map created'); }, onMapClick: (point, coordinates) { _logEvent('Map clicked at ${coordinates.latitude.toStringAsFixed(4)}, ${coordinates.longitude.toStringAsFixed(4)}'); }, onMapLongClick: (point, coordinates) { _logEvent('Map long clicked at ${coordinates.latitude.toStringAsFixed(4)}, ${coordinates.longitude.toStringAsFixed(4)}'); }, onCameraMove: (position) { _logEvent('Camera moving - Zoom: ${position.zoom.toStringAsFixed(2)}'); }, onCameraIdle: () { _logEvent('Camera idle'); }, onStyleLoaded: () { _logEvent('Style loaded'); }, onError: (error) { _logEvent('Error: $error'); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), ), ], ), ); } void _logEvent(String event) { setState(() { final timestamp = DateTime.now().toString().substring(11, 19); eventLog.add('[$timestamp] $event'); // Keep only last 50 events if (eventLog.length > 50) { eventLog.removeAt(0); } }); } void _clearEventLog() { setState(() { eventLog.clear(); }); } } ``` ## Performance Optimization ### Debounced Interactions ```dart import 'dart:async'; class OptimizedInteractionsScreen extends StatefulWidget { @override _OptimizedInteractionsScreenState createState() => _OptimizedInteractionsScreenState(); } class _OptimizedInteractionsScreenState extends State { MapMetricsController? mapController; Timer? _debounceTimer; CameraPosition? lastCameraPosition; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Optimized Interactions')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onCameraMove: _debouncedCameraMove, onMapClick: _debouncedMapClick, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), ), ); } void _debouncedCameraMove(CameraPosition position) { // Cancel previous timer _debounceTimer?.cancel(); // Set new timer _debounceTimer = Timer(Duration(milliseconds: 300), () { _handleCameraMove(position); }); } void _debouncedMapClick(Point point, LatLng coordinates) { // Cancel previous timer _debounceTimer?.cancel(); // Set new timer _debounceTimer = Timer(Duration(milliseconds: 100), () { _handleMapClick(coordinates); }); } void _handleCameraMove(CameraPosition position) { // Only process if position changed significantly if (lastCameraPosition == null || (position.zoom - lastCameraPosition!.zoom).abs() > 0.5 || _distance(position.target, lastCameraPosition!.target) > 0.001) { lastCameraPosition = position; print('Camera moved to: ${position.target}, zoom: ${position.zoom}'); // Perform expensive operations here _loadDataForViewport(position); } } void _handleMapClick(LatLng coordinates) { print('Map clicked at: $coordinates'); // Perform click-related operations } void _loadDataForViewport(CameraPosition position) { // Simulate loading data for the current viewport print('Loading data for viewport...'); } double _distance(LatLng point1, LatLng point2) { final latDiff = point1.latitude - point2.latitude; final lngDiff = point1.longitude - point2.longitude; return sqrt(latDiff * latDiff + lngDiff * lngDiff); } @override void dispose() { _debounceTimer?.cancel(); super.dispose(); } } ``` ## Best Practices ### Interaction Guidelines 1. **Debounce Frequent Events**: Use timers to debounce camera move events 2. **Batch Operations**: Group related operations to improve performance 3. **Error Handling**: Always handle potential errors in event callbacks 4. **Memory Management**: Dispose of controllers and timers properly 5. **User Feedback**: Provide visual feedback for user interactions ### Common Patterns ```dart // Pattern 1: State-based interactions class StateBasedInteractions { bool isMapReady = false; bool isUserInteracting = false; void onMapCreated(controller) { isMapReady = true; // Enable interactions } void onCameraMove(position) { isUserInteracting = true; // Handle movement } void onCameraIdle() { isUserInteracting = false; // Perform final actions } } // Pattern 2: Event-driven architecture class EventDrivenInteractions { final StreamController _eventController = StreamController.broadcast(); Stream get events => _eventController.stream; void onMapClick(point, coordinates) { _eventController.add(MapClickEvent(coordinates)); } void onCameraMove(position) { _eventController.add(CameraMoveEvent(position)); } } ``` ## Next Steps Now that you understand map interactions, try: - [Custom Map Styling](./flutter-custom-styling) --- **Pro Tip**: Use debouncing for camera move events to improve performance when handling large datasets or performing expensive operations based on map position. --- # Jump to Locations in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-jump-to-locations # Jump to Locations in Flutter This tutorial shows how to navigate the map through a series of locations — either instantly with `moveCamera` or with smooth animation using `animateCamera`. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Jump vs Fly | Method | Description | |--------|-------------| | `moveCamera` | Instant jump — no animation | | `animateCamera` | Smooth fly — animated transition | ## Basic Jump to Locations Provide buttons that instantly jump to different cities: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class JumpToLocationsScreen extends StatefulWidget { @override _JumpToLocationsScreenState createState() => _JumpToLocationsScreenState(); } class _JumpToLocationsScreenState extends State { MapMetricsController? mapController; String currentCity = 'Paris'; final List> locations = [ {'name': 'Paris', 'position': LatLng(48.8566, 2.3522), 'zoom': 12.0}, {'name': 'New York', 'position': LatLng(40.7128, -74.0060), 'zoom': 12.0}, {'name': 'Tokyo', 'position': LatLng(35.6895, 139.6917), 'zoom': 12.0}, {'name': 'Sydney', 'position': LatLng(-33.8688, 151.2093), 'zoom': 12.0}, {'name': 'Dubai', 'position': LatLng(25.2048, 55.2708), 'zoom': 12.0}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Jump to Locations')), body: Column( children: [ // Location buttons SingleChildScrollView( scrollDirection: Axis.horizontal, padding: EdgeInsets.all(8), child: Row( children: locations.map((loc) { final isActive = currentCity == loc['name']; return Padding( padding: EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(loc['name']), selected: isActive, onSelected: (_) => _jumpTo(loc), selectedColor: Colors.blue[100], ), ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), ), ], ), ); } void _jumpTo(Map location) { setState(() { currentCity = location['name']; }); // Instant jump — no animation mapController?.moveCamera( CameraUpdate.newLatLngZoom(location['position'], location['zoom']), ); } } ``` ## Animated Navigation Use `animateCamera` for a smooth transition between locations: ```dart void _flyTo(Map location) { setState(() { currentCity = location['name']; }); // Smooth animated flight mapController?.animateCamera( CameraUpdate.newLatLngZoom(location['position'], location['zoom']), ); } ``` ## Auto Tour: Cycle Through Locations Automatically cycle through a list of locations with a timer: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:async'; class AutoTourScreen extends StatefulWidget { @override _AutoTourScreenState createState() => _AutoTourScreenState(); } class _AutoTourScreenState extends State { MapMetricsController? mapController; Timer? tourTimer; int currentIndex = 0; bool isTourRunning = false; final List> locations = [ { 'name': 'Paris', 'position': LatLng(48.8566, 2.3522), 'zoom': 14.0, 'bearing': 0.0, 'tilt': 45.0, }, { 'name': 'New York', 'position': LatLng(40.7128, -74.0060), 'zoom': 14.0, 'bearing': 90.0, 'tilt': 50.0, }, { 'name': 'Tokyo', 'position': LatLng(35.6895, 139.6917), 'zoom': 14.0, 'bearing': 180.0, 'tilt': 45.0, }, { 'name': 'Sydney', 'position': LatLng(-33.8688, 151.2093), 'zoom': 14.0, 'bearing': 270.0, 'tilt': 50.0, }, { 'name': 'Dubai', 'position': LatLng(25.2048, 55.2708), 'zoom': 14.0, 'bearing': 45.0, 'tilt': 55.0, }, ]; @override Widget build(BuildContext context) { final current = locations[currentIndex]; return Scaffold( appBar: AppBar( title: Text('World Tour'), ), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 3.0, ), ), // City name overlay Positioned( top: 16, left: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(8), ), child: Text( current['name'], style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ), ), // Controls Positioned( bottom: 24, left: 16, right: 16, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // Previous FloatingActionButton.small( heroTag: 'prev', onPressed: _previous, child: Icon(Icons.skip_previous), ), SizedBox(width: 12), // Play/Pause FloatingActionButton( heroTag: 'play', onPressed: _toggleTour, child: Icon(isTourRunning ? Icons.pause : Icons.play_arrow), ), SizedBox(width: 12), // Next FloatingActionButton.small( heroTag: 'next', onPressed: _next, child: Icon(Icons.skip_next), ), ], ), ), ], ), ); } void _navigateTo(int index) { final location = locations[index]; mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: location['position'], zoom: location['zoom'], bearing: location['bearing'], tilt: location['tilt'], ), ), ); } void _next() { setState(() { currentIndex = (currentIndex + 1) % locations.length; }); _navigateTo(currentIndex); } void _previous() { setState(() { currentIndex = (currentIndex - 1 + locations.length) % locations.length; }); _navigateTo(currentIndex); } void _toggleTour() { if (isTourRunning) { tourTimer?.cancel(); setState(() { isTourRunning = false; }); } else { setState(() { isTourRunning = true; }); _navigateTo(currentIndex); tourTimer = Timer.periodic(Duration(seconds: 5), (_) { _next(); }); } } @override void dispose() { tourTimer?.cancel(); mapController?.dispose(); super.dispose(); } } ``` ## Camera Methods Comparison | Method | Animation | Use Case | |--------|-----------|----------| | `moveCamera(CameraUpdate)` | None (instant) | Quick navigation, user-triggered jumps | | `animateCamera(CameraUpdate)` | Smooth fly | Visual transitions, tours, presentations | | `animateCamera(CameraUpdate, duration)` | Custom speed | Cinematic effects, slow reveals | ## Next Steps - [Fly to a Location](./flutter-fly-to-location) — Deep dive into fly-to animations - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — 3D perspectives for each stop - [Map Interactions](./flutter-interactions) — Handle taps and gestures --- **Tip**: Combine `animateCamera` with different `bearing` and `tilt` values at each stop for a cinematic world tour effect. --- # Locate the User in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-locate-user # Locate the User in Flutter Show the user's current GPS location on the map. This guide covers enabling the built-in location layer and requesting location permissions on both Android and iOS. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) (includes platform permission setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Platform Permissions ### Android Add these permissions to `android/app/src/main/AndroidManifest.xml`: ```xml ``` ### iOS Add these keys to `ios/Runner/Info.plist`: ```xml NSLocationWhenInUseUsageDescription This app needs access to your location to show it on the map. NSLocationAlwaysUsageDescription This app needs access to your location to show it on the map. ``` ## Basic User Location Enable the built-in location dot on the map: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LocateUserScreen extends StatefulWidget { @override _LocateUserScreenState createState() => _LocateUserScreenState(); } class _LocateUserScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('My Location')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 4.0, ), myLocationEnabled: true, myLocationTrackingMode: MyLocationTrackingMode.Tracking, myLocationRenderMode: MyLocationRenderMode.COMPASS, ), floatingActionButton: FloatingActionButton( onPressed: _goToMyLocation, child: Icon(Icons.my_location), ), ); } void _goToMyLocation() { mapController?.animateCamera( CameraUpdate.zoomTo(15.0), ); } } ``` The blue dot appears at the user's current location, and the compass arrow shows the direction they are facing. ## Location Tracking Modes | Mode | Description | |------|-------------| | `MyLocationTrackingMode.None` | Shows location dot but doesn't move the camera | | `MyLocationTrackingMode.Tracking` | Camera follows the user as they move | | `MyLocationTrackingMode.TrackingCompass` | Camera follows and rotates to match user heading | | `MyLocationTrackingMode.TrackingGPS` | Camera follows with GPS bearing | ## Location Render Modes | Mode | Description | |------|-------------| | `MyLocationRenderMode.NORMAL` | Simple blue dot | | `MyLocationRenderMode.COMPASS` | Blue dot with compass heading arrow | | `MyLocationRenderMode.GPS` | Blue dot with GPS bearing indicator | ## Complete Example: Locate Me Button A complete example with a "Locate Me" button that requests permission and flies to the user's location: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LocateMeScreen extends StatefulWidget { @override _LocateMeScreenState createState() => _LocateMeScreenState(); } class _LocateMeScreenState extends State { MapMetricsController? mapController; String statusMessage = 'Tap the button to find your location'; bool isLocating = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Locate Me')), body: Column( children: [ // Status bar Container( width: double.infinity, padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10), color: Colors.grey[100], child: Row( children: [ if (isLocating) Padding( padding: EdgeInsets.only(right: 8), child: SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), ), Expanded(child: Text(statusMessage)), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, onUserLocationUpdated: (UserLocation location) { setState(() { isLocating = false; statusMessage = 'Lat: ${location.position.latitude.toStringAsFixed(5)}, ' 'Lng: ${location.position.longitude.toStringAsFixed(5)}'; }); }, initialCameraPosition: CameraPosition( target: LatLng(0.0, 0.0), zoom: 2.0, ), myLocationEnabled: true, myLocationTrackingMode: MyLocationTrackingMode.None, ), ), ], ), floatingActionButton: FloatingActionButton.extended( onPressed: _locateMe, icon: Icon(Icons.my_location), label: Text('Locate Me'), ), ); } void _locateMe() { setState(() { isLocating = true; statusMessage = 'Getting your location...'; }); // Enable tracking and fly to user mapController?.animateCamera( CameraUpdate.zoomTo(15.0), ); } } ``` ## Handling Permission Errors Always handle the case where the user denies location permission: ```dart MapMetrics( // ... other properties myLocationEnabled: true, onError: (String error) { if (error.contains('location') || error.contains('permission')) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Location Permission Required'), content: Text( 'Please enable location permissions in your device settings ' 'to use this feature.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ), ); } }, ) ``` ## Next Steps - [Fly to a Location](./flutter-fly-to-location) — Animate the camera to any coordinates - [Add a Popup](./flutter-add-a-popup) — Show info when tapping markers - [Map Interactions](./flutter-interactions) — Handle taps, long presses, and gestures --- **Note**: Location only works on physical devices or emulators with location simulation enabled. It also requires HTTPS in production web builds. --- # Flutter MapMetrics Integration https://docs.mapatlas.xyz/sdk/examples/flutter-mapmetrics-intro # Flutter MapMetrics Integration Welcome to the Flutter MapMetrics integration guide! This section will help you integrate MapMetrics Atlas API with your Flutter applications using the MapMetrics Flutter package. The package is available on https://pub.dev/packages/mapmetrics and tthe api is here https://pub.dev/documentation/mapmetrics/latest/. ## Overview Flutter MapMetrics integration allows you to create beautiful, interactive maps in your Flutter applications using MapMetrics Atlas API. This combination provides: - **High Performance**: Native speed with MapMetrics GL rendering - **Customizable Maps**: Use MapMetrics Atlas API for custom map styles - **Cross-Platform**: Works on iOS, Android, Web, and Desktop - **Community-Driven Data**: Access to MapMetrics community-contributed map data - **No Vendor Lock-in**: Open-source solution with flexible data providers ## Prerequisites Before you begin, make sure you have: 1. **Flutter SDK** installed (version 3.0.0 or higher) 2. **Dart SDK** installed 3. **MapMetrics Atlas API Key** (create one at [MapMetrics Portal](https://portal.mapmetrics.org)) 4. **Custom Map Style URL** (create one in the MapMetrics Portal) ## Quick Start 1. **Add the dependency** to your `pubspec.yaml`: ```yaml dependencies: mapmetrics: ^1.0.6 ``` 2. **Get your MapMetrics credentials**: - Visit [MapMetrics Portal](https://portal.mapmetrics.org) - Create an API key - Create a custom map style - Copy your style URL 3. **Basic implementation**: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MapScreen extends StatefulWidget { @override _MapScreenState createState() => _MapScreenState(); } class _MapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('MapMetrics Map')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, ), ); } } ``` ## What You'll Learn In the following tutorials, you'll learn how to: - [Setup Flutter with MapMetrics](./flutter-setup) - [Display a Basic Map](./flutter-basic-map) - [Add Markers and Annotations](./flutter-markers) - [Custom Map Styling](./flutter-custom-styling) - [Handle Map Interactions](./flutter-interactions) ## MapMetrics Atlas API Benefits When using Flutter with MapMetrics Atlas API, you get: - **Real-time Community Data**: Maps updated with community contributions - **Custom Styling**: Full control over map appearance through MapMetrics Portal - **High Performance**: Optimized vector tiles for smooth rendering - **Global Coverage**: Comprehensive map data worldwide - **Flexible Attribution**: Simple attribution requirements ## Next Steps Ready to get started? Begin with the [Setup Guide](./flutter-setup) to configure your Flutter project with MapMetrics Atlas API. --- **Note**: Make sure to follow the [attribution requirements](/overview/sdk/#attribution) when using MapMetrics in your applications. --- # Markers and Annotations with Flutter and MapMetrics https://docs.mapatlas.xyz/sdk/examples/flutter-markers # Markers and Annotations with Flutter and MapMetrics This tutorial will show you how to add markers, annotations, and custom overlays to your MapMetrics map in Flutter. ## Adding Basic Markers Here's how to add simple markers to your map: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MarkersScreen extends StatefulWidget { @override _MarkersScreenState createState() => _MarkersScreenState(); } class _MarkersScreenState extends State { MapMetricsController? mapController; Set markers = {}; @override void initState() { super.initState(); _initializeMarkers(); } void _initializeMarkers() { markers = { Marker( markerId: MarkerId('new_york'), position: LatLng(40.7128, -74.0060), infoWindow: InfoWindow( title: 'New York City', snippet: 'The Big Apple', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), ), Marker( markerId: MarkerId('san_francisco'), position: LatLng(37.7749, -122.4194), infoWindow: InfoWindow( title: 'San Francisco', snippet: 'The Golden Gate City', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), ), Marker( markerId: MarkerId('chicago'), position: LatLng(41.8781, -87.6298), infoWindow: InfoWindow( title: 'Chicago', snippet: 'The Windy City', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), ), }; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MapMetrics Markers'), actions: [ IconButton( icon: Icon(Icons.add_location), onPressed: _addRandomMarker, ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { setState(() { mapController = controller; }); }, onMapClick: (Point point, LatLng coordinates) { _addMarkerAtLocation(coordinates); }, initialCameraPosition: CameraPosition( target: LatLng(39.8283, -98.5795), // Center of USA zoom: 4.0, ), markers: markers, ), floatingActionButton: FloatingActionButton( onPressed: _clearMarkers, child: Icon(Icons.clear_all), ), ); } void _addMarkerAtLocation(LatLng position) { final String markerId = 'marker_${DateTime.now().millisecondsSinceEpoch}'; final Marker marker = Marker( markerId: MarkerId(markerId), position: position, infoWindow: InfoWindow( title: 'Custom Marker', snippet: 'Added at ${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}', ), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), ); setState(() { markers.add(marker); }); } void _addRandomMarker() { final double lat = 25.0 + (Random().nextDouble() * 20.0); // 25-45 latitude final double lng = -125.0 + (Random().nextDouble() * 50.0); // -125 to -75 longitude _addMarkerAtLocation(LatLng(lat, lng)); } void _clearMarkers() { setState(() { markers.clear(); }); } } ## Custom Marker Icons ### Using Custom Images ```dart Marker( markerId: MarkerId('custom_icon'), position: LatLng(40.7128, -74.0060), icon: BitmapDescriptor.fromAssetImage( ImageConfiguration(size: Size(48, 48)), 'assets/images/custom_marker.png', ), infoWindow: InfoWindow(title: 'Custom Icon'), ) ``` ### Using Different Default Colors ```dart // Available default colors BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueYellow) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRose) BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueAzure) ``` ### Using Network Images ```dart Marker( markerId: MarkerId('network_icon'), position: LatLng(40.7128, -74.0060), icon: BitmapDescriptor.fromNetworkImage( ImageConfiguration(size: Size(48, 48)), 'https://example.com/marker-icon.png', ), ) ``` ## Marker Interactions ### Handle Marker Taps ```dart MapMetrics( // ... other properties onMarkerTapped: (MarkerId markerId) { print('Marker tapped: ${markerId.value}'); _showMarkerInfo(markerId); }, markers: markers, ) void _showMarkerInfo(MarkerId markerId) { final Marker? marker = markers.firstWhere( (m) => m.markerId == markerId, orElse: () => null, ); if (marker != null) { showDialog( context: context, builder: (context) => AlertDialog( title: Text(marker.infoWindow.title ?? 'Marker'), content: Text(marker.infoWindow.snippet ?? ''), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Close'), ), ], ), ); } } ``` ## Clustered Markers For better performance with many markers, implement clustering: ```dart class ClusteredMarkersScreen extends StatefulWidget { @override _ClusteredMarkersScreenState createState() => _ClusteredMarkersScreenState(); } class _ClusteredMarkersScreenState extends State { MapMetricsController? mapController; Set markers = {}; List clusterPoints = []; @override void initState() { super.initState(); _generateClusterPoints(); } void _generateClusterPoints() { // Generate random points around a center final LatLng center = LatLng(40.7128, -74.0060); clusterPoints = List.generate(50, (index) { final double latOffset = (Random().nextDouble() - 0.5) * 0.1; final double lngOffset = (Random().nextDouble() - 0.5) * 0.1; return LatLng( center.latitude + latOffset, center.longitude + lngOffset, ); }); _updateMarkers(); } void _updateMarkers() { markers = clusterPoints.asMap().entries.map((entry) { final int index = entry.key; final LatLng position = entry.value; return Marker( markerId: MarkerId('cluster_$index'), position: position, infoWindow: InfoWindow( title: 'Point $index', snippet: 'Generated point', ), icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueRed, ), ); }).toSet(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Clustered Markers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 12.0, ), markers: markers, ), ); } } ``` ## Polygons and Polylines ### Adding Polygons ```dart Set polygons = { Polygon( polygonId: PolygonId('custom_polygon'), points: [ LatLng(40.7, -74.0), LatLng(40.7, -73.9), LatLng(40.8, -73.9), LatLng(40.8, -74.0), ], strokeWidth: 2, strokeColor: Colors.red, fillColor: Colors.red.withOpacity(0.3), ), }; MapMetrics( // ... other properties polygons: polygons, ) ``` ### Adding Polylines ```dart Set polylines = { Polyline( polylineId: PolylineId('route'), points: [ LatLng(40.7128, -74.0060), // New York LatLng(39.9526, -75.1652), // Philadelphia LatLng(38.9072, -77.0369), // Washington DC ], color: Colors.blue, width: 3, ), }; MapMetrics( // ... other properties polylines: polylines, ) ``` ## Circles ```dart Set circles = { Circle( circleId: CircleId('radius_circle'), center: LatLng(40.7128, -74.0060), radius: 5000, // meters strokeWidth: 2, strokeColor: Colors.blue, fillColor: Colors.blue.withOpacity(0.2), ), }; MapMetrics( // ... other properties circles: circles, ) ``` ## Advanced Marker Features ### Draggable Markers ```dart Marker( markerId: MarkerId('draggable'), position: LatLng(40.7128, -74.0060), draggable: true, onDragEnd: (LatLng newPosition) { print('Marker moved to: $newPosition'); }, ) ``` ### Flat Markers (No Perspective) ```dart Marker( markerId: MarkerId('flat_marker'), position: LatLng(40.7128, -74.0060), flat: true, // Marker stays flat regardless of map tilt ) ``` ### Marker Rotation ```dart Marker( markerId: MarkerId('rotated_marker'), position: LatLng(40.7128, -74.0060), rotation: 45.0, // Rotate marker 45 degrees ) ``` ## Performance Optimization ### Marker Management ```dart class OptimizedMarkersScreen extends StatefulWidget { @override _OptimizedMarkersScreenState createState() => _OptimizedMarkersScreenState(); } class _OptimizedMarkersScreenState extends State { MapMetricsController? mapController; Set visibleMarkers = {}; List allPoints = []; @override void initState() { super.initState(); _loadAllPoints(); } void _loadAllPoints() { // Load all your data points here allPoints = [/* your data */]; } void _updateVisibleMarkers(CameraPosition position) { // Only show markers within the current viewport final double zoom = position.zoom; final LatLng center = position.target; // Calculate visible bounds based on zoom level final double latDelta = 180.0 / pow(2, zoom); final double lngDelta = 360.0 / pow(2, zoom); final visiblePoints = allPoints.where((point) { return (point.latitude - center.latitude).abs() < latDelta && (point.longitude - center.longitude).abs() < lngDelta; }).take(100); // Limit to 100 markers for performance setState(() { visibleMarkers = visiblePoints.map((point) => Marker( markerId: MarkerId('point_${point.latitude}_${point.longitude}'), position: point, icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), )).toSet(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Optimized Markers')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onCameraMove: _updateVisibleMarkers, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 10.0, ), markers: visibleMarkers, ), ); } } ``` ## Next Steps Now that you can add markers and annotations, try: - [Custom Map Styling](./flutter-custom-styling) - [Handling Map Interactions](./flutter-interactions) --- **Pro Tip**: Use the MapMetrics Portal to create custom map styles that complement your markers. You can adjust the map's color scheme to make your markers stand out better. --- # Measure Distances in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-measure-distances # Measure Distances in Flutter This tutorial shows how to let users tap on the map to place points and measure the distance between them using the Haversine formula. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Complete Measurement Tool Tap on the map to add points. A line connects them and the total distance is calculated: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:math'; class MeasureDistanceScreen extends StatefulWidget { @override _MeasureDistanceScreenState createState() => _MeasureDistanceScreenState(); } class _MeasureDistanceScreenState extends State { MapMetricsController? mapController; List points = []; Set get markers => points.asMap().entries.map((entry) { return Marker( markerId: MarkerId('point_${entry.key}'), position: entry.value, icon: BitmapDescriptor.defaultMarkerWithHue( entry.key == 0 ? BitmapDescriptor.hueGreen : BitmapDescriptor.hueRed, ), infoWindow: InfoWindow( title: 'Point ${entry.key + 1}', snippet: entry.key > 0 ? 'Segment: ${_formatDistance(_haversine(points[entry.key - 1], entry.value))}' : 'Start point', ), ); }).toSet(); Set get polylines => points.length >= 2 ? { Polyline( polylineId: PolylineId('measurement'), points: points, color: Colors.blue, width: 3, patterns: [PatternItem.dash(12), PatternItem.gap(8)], ), } : {}; double get totalDistance { double total = 0; for (int i = 1; i < points.length; i++) { total += _haversine(points[i - 1], points[i]); } return total; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Measure Distance')), body: Column( children: [ // Distance display Container( width: double.infinity, padding: EdgeInsets.all(12), color: Colors.grey[100], child: Row( children: [ Icon(Icons.straighten, size: 20, color: Colors.blue), SizedBox(width: 8), Text( points.length < 2 ? 'Tap on the map to start measuring' : 'Total: ${_formatDistance(totalDistance)} | ${points.length} points', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500), ), Spacer(), if (points.isNotEmpty) TextButton( onPressed: _clearPoints, child: Text('Clear'), ), ], ), ), // Segment details if (points.length >= 2) Container( height: 50, child: ListView.builder( scrollDirection: Axis.horizontal, padding: EdgeInsets.symmetric(horizontal: 8), itemCount: points.length - 1, itemBuilder: (context, index) { final dist = _haversine(points[index], points[index + 1]); return Container( margin: EdgeInsets.symmetric(horizontal: 4, vertical: 8), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: Colors.blue[50], borderRadius: BorderRadius.circular(16), ), child: Center( child: Text( '${index + 1}→${index + 2}: ${_formatDistance(dist)}', style: TextStyle(fontSize: 12), ), ), ); }, ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onMapClick: (Point point, LatLng coordinates) { setState(() { points.add(coordinates); }); }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), markers: markers, polylines: polylines, ), ), ], ), floatingActionButton: points.isNotEmpty ? FloatingActionButton.small( onPressed: _undoLastPoint, child: Icon(Icons.undo), tooltip: 'Undo last point', ) : null, ); } void _clearPoints() { setState(() { points.clear(); }); } void _undoLastPoint() { if (points.isNotEmpty) { setState(() { points.removeLast(); }); } } /// Haversine formula to calculate distance between two LatLng points in meters double _haversine(LatLng a, LatLng b) { const double R = 6371000; // Earth's radius in meters final double lat1 = a.latitude * pi / 180; final double lat2 = b.latitude * pi / 180; final double dLat = (b.latitude - a.latitude) * pi / 180; final double dLng = (b.longitude - a.longitude) * pi / 180; final double h = sin(dLat / 2) * sin(dLat / 2) + cos(lat1) * cos(lat2) * sin(dLng / 2) * sin(dLng / 2); final double c = 2 * atan2(sqrt(h), sqrt(1 - h)); return R * c; } /// Format distance for display String _formatDistance(double meters) { if (meters >= 1000) { return '${(meters / 1000).toStringAsFixed(2)} km'; } else { return '${meters.toStringAsFixed(0)} m'; } } } ``` ## How the Haversine Formula Works The Haversine formula calculates the great-circle distance between two points on a sphere: ```dart double _haversine(LatLng a, LatLng b) { const double R = 6371000; // Earth's radius in meters final double lat1 = a.latitude * pi / 180; final double lat2 = b.latitude * pi / 180; final double dLat = (b.latitude - a.latitude) * pi / 180; final double dLng = (b.longitude - a.longitude) * pi / 180; final double h = sin(dLat / 2) * sin(dLat / 2) + cos(lat1) * cos(lat2) * sin(dLng / 2) * sin(dLng / 2); final double c = 2 * atan2(sqrt(h), sqrt(1 - h)); return R * c; // Distance in meters } ``` This requires `import 'dart:math';` for the `sin`, `cos`, `atan2`, `sqrt`, and `pi` functions. ## Next Steps - [Add a Polyline](./flutter-add-a-polyline) — Draw styled lines on the map - [Draw a Circle](./flutter-draw-a-circle) — Show a radius around a point - [Map Interactions](./flutter-interactions) — Handle all tap events --- **Tip**: The Haversine formula gives great-circle distance (straight line over the Earth's surface). For road/walking distance, you would need to use the MapMetrics Directions API. --- # Multiple Geometries in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-multiple-geometries # Multiple Geometries in Flutter This tutorial shows how to display markers, polylines, polygons, and circles all on the same map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Complete Example Display all geometry types together on a single map: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MultipleGeometriesScreen extends StatefulWidget { @override _MultipleGeometriesScreenState createState() => _MultipleGeometriesScreenState(); } class _MultipleGeometriesScreenState extends State { MapMetricsController? mapController; bool showMarkers = true; bool showPolylines = true; bool showPolygons = true; bool showCircles = true; // --- Markers --- final Set markers = { Marker( markerId: MarkerId('eiffel'), position: LatLng(48.8584, 2.2945), infoWindow: InfoWindow(title: 'Eiffel Tower'), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), ), Marker( markerId: MarkerId('louvre'), position: LatLng(48.8606, 2.3376), infoWindow: InfoWindow(title: 'Louvre Museum'), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), ), Marker( markerId: MarkerId('notre_dame'), position: LatLng(48.8530, 2.3499), infoWindow: InfoWindow(title: 'Notre-Dame'), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), ), Marker( markerId: MarkerId('sacre_coeur'), position: LatLng(48.8867, 2.3431), infoWindow: InfoWindow(title: 'Sacré-Cœur'), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), ), }; // --- Polylines --- final Set polylines = { Polyline( polylineId: PolylineId('walking_route'), points: [ LatLng(48.8584, 2.2945), // Eiffel Tower LatLng(48.8606, 2.3376), // Louvre LatLng(48.8530, 2.3499), // Notre-Dame ], color: Colors.blue, width: 3, ), Polyline( polylineId: PolylineId('metro_line'), points: [ LatLng(48.8530, 2.3499), // Notre-Dame LatLng(48.8670, 2.3640), // Midpoint LatLng(48.8867, 2.3431), // Sacré-Cœur ], color: Colors.purple, width: 3, patterns: [PatternItem.dash(15), PatternItem.gap(10)], ), }; // --- Polygons --- final Set polygons = { Polygon( polygonId: PolygonId('latin_quarter'), points: [ LatLng(48.855, 2.340), LatLng(48.855, 2.360), LatLng(48.845, 2.360), LatLng(48.845, 2.340), ], strokeWidth: 2, strokeColor: Colors.green, fillColor: Colors.green.withOpacity(0.15), ), Polygon( polygonId: PolygonId('marais'), points: [ LatLng(48.862, 2.350), LatLng(48.862, 2.370), LatLng(48.852, 2.370), LatLng(48.852, 2.350), ], strokeWidth: 2, strokeColor: Colors.orange, fillColor: Colors.orange.withOpacity(0.15), ), }; // --- Circles --- final Set circles = { Circle( circleId: CircleId('eiffel_area'), center: LatLng(48.8584, 2.2945), radius: 500, strokeWidth: 2, strokeColor: Colors.red, fillColor: Colors.red.withOpacity(0.1), ), Circle( circleId: CircleId('sacre_coeur_area'), center: LatLng(48.8867, 2.3431), radius: 400, strokeWidth: 2, strokeColor: Colors.orange, fillColor: Colors.orange.withOpacity(0.1), ), }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Multiple Geometries')), body: Column( children: [ // Toggle buttons Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), color: Colors.grey[100], child: Row( children: [ _toggleChip('Markers', showMarkers, (v) => setState(() => showMarkers = v)), SizedBox(width: 4), _toggleChip('Lines', showPolylines, (v) => setState(() => showPolylines = v)), SizedBox(width: 4), _toggleChip('Polygons', showPolygons, (v) => setState(() => showPolygons = v)), SizedBox(width: 4), _toggleChip('Circles', showCircles, (v) => setState(() => showCircles = v)), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8600, 2.3300), zoom: 13.0, ), markers: showMarkers ? markers : {}, polylines: showPolylines ? polylines : {}, polygons: showPolygons ? polygons : {}, circles: showCircles ? circles : {}, ), ), ], ), ); } Widget _toggleChip(String label, bool value, ValueChanged onChanged) { return FilterChip( label: Text(label, style: TextStyle(fontSize: 12)), selected: value, onSelected: onChanged, selectedColor: Colors.blue[100], checkmarkColor: Colors.blue, visualDensity: VisualDensity.compact, ); } } ``` ## Geometry Types Summary | Type | Widget | Key Properties | |------|--------|----------------| | **Marker** | `Marker` | `position`, `icon`, `infoWindow`, `draggable` | | **Polyline** | `Polyline` | `points`, `color`, `width`, `patterns` | | **Polygon** | `Polygon` | `points`, `strokeColor`, `fillColor`, `strokeWidth` | | **Circle** | `Circle` | `center`, `radius`, `strokeColor`, `fillColor` | ## Next Steps - [Add a Polyline](./flutter-add-a-polyline) — Detailed polyline guide - [Add a Polygon](./flutter-add-a-polygon) — Detailed polygon guide - [Draw a Circle](./flutter-draw-a-circle) — Detailed circle guide - [Markers and Annotations](./flutter-markers) — Detailed markers guide --- **Tip**: Use `FilterChip` or `ChoiceChip` widgets to let users toggle individual geometry layers on and off. Pass an empty `Set` to hide a layer without removing the data. --- # Navigation Controls in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-navigation-controls # Navigation Controls in Flutter This tutorial shows how to add custom map navigation controls like zoom buttons, compass, and scale indicators to your MapMetrics Flutter map. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Zoom Controls Add simple zoom in/out buttons as a floating overlay: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class NavigationControlsScreen extends StatefulWidget { @override _NavigationControlsScreenState createState() => _NavigationControlsScreenState(); } class _NavigationControlsScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Navigation Controls')), body: Stack( children: [ // Map MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), // Zoom controls (top-right) Positioned( top: 16, right: 16, child: Column( children: [ _controlButton(Icons.add, _zoomIn), SizedBox(height: 4), _controlButton(Icons.remove, _zoomOut), ], ), ), ], ), ); } Widget _controlButton(IconData icon, VoidCallback onPressed) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(4), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: IconButton( icon: Icon(icon, color: Colors.black87), onPressed: onPressed, constraints: BoxConstraints(minWidth: 40, minHeight: 40), padding: EdgeInsets.zero, ), ); } void _zoomIn() => mapController?.animateCamera(CameraUpdate.zoomIn()); void _zoomOut() => mapController?.animateCamera(CameraUpdate.zoomOut()); } ``` ## Complete Navigation Panel A full set of controls — zoom, compass, location, and reset: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FullNavigationScreen extends StatefulWidget { @override _FullNavigationScreenState createState() => _FullNavigationScreenState(); } class _FullNavigationScreenState extends State { MapMetricsController? mapController; CameraPosition? currentCamera; @override Widget build(BuildContext context) { final bearing = currentCamera?.bearing ?? 0.0; return Scaffold( body: Stack( children: [ // Map MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onCameraMove: (position) { setState(() { currentCamera = position; }); }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), myLocationEnabled: true, ), // Navigation panel (top-right) Positioned( top: 60, right: 16, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 6)], ), child: Column( children: [ // Compass (rotates with bearing) IconButton( icon: Transform.rotate( angle: -bearing * 3.14159 / 180, child: Icon(Icons.navigation, color: Colors.red), ), onPressed: _resetNorth, tooltip: 'Reset North', ), Divider(height: 1), // Zoom in IconButton( icon: Icon(Icons.add), onPressed: _zoomIn, tooltip: 'Zoom In', ), Divider(height: 1), // Zoom out IconButton( icon: Icon(Icons.remove), onPressed: _zoomOut, tooltip: 'Zoom Out', ), Divider(height: 1), // My location IconButton( icon: Icon(Icons.my_location, color: Colors.blue), onPressed: _goToMyLocation, tooltip: 'My Location', ), Divider(height: 1), // Reset view IconButton( icon: Icon(Icons.refresh), onPressed: _resetView, tooltip: 'Reset View', ), ], ), ), ), // Zoom level indicator (bottom-left) if (currentCamera != null) Positioned( bottom: 24, left: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(4), ), child: Text( 'Zoom: ${currentCamera!.zoom.toStringAsFixed(1)}', style: TextStyle(fontSize: 12, fontFamily: 'monospace'), ), ), ), ], ), ); } void _zoomIn() => mapController?.animateCamera(CameraUpdate.zoomIn()); void _zoomOut() => mapController?.animateCamera(CameraUpdate.zoomOut()); void _resetNorth() { final target = currentCamera?.target ?? LatLng(48.8566, 2.3522); final zoom = currentCamera?.zoom ?? 12.0; mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition(target: target, zoom: zoom, bearing: 0, tilt: 0), ), ); } void _goToMyLocation() { mapController?.animateCamera(CameraUpdate.zoomTo(15.0)); } void _resetView() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, bearing: 0, tilt: 0, ), ), ); } } ``` ## Control Positioning | Position | Use Case | |----------|----------| | Top-right | Zoom controls, compass (most common) | | Top-left | Search bar, back button | | Bottom-right | Attribution, scale bar | | Bottom-left | Zoom level, coordinates | ## Next Steps - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Use compass to control 3D view - [Locate the User](./flutter-locate-user) — GPS location with controls - [Fullscreen Map](./flutter-fullscreen-map) — Combine controls with fullscreen --- **Tip**: Wrap your controls in a `Container` with a white background and `boxShadow` to match the look of standard map control panels. --- # Offset the Vanishing Point Using Padding in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-offset-vanishing-point # Offset the Vanishing Point Using Padding in Flutter This tutorial shows how to offset the map's center point using padding — so the map's focal point shifts to accommodate UI overlays like side panels, bottom sheets, or info cards without covering the area of interest. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Bottom Sheet with Map Padding Shift the map center up when a bottom panel is shown: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MapPaddingScreen extends StatefulWidget { @override _MapPaddingScreenState createState() => _MapPaddingScreenState(); } class _MapPaddingScreenState extends State { MapMetricsController? mapController; bool showPanel = false; final double panelHeight = 200.0; final LatLng target = LatLng(48.8584, 2.2945); // Eiffel Tower @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Map Padding')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: target, zoom: 15.0, ), // Apply padding to shift the vanishing point contentPadding: EdgeInsets.only( bottom: showPanel ? panelHeight : 0.0, ), markers: { Marker( markerId: MarkerId('target'), position: target, infoWindow: InfoWindow(title: 'Eiffel Tower'), ), }, ), // Bottom panel if (showPanel) Positioned( bottom: 0, left: 0, right: 0, child: Container( height: panelHeight, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(16)), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 8)], ), child: Padding( padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text('Eiffel Tower', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold)), ), IconButton( icon: Icon(Icons.close), onPressed: () => setState(() => showPanel = false), ), ], ), Text('Champ de Mars, 5 Av. Anatole France', style: TextStyle(color: Colors.grey[600])), SizedBox(height: 12), Row( children: [ Icon(Icons.star, color: Colors.amber, size: 18), Text(' 4.7 (200K reviews)'), ], ), Spacer(), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () {}, child: Text('Get Directions'), ), ), ], ), ), ), ), ], ), floatingActionButton: showPanel ? null : FloatingActionButton( onPressed: () { setState(() => showPanel = true); // Re-center with padding applied mapController?.animateCamera( CameraUpdate.newLatLng(target), ); }, child: Icon(Icons.info), ), ); } } ``` ## Side Panel Padding Shift the map center to the right when a left panel is open: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SidePanelPaddingScreen extends StatefulWidget { @override _SidePanelPaddingScreenState createState() => _SidePanelPaddingScreenState(); } class _SidePanelPaddingScreenState extends State { MapMetricsController? mapController; bool showSidePanel = false; final double panelWidth = 250.0; final List> places = [ {'name': 'Eiffel Tower', 'lat': 48.8584, 'lng': 2.2945}, {'name': 'Louvre Museum', 'lat': 48.8606, 'lng': 2.3376}, {'name': 'Notre-Dame', 'lat': 48.8530, 'lng': 2.3499}, {'name': 'Sacre-Coeur', 'lat': 48.8867, 'lng': 2.3431}, {'name': 'Arc de Triomphe', 'lat': 48.8738, 'lng': 2.2950}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Side Panel Padding'), leading: IconButton( icon: Icon(showSidePanel ? Icons.menu_open : Icons.menu), onPressed: () => setState(() => showSidePanel = !showSidePanel), ), ), body: Row( children: [ // Side panel if (showSidePanel) Container( width: panelWidth, color: Colors.white, child: ListView.builder( itemCount: places.length, itemBuilder: (context, i) { final place = places[i]; return ListTile( leading: Icon(Icons.location_on, color: Colors.blue), title: Text(place['name']), onTap: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(place['lat'], place['lng']), 15.0, ), ); }, ); }, ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.860, 2.320), zoom: 13.0, ), // Padding shifts the logical center contentPadding: EdgeInsets.only( left: showSidePanel ? panelWidth * 0.5 : 0.0, ), markers: places.map((p) { return Marker( markerId: MarkerId(p['name']), position: LatLng(p['lat'], p['lng']), infoWindow: InfoWindow(title: p['name']), ); }).toSet(), ), ), ], ), ); } } ``` ## Padding Options | Padding | Effect | Use Case | |---------|--------|----------| | `bottom` | Shifts center up | Bottom sheets, info panels | | `top` | Shifts center down | Top search bars | | `left` | Shifts center right | Left sidebars, drawers | | `right` | Shifts center left | Right panels | | Combined | Shifts to available space | Complex layouts | ## Next Steps - [Display a Popup](./flutter-display-popup) — Popup patterns - [Fullscreen Map](./flutter-fullscreen-map) — Full screen map view - [Fit to Bounding Box](./flutter-fit-to-bounding-box) — Fit content with padding --- **Tip**: Map padding keeps the target location visible and centered in the *available* space, not the full screen. This is essential for apps like Uber or Google Maps where a bottom sheet covers half the screen but the pin should still be centered in the visible portion. --- # Popup on Click in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-popup-on-click # Popup on Click in Flutter This tutorial shows how to display a popup with detailed information when the user taps on a map feature like a marker or polygon. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Popup on Marker Tap Show a detail card when any marker is tapped: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PopupOnClickScreen extends StatefulWidget { @override _PopupOnClickScreenState createState() => _PopupOnClickScreenState(); } class _PopupOnClickScreenState extends State { MapMetricsController? mapController; Map? selectedPlace; final List> places = [ { 'id': 'eiffel', 'name': 'Eiffel Tower', 'description': 'Iconic iron lattice tower built in 1889.', 'category': 'Landmark', 'position': LatLng(48.8584, 2.2945), 'rating': 4.7, }, { 'id': 'louvre', 'name': 'Louvre Museum', 'description': 'World\'s largest art museum, home to the Mona Lisa.', 'category': 'Museum', 'position': LatLng(48.8606, 2.3376), 'rating': 4.8, }, { 'id': 'notre_dame', 'name': 'Notre-Dame Cathedral', 'description': 'Medieval Catholic cathedral, a masterpiece of Gothic architecture.', 'category': 'Church', 'position': LatLng(48.8530, 2.3499), 'rating': 4.6, }, { 'id': 'sacre_coeur', 'name': 'Sacré-Cœur', 'description': 'White-domed basilica on the highest point in Paris.', 'category': 'Church', 'position': LatLng(48.8867, 2.3431), 'rating': 4.5, }, ]; Set get markers => places.map((place) { return Marker( markerId: MarkerId(place['id']), position: place['position'], icon: BitmapDescriptor.defaultMarkerWithHue( place['category'] == 'Museum' ? BitmapDescriptor.hueBlue : place['category'] == 'Church' ? BitmapDescriptor.hueViolet : BitmapDescriptor.hueRed, ), ); }).toSet(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Tap a Marker')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onMarkerTapped: (MarkerId markerId) { final place = places.firstWhere( (p) => p['id'] == markerId.value, ); setState(() => selectedPlace = place); }, onMapClick: (Point point, LatLng coordinates) { // Dismiss popup when tapping the map setState(() => selectedPlace = null); }, initialCameraPosition: CameraPosition( target: LatLng(48.8620, 2.3200), zoom: 13.0, ), markers: markers, ), // Popup card if (selectedPlace != null) Positioned( bottom: 24, left: 16, right: 16, child: Card( elevation: 6, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( selectedPlace!['name'], style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: Icon(Icons.close, size: 20), onPressed: () => setState(() => selectedPlace = null), padding: EdgeInsets.zero, constraints: BoxConstraints(), ), ], ), SizedBox(height: 4), Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: Colors.blue[50], borderRadius: BorderRadius.circular(12), ), child: Text( selectedPlace!['category'], style: TextStyle(fontSize: 12, color: Colors.blue), ), ), SizedBox(height: 8), Text( selectedPlace!['description'], style: TextStyle( fontSize: 14, color: Colors.grey[700]), ), SizedBox(height: 8), Row( children: [ Icon(Icons.star, color: Colors.amber, size: 18), SizedBox(width: 4), Text( '${selectedPlace!['rating']}', style: TextStyle(fontWeight: FontWeight.w500), ), ], ), ], ), ), ), ), ], ), ); } } ``` ## Popup on Map Tap (Any Location) Show coordinates and reverse-geocoded info when tapping anywhere: ```dart MapMetrics( // ... config onMapClick: (Point point, LatLng coordinates) { showModalBottomSheet( context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) => Padding( padding: EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Tapped Location', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), SizedBox(height: 12), _infoRow(Icons.location_on, 'Latitude', coordinates.latitude.toStringAsFixed(6)), _infoRow(Icons.location_on, 'Longitude', coordinates.longitude.toStringAsFixed(6)), _infoRow(Icons.touch_app, 'Screen X', '${point.x.toInt()}'), _infoRow(Icons.touch_app, 'Screen Y', '${point.y.toInt()}'), SizedBox(height: 12), ], ), ), ); }, ) Widget _infoRow(IconData icon, String label, String value) { return Padding( padding: EdgeInsets.symmetric(vertical: 4), child: Row( children: [ Icon(icon, size: 16, color: Colors.grey), SizedBox(width: 8), Text('$label: ', style: TextStyle(fontWeight: FontWeight.w500)), Text(value, style: TextStyle(fontFamily: 'monospace')), ], ), ); } ``` ## Next Steps - [Add a Popup](./flutter-add-a-popup) — InfoWindow-based popups - [Show Polygon Info on Click](./flutter-show-polygon-info-on-click) — Popup for polygon features - [Map Interactions](./flutter-interactions) — Full interaction handling --- **Tip**: Dismiss the popup when the user taps on the map background by handling `onMapClick` and clearing the selected item. --- # Display a Popup on Long Press in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-popup-on-long-press # Display a Popup on Long Press in Flutter This tutorial shows how to show a popup when the user long-presses on the map — the Flutter equivalent of hover popups on web. Great for adding new markers, getting location info, or context menus. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Long Press Popup Show a dialog with coordinates when the user long-presses: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class LongPressPopupScreen extends StatefulWidget { @override _LongPressPopupScreenState createState() => _LongPressPopupScreenState(); } class _LongPressPopupScreenState extends State { MapMetricsController? mapController; LatLng? longPressPosition; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Long Press Popup')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), onMapLongClick: (Point point, LatLng coordinates) { setState(() { longPressPosition = coordinates; }); }, onMapClick: (Point point, LatLng coordinates) { // Dismiss popup on regular tap setState(() { longPressPosition = null; }); }, markers: longPressPosition != null ? { Marker( markerId: MarkerId('long_press'), position: longPressPosition!, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueViolet), ), } : {}, ), // Popup card if (longPressPosition != null) Positioned( bottom: 24, left: 16, right: 16, child: Card( elevation: 6, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.location_on, color: Colors.purple), SizedBox(width: 8), Text('Long Press Location', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), Spacer(), IconButton( icon: Icon(Icons.close, size: 20), onPressed: () { setState(() { longPressPosition = null; }); }, ), ], ), SizedBox(height: 8), Text( 'Lat: ${longPressPosition!.latitude.toStringAsFixed(6)}', style: TextStyle(fontFamily: 'monospace'), ), Text( 'Lng: ${longPressPosition!.longitude.toStringAsFixed(6)}', style: TextStyle(fontFamily: 'monospace'), ), ], ), ), ), ), ], ), ); } } ``` ## Long Press Context Menu Show an action menu with options like "Add Marker", "Get Directions", etc.: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ContextMenuScreen extends StatefulWidget { @override _ContextMenuScreenState createState() => _ContextMenuScreenState(); } class _ContextMenuScreenState extends State { MapMetricsController? mapController; Set userMarkers = {}; int markerCount = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Context Menu'), actions: [ if (userMarkers.isNotEmpty) TextButton( onPressed: () { setState(() { userMarkers.clear(); markerCount = 0; }); }, child: Text('Clear All', style: TextStyle(color: Colors.white)), ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), onMapLongClick: (Point point, LatLng coordinates) { _showContextMenu(coordinates); }, markers: userMarkers, ), ); } void _showContextMenu(LatLng position) { showModalBottomSheet( context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ // Drag handle Container( width: 40, height: 4, margin: EdgeInsets.only(top: 12), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), Padding( padding: EdgeInsets.all(16), child: Text( '${position.latitude.toStringAsFixed(4)}, ' '${position.longitude.toStringAsFixed(4)}', style: TextStyle( color: Colors.grey[600], fontFamily: 'monospace'), ), ), ListTile( leading: Icon(Icons.add_location, color: Colors.blue), title: Text('Add Marker'), onTap: () { Navigator.pop(context); _addMarker(position); }, ), ListTile( leading: Icon(Icons.directions, color: Colors.green), title: Text('Get Directions Here'), onTap: () { Navigator.pop(context); // Handle directions ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Directions to ${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}')), ); }, ), ListTile( leading: Icon(Icons.copy, color: Colors.orange), title: Text('Copy Coordinates'), onTap: () { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Coordinates copied!')), ); }, ), ListTile( leading: Icon(Icons.info_outline, color: Colors.purple), title: Text('What\'s Here?'), onTap: () { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Searching for nearby places...')), ); }, ), SizedBox(height: 8), ], ), ); }, ); } void _addMarker(LatLng position) { markerCount++; setState(() { userMarkers.add( Marker( markerId: MarkerId('user_$markerCount'), position: position, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), infoWindow: InfoWindow( title: 'Marker $markerCount', snippet: '${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}', ), ), ); }); } } ``` ## Long Press vs Tap Comparison | Gesture | Callback | Best For | |---------|----------|----------| | Tap | `onMapClick` | Select, dismiss, quick actions | | Long press | `onMapLongClick` | Context menus, add markers, advanced actions | | Marker tap | `Marker.onTap` | Show details for a specific marker | ## Next Steps - [Display a Popup](./flutter-display-popup) — More popup patterns - [Popup on Click](./flutter-popup-on-click) — Tap-based popups - [Get Coordinates on Tap](./flutter-get-coordinates-on-tap) — Show tap coordinates --- **Tip**: Long press is the mobile convention for "right-click" context menus. Use `showModalBottomSheet` for a native-feeling action menu — it's easier to reach with one hand than a popup card at the top of the screen. --- # Render World Copies in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-render-world-copies # Render World Copies in Flutter This tutorial shows how to control whether the map renders copies of the world when zoomed out — and how to handle wrapping at the antimeridian. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Toggle World Copies Enable or disable world repetition when zoomed out: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class WorldCopiesScreen extends StatefulWidget { @override _WorldCopiesScreenState createState() => _WorldCopiesScreenState(); } class _WorldCopiesScreenState extends State { MapMetricsController? mapController; bool renderWorldCopies = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('World Copies'), actions: [ Row( children: [ Text('Copies', style: TextStyle(color: Colors.white)), Switch( value: renderWorldCopies, onChanged: (val) { setState(() => renderWorldCopies = val); mapController?.setRenderWorldCopies(val); }, activeColor: Colors.white, ), ], ), ], ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(20.0, 0.0), zoom: 1.0, ), renderWorldCopies: renderWorldCopies, ), ); } } ``` ## Antimeridian-Crossing Routes Draw a route that crosses the international date line (180th meridian): ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class AntimeridianRouteScreen extends StatefulWidget { @override _AntimeridianRouteScreenState createState() => _AntimeridianRouteScreenState(); } class _AntimeridianRouteScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Antimeridian Route')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(35.0, 180.0), // Center on antimeridian zoom: 2.0, ), renderWorldCopies: true, polylines: { // Route: Tokyo → Honolulu (crosses antimeridian) Polyline( polylineId: PolylineId('trans_pacific'), points: [ LatLng(35.6762, 139.6503), // Tokyo LatLng(35.0, 160.0), LatLng(30.0, 180.0), // Antimeridian LatLng(25.0, -170.0), LatLng(21.3069, -157.8583), // Honolulu ], color: Colors.blue, width: 3, ), }, markers: { Marker( markerId: MarkerId('tokyo'), position: LatLng(35.6762, 139.6503), infoWindow: InfoWindow(title: 'Tokyo'), ), Marker( markerId: MarkerId('honolulu'), position: LatLng(21.3069, -157.8583), infoWindow: InfoWindow(title: 'Honolulu'), ), }, ), ); } } ``` ## Global Data Visualization Display global data with proper world wrapping: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class GlobalDataScreen extends StatefulWidget { @override _GlobalDataScreenState createState() => _GlobalDataScreenState(); } class _GlobalDataScreenState extends State { MapMetricsController? mapController; bool showCopies = true; final List> globalOffices = [ {'city': 'New York', 'lat': 40.713, 'lng': -74.006}, {'city': 'London', 'lat': 51.507, 'lng': -0.128}, {'city': 'Dubai', 'lat': 25.205, 'lng': 55.271}, {'city': 'Mumbai', 'lat': 19.076, 'lng': 72.878}, {'city': 'Singapore', 'lat': 1.352, 'lng': 103.820}, {'city': 'Tokyo', 'lat': 35.676, 'lng': 139.650}, {'city': 'Sydney', 'lat': -33.869, 'lng': 151.209}, {'city': 'Sao Paulo', 'lat': -23.551, 'lng': -46.633}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Global Offices')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(20.0, 0.0), zoom: 1.5, ), renderWorldCopies: showCopies, markers: globalOffices.map((office) { return Marker( markerId: MarkerId(office['city']), position: LatLng(office['lat'], office['lng']), infoWindow: InfoWindow(title: office['city']), ); }).toSet(), ), Positioned( bottom: 16, left: 16, child: Card( child: Padding( padding: EdgeInsets.all(8), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text('World copies'), Switch( value: showCopies, onChanged: (val) { setState(() => showCopies = val); mapController?.setRenderWorldCopies(val); }, ), ], ), ), ), ), ], ), ); } } ``` ## When to Enable/Disable World Copies | Scenario | World Copies | Reason | |----------|-------------|--------| | Global flight map | **On** | Routes can wrap naturally | | Country-level app | **Off** | Prevents confusion at edges | | City-level app | Doesn't matter | Not visible at high zoom | | Dashboard/analytics | **On** | Shows complete global data | | Embedded preview | **Off** | Cleaner single-world view | ## Next Steps - [Display the Whole World](./flutter-display-whole-world) — Global map view - [Restrict Map Panning](./flutter-restrict-map-panning) — Lock to a region - [Arc Layer](./flutter-arc-layer) — Flight route arcs --- **Tip**: When `renderWorldCopies` is `false`, the map stops at the edges of one world. This is useful for apps that restrict the view to a single country or region — users can't accidentally pan into a duplicate world. --- # Restrict Map Panning in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-restrict-map-panning # Restrict Map Panning in Flutter This tutorial shows how to limit the map to a specific geographic area so users cannot pan or zoom outside of it. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Set Max Bounds Restrict the map to only show Paris: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RestrictPanningScreen extends StatefulWidget { @override _RestrictPanningScreenState createState() => _RestrictPanningScreenState(); } class _RestrictPanningScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Restricted to Paris')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) { mapController = controller; // Set bounds to restrict panning controller.setMaxBounds( LatLngBounds( southwest: LatLng(48.800, 2.220), northeast: LatLng(48.920, 2.470), ), ); }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), minMaxZoomPreference: MinMaxZoomPreference(10.0, 18.0), ), ); } } ``` Users can pan and zoom within Paris but the map will bounce back if they try to go outside the boundary. ## Restrict with Min/Max Zoom Combine bounds with zoom limits: ```dart MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) { controller.setMaxBounds( LatLngBounds( southwest: LatLng(48.800, 2.220), northeast: LatLng(48.920, 2.470), ), ); }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), minMaxZoomPreference: MinMaxZoomPreference( 10.0, // Can't zoom out further than this 18.0, // Can't zoom in further than this ), ) ``` ## Toggle Bounds On/Off Let users switch between restricted and free panning: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ToggleBoundsScreen extends StatefulWidget { @override _ToggleBoundsScreenState createState() => _ToggleBoundsScreenState(); } class _ToggleBoundsScreenState extends State { MapMetricsController? mapController; bool isRestricted = true; final LatLngBounds parisBounds = LatLngBounds( southwest: LatLng(48.800, 2.220), northeast: LatLng(48.920, 2.470), ); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(isRestricted ? 'Restricted' : 'Free Panning'), actions: [ TextButton.icon( onPressed: _toggleBounds, icon: Icon( isRestricted ? Icons.lock : Icons.lock_open, color: Colors.white, ), label: Text( isRestricted ? 'Unlock' : 'Lock', style: TextStyle(color: Colors.white), ), ), ], ), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) { mapController = controller; if (isRestricted) { controller.setMaxBounds(parisBounds); } }, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 12.0, ), ), // Bounds indicator if (isRestricted) Positioned( bottom: 24, left: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.9), borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.lock, size: 16, color: Colors.white), SizedBox(width: 6), Text( 'Map restricted to Paris', style: TextStyle(color: Colors.white, fontSize: 13), ), ], ), ), ), ], ), ); } void _toggleBounds() { setState(() { isRestricted = !isRestricted; }); if (isRestricted) { mapController?.setMaxBounds(parisBounds); } else { mapController?.setMaxBounds(null); // Remove bounds } } } ``` ## Switchable Regions Let users select which region to restrict to: ```dart final Map regions = { 'Paris': LatLngBounds( southwest: LatLng(48.800, 2.220), northeast: LatLng(48.920, 2.470), ), 'Manhattan': LatLngBounds( southwest: LatLng(40.700, -74.020), northeast: LatLng(40.800, -73.930), ), 'Central London': LatLngBounds( southwest: LatLng(51.490, -0.180), northeast: LatLng(51.530, -0.070), ), }; void _switchRegion(String regionName) { final bounds = regions[regionName]; if (bounds == null) return; mapController?.setMaxBounds(bounds); mapController?.animateCamera( CameraUpdate.newLatLngBounds(bounds, 50.0), ); } ``` ## Restriction Options | Option | Description | |--------|-------------| | `setMaxBounds(bounds)` | Restrict panning to a bounding box | | `setMaxBounds(null)` | Remove bounds restriction | | `MinMaxZoomPreference(min, max)` | Restrict zoom levels | ## Next Steps - [Fit to Bounding Box](./flutter-fit-to-bounding-box) — Zoom to fit a specific area - [Disable Scroll Zoom](./flutter-disable-scroll-zoom) — Control zoom gestures - [Toggle Interactions](./flutter-toggle-interactions) — Fine-grained gesture control --- **Tip**: Combine `setMaxBounds` with `MinMaxZoomPreference` to prevent users from zooming out far enough to see the bounds edges, giving a seamless restricted experience. --- # Right-to-Left (RTL) Text Support in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-rtl-support # Right-to-Left (RTL) Text Support in Flutter This tutorial shows how to properly display right-to-left scripts like Arabic, Hebrew, and Persian on your MapMetrics Flutter map — ensuring labels and UI elements render correctly. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Enable RTL Text Plugin Load the RTL text plugin so map labels in Arabic, Hebrew, etc. render correctly: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RtlMapScreen extends StatefulWidget { @override _RtlMapScreenState createState() => _RtlMapScreenState(); } class _RtlMapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('RTL Support')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; // Enable RTL text rendering controller.setRTLTextPlugin( 'https://cdn.mapmetrics-atlas.net/basemaps-assets/js/mapmetrics-gl-rtl-text.min.js', ); }, initialCameraPosition: CameraPosition( target: LatLng(25.2048, 55.2708), // Dubai zoom: 10.0, ), ), ); } } ``` ## RTL Map with City Markers Display markers in Middle Eastern cities with Arabic labels: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ArabicCitiesScreen extends StatefulWidget { @override _ArabicCitiesScreenState createState() => _ArabicCitiesScreenState(); } class _ArabicCitiesScreenState extends State { MapMetricsController? mapController; final List> cities = [ {'name': 'Dubai', 'nameAr': 'دبي', 'lat': 25.2048, 'lng': 55.2708}, {'name': 'Abu Dhabi', 'nameAr': 'أبوظبي', 'lat': 24.4539, 'lng': 54.3773}, {'name': 'Riyadh', 'nameAr': 'الرياض', 'lat': 24.7136, 'lng': 46.6753}, {'name': 'Cairo', 'nameAr': 'القاهرة', 'lat': 30.0444, 'lng': 31.2357}, {'name': 'Beirut', 'nameAr': 'بيروت', 'lat': 33.8938, 'lng': 35.5018}, {'name': 'Amman', 'nameAr': 'عمّان', 'lat': 31.9454, 'lng': 35.9284}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Arabic Cities')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; controller.setRTLTextPlugin( 'https://cdn.mapmetrics-atlas.net/basemaps-assets/js/mapmetrics-gl-rtl-text.min.js', ); }, initialCameraPosition: CameraPosition( target: LatLng(27.0, 45.0), zoom: 4.0, ), markers: cities.map((city) { return Marker( markerId: MarkerId(city['name']), position: LatLng(city['lat'], city['lng']), infoWindow: InfoWindow( title: city['nameAr'], snippet: city['name'], ), ); }).toSet(), ), ); } } ``` ## Full RTL App Layout Build a complete RTL-aware map app with Arabic UI: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RtlAppScreen extends StatefulWidget { @override _RtlAppScreenState createState() => _RtlAppScreenState(); } class _RtlAppScreenState extends State { MapMetricsController? mapController; bool isRtl = true; final List> locations = [ {'nameAr': 'برج خليفة', 'nameEn': 'Burj Khalifa', 'lat': 25.1972, 'lng': 55.2744}, {'nameAr': 'نخلة جميرا', 'nameEn': 'Palm Jumeirah', 'lat': 25.1124, 'lng': 55.1390}, {'nameAr': 'دبي مول', 'nameEn': 'Dubai Mall', 'lat': 25.1985, 'lng': 55.2796}, ]; @override Widget build(BuildContext context) { return Directionality( textDirection: isRtl ? TextDirection.rtl : TextDirection.ltr, child: Scaffold( appBar: AppBar( title: Text(isRtl ? 'خريطة دبي' : 'Dubai Map'), actions: [ TextButton( onPressed: () => setState(() => isRtl = !isRtl), child: Text( isRtl ? 'EN' : 'عربي', style: TextStyle(color: Colors.white, fontSize: 16), ), ), ], ), body: Column( children: [ // Location list Container( height: 60, child: ListView.builder( scrollDirection: Axis.horizontal, reverse: isRtl, // RTL scroll direction padding: EdgeInsets.all(8), itemCount: locations.length, itemBuilder: (context, i) { final loc = locations[i]; return Padding( padding: EdgeInsets.symmetric(horizontal: 4), child: ActionChip( label: Text(isRtl ? loc['nameAr'] : loc['nameEn']), onPressed: () { mapController?.animateCamera( CameraUpdate.newLatLngZoom( LatLng(loc['lat'], loc['lng']), 15.0, ), ); }, ), ); }, ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; controller.setRTLTextPlugin( 'https://cdn.mapmetrics-atlas.net/basemaps-assets/js/mapmetrics-gl-rtl-text.min.js', ); }, initialCameraPosition: CameraPosition( target: LatLng(25.2048, 55.2708), zoom: 11.0, ), markers: locations.map((loc) { return Marker( markerId: MarkerId(loc['nameEn']), position: LatLng(loc['lat'], loc['lng']), infoWindow: InfoWindow( title: isRtl ? loc['nameAr'] : loc['nameEn'], ), ); }).toSet(), ), ), ], ), ), ); } } ``` ## RTL-Supported Scripts | Script | Language Examples | Direction | |--------|-----------------|-----------| | Arabic | Arabic, Urdu, Pashto | Right-to-Left | | Hebrew | Hebrew, Yiddish | Right-to-Left | | Persian | Farsi, Dari | Right-to-Left | | Thaana | Dhivehi (Maldives) | Right-to-Left | ## Key Steps for RTL 1. Call `setRTLTextPlugin()` early in `onMapCreated` 2. Wrap your app/screen with `Directionality` for UI widgets 3. Use `TextDirection.rtl` for Arabic/Hebrew text in Flutter widgets 4. Map labels are handled automatically by the RTL plugin ## Next Steps - [Custom Map Styling](./flutter-custom-styling) — Style your map for any region - [Display the Whole World](./flutter-display-whole-world) — Global map view - [Add Custom Icons with Markers](./flutter-add-custom-icons-markers) — Custom markers --- **Tip**: The RTL text plugin only needs to be loaded once — subsequent map instances in the same app session will use the cached plugin. Call it in `onMapCreated` on your first map screen. --- # Satellite Map with Terrain Elevation in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-satellite-terrain # Satellite Map with Terrain Elevation in Flutter This tutorial shows how to display satellite imagery combined with 3D terrain elevation — ideal for outdoor, hiking, and geographic exploration apps. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Satellite View Switch to a satellite style URL for aerial imagery: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SatelliteMapScreen extends StatefulWidget { @override _SatelliteMapScreenState createState() => _SatelliteMapScreenState(); } class _SatelliteMapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Satellite View')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_SATELLITE_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), // Swiss Alps zoom: 10.0, tilt: 60.0, bearing: 30.0, ), ), ); } } ``` ## Toggle Between Map and Satellite Let users switch between standard map and satellite views: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class MapToggleScreen extends StatefulWidget { @override _MapToggleScreenState createState() => _MapToggleScreenState(); } class _MapToggleScreenState extends State { MapMetricsController? mapController; bool isSatellite = false; final String mapStyleUrl = 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY'; final String satelliteStyleUrl = 'https://gateway.mapmetrics.org/styles/YOUR_SATELLITE_STYLE_ID?token=YOUR_API_KEY'; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Map / Satellite Toggle')), body: Stack( children: [ MapMetrics( styleUrl: isSatellite ? satelliteStyleUrl : mapStyleUrl, onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 15.0, tilt: isSatellite ? 45.0 : 0.0, ), ), // Toggle button Positioned( top: 16, right: 16, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: ToggleButtons( isSelected: [!isSatellite, isSatellite], onPressed: (index) { setState(() { isSatellite = index == 1; }); }, borderRadius: BorderRadius.circular(8), children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ Icon(Icons.map, size: 18), SizedBox(width: 4), Text('Map'), ], ), ), Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ Icon(Icons.satellite, size: 18), SizedBox(width: 4), Text('Satellite'), ], ), ), ], ), ), ), ], ), ); } } ``` ## Satellite with 3D Terrain Combine satellite imagery with terrain elevation for a dramatic effect: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SatelliteTerrainScreen extends StatefulWidget { @override _SatelliteTerrainScreenState createState() => _SatelliteTerrainScreenState(); } class _SatelliteTerrainScreenState extends State { MapMetricsController? mapController; double exaggeration = 1.5; bool terrainEnabled = true; final List> locations = [ {'name': 'Swiss Alps', 'lat': 46.818, 'lng': 8.228, 'zoom': 10.0, 'bearing': 30.0}, {'name': 'Grand Canyon', 'lat': 36.107, 'lng': -112.113, 'zoom': 11.0, 'bearing': 90.0}, {'name': 'Mount Fuji', 'lat': 35.361, 'lng': 138.727, 'zoom': 11.0, 'bearing': 200.0}, {'name': 'Himalayas', 'lat': 27.988, 'lng': 86.925, 'zoom': 10.0, 'bearing': 45.0}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Satellite + Terrain')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_SATELLITE_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.818, 8.228), zoom: 10.0, tilt: 60.0, bearing: 30.0, ), onStyleLoaded: () { if (terrainEnabled) _enableTerrain(); }, ), // Location buttons Positioned( bottom: 80, left: 8, right: 8, child: SizedBox( height: 40, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: locations.length, separatorBuilder: (_, __) => SizedBox(width: 6), itemBuilder: (context, i) { final loc = locations[i]; return ElevatedButton( onPressed: () { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(loc['lat'], loc['lng']), zoom: loc['zoom'], tilt: 60.0, bearing: loc['bearing'], ), ), ); }, child: Text(loc['name'], style: TextStyle(fontSize: 11)), style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 12), ), ); }, ), ), ), // Terrain toggle Positioned( bottom: 16, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), child: Row( children: [ Text('3D Terrain'), Switch( value: terrainEnabled, onChanged: (val) { setState(() => terrainEnabled = val); if (val) { _enableTerrain(); } else { mapController?.setTerrain( 'terrain-source', exaggeration: 0.0); } }, ), Expanded( child: Slider( value: exaggeration, min: 0.5, max: 3.0, divisions: 25, label: '${exaggeration.toStringAsFixed(1)}x', onChanged: terrainEnabled ? (val) { setState(() => exaggeration = val); mapController?.setTerrain( 'terrain-source', exaggeration: val); } : null, ), ), ], ), ), ), ), ], ), ); } void _enableTerrain() { mapController?.addRasterDemSource( 'terrain-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.setTerrain('terrain-source', exaggeration: exaggeration); } } ``` ## Next Steps - [3D Terrain](./flutter-3d-terrain) — Terrain-only examples - [3D Buildings](./flutter-3d-buildings) — Extruded buildings - [Customize Camera Animations](./flutter-customize-camera-animations) — Cinematic camera --- **Tip**: Satellite + terrain is very data-heavy. For production apps, enable terrain only when the user zooms past level 8 to save bandwidth and improve load times at global zoom levels. --- # Set Pitch and Bearing in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-set-pitch-and-bearing # Set Pitch and Bearing in Flutter This tutorial shows how to control the map's pitch (tilt) and bearing (rotation) to create 3D perspectives and custom viewing angles. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## What Are Pitch and Bearing? - **Pitch** (tilt): The angle of the camera from 0° (looking straight down) to 60°–85° (looking towards the horizon). Higher pitch creates a more 3D perspective. - **Bearing** (rotation): The compass direction the camera faces, from -180° to 180° (or 0° to 360°). 0° faces north, 90° faces east. ## Basic Pitch and Bearing Set the initial pitch and bearing in the camera position: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PitchBearingScreen extends StatefulWidget { @override _PitchBearingScreenState createState() => _PitchBearingScreenState(); } class _PitchBearingScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Pitch & Bearing')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), // New York zoom: 15.0, bearing: 45.0, // Rotated 45° tilt: 60.0, // Tilted for 3D view ), ), ); } } ``` ## Interactive Controls Let users adjust pitch and bearing with sliders: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class InteractivePitchBearingScreen extends StatefulWidget { @override _InteractivePitchBearingScreenState createState() => _InteractivePitchBearingScreenState(); } class _InteractivePitchBearingScreenState extends State { MapMetricsController? mapController; double pitch = 0.0; double bearing = 0.0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Adjust Perspective')), body: Column( children: [ // Controls Container( padding: EdgeInsets.all(12), color: Colors.grey[100], child: Column( children: [ // Pitch slider Row( children: [ SizedBox(width: 70, child: Text('Pitch: ${pitch.toInt()}°')), Expanded( child: Slider( value: pitch, min: 0, max: 60, onChanged: (value) { setState(() { pitch = value; }); _updateCamera(); }, ), ), ], ), // Bearing slider Row( children: [ SizedBox( width: 70, child: Text('Bearing: ${bearing.toInt()}°'), ), Expanded( child: Slider( value: bearing, min: 0, max: 360, onChanged: (value) { setState(() { bearing = value; }); _updateCamera(); }, ), ), ], ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 15.0, ), ), ), ], ), ); } void _updateCamera() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 15.0, bearing: bearing, tilt: pitch, ), ), ); } } ``` ## Common Presets Use preset buttons for commonly used perspectives: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PresetViewsScreen extends StatefulWidget { @override _PresetViewsScreenState createState() => _PresetViewsScreenState(); } class _PresetViewsScreenState extends State { MapMetricsController? mapController; final List> presets = [ { 'name': 'Top Down', 'icon': Icons.arrow_downward, 'pitch': 0.0, 'bearing': 0.0, 'zoom': 15.0, }, { 'name': 'Street View', 'icon': Icons.streetview, 'pitch': 60.0, 'bearing': 0.0, 'zoom': 17.0, }, { 'name': 'Bird\'s Eye', 'icon': Icons.flight, 'pitch': 45.0, 'bearing': -45.0, 'zoom': 16.0, }, { 'name': 'Cinematic', 'icon': Icons.movie, 'pitch': 50.0, 'bearing': 130.0, 'zoom': 16.0, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('View Presets')), body: Column( children: [ // Preset buttons SingleChildScrollView( scrollDirection: Axis.horizontal, padding: EdgeInsets.all(8), child: Row( children: presets.map((preset) { return Padding( padding: EdgeInsets.only(right: 8), child: ElevatedButton.icon( onPressed: () => _applyPreset(preset), icon: Icon(preset['icon']), label: Text(preset['name']), ), ); }).toList(), ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), zoom: 15.0, ), ), ), ], ), ); } void _applyPreset(Map preset) { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(40.7128, -74.0060), zoom: preset['zoom'], bearing: preset['bearing'], tilt: preset['pitch'], ), ), ); } } ``` ## Common Perspective Values | View | Pitch | Bearing | Best For | |------|-------|---------|----------| | Top Down | 0° | 0° | 2D overview, data visualization | | Slight Tilt | 30° | 0° | General browsing | | Street View | 60° | 0° | Street-level exploration | | Bird's Eye | 45° | -45° | Urban areas, buildings | | Cinematic | 50° | 130° | Dramatic presentation | ## Next Steps - [Fly to a Location](./flutter-fly-to-location) — Combine fly-to with pitch and bearing changes - [Fullscreen Map](./flutter-fullscreen-map) — Immersive fullscreen experience - [Jump to Locations](./flutter-jump-to-locations) — Tour with different perspectives --- **Tip**: Combine pitch with a 3D map style to see buildings and terrain in perspective. Higher pitch values create a more dramatic 3D effect. --- # Flutter Setup with MapMetrics https://docs.mapatlas.xyz/sdk/examples/flutter-setup # Flutter Setup with MapMetrics This guide will walk you through setting up a Flutter project to use MapMetrics Atlas API with the MapMetrics Flutter package. ## Step 1: Create a New Flutter Project First, create a new Flutter project: ```bash flutter create mapmetrics_demo cd mapmetrics_demo ``` ## Step 2: Add Dependencies Open your `pubspec.yaml` file and add the MapMetrics dependency: ```yaml dependencies: flutter: sdk: flutter mapmetrics: ^1.0.6 cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 ``` Then run: ```bash flutter pub get ``` ## Step 3: Platform Configuration ### Android Configuration 1. **Update Android Manifest** (`android/app/src/main/AndroidManifest.xml`): ```xml ``` 2. **Update build.gradle** (`android/app/build.gradle`): ```gradle android { compileSdkVersion 33 defaultConfig { minSdkVersion 21 targetSdkVersion 33 } } ``` ### iOS Configuration 1. **Update Info.plist** (`ios/Runner/Info.plist`): ```xml NSLocationWhenInUseUsageDescription This app needs access to location when open to show your position on the map. NSLocationAlwaysUsageDescription This app needs access to location when in the background to show your position on the map. ``` 2. **Update Podfile** (`ios/Podfile`): ```ruby platform :ios, '12.0' ``` ## Step 4: Get MapMetrics Credentials 1. **Create API Key**: - Visit [MapMetrics Portal](https://portal.mapmetrics.org) - Sign up or log in - Go to "Keys" section - Click "New Key" - Configure permissions and allowed domains - Copy your API key 2. **Create Map Style**: - Go to "Styles" section in the portal - Click "New Style" - Choose a template or create custom - Customize colors, fonts, and features - Save and copy the style URL ## Step 5: Create Your First Map Replace the contents of `lib/main.dart`: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'MapMetrics Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MapScreen(), ); } } class MapScreen extends StatefulWidget { @override _MapScreenState createState() => _MapScreenState(); } class _MapScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MapMetrics Atlas Map'), ), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { setState(() { mapController = controller; }); }, initialCameraPosition: CameraPosition( target: LatLng(40.7128, -74.0060), // New York City zoom: 10.0, ), ), ); } } ``` ## Step 6: Replace Placeholder Values Replace the following in your code: - `YOUR_STYLE_ID`: Your MapMetrics style ID - `YOUR_API_KEY`: Your MapMetrics API key ## Step 7: Run the App ```bash flutter run ``` ## Step 8: Add Attribution (Required) Add the required attribution to your app. You can do this in your app's about section or settings: ```dart Widget buildAttribution() { return Column( children: [ Text('© MapMetrics'), Text('© OSM contributors'), ], ); } ``` ## Troubleshooting ### Common Issues 1. **Build Errors**: Make sure you're using Flutter 3.0.0+ and have the correct SDK versions 2. **Map Not Loading**: Verify your API key and style URL are correct 3. **Permission Errors**: Ensure you've added the required permissions in Android/iOS configs 4. **Network Issues**: Check that your device has internet access ### Debug Mode Enable debug mode to see detailed logs: ```dart MapMetrics( styleUrl: 'your_style_url', onMapCreated: (controller) { controller.setDebugMode(true); }, ) ``` ## Next Steps Now that you have a basic setup working, try the [Basic Map Tutorial](./flutter-basic-map) to learn more about map interactions and customization. ## Configuration Options You can customize your map with various options: ```dart MapMetrics( styleUrl: 'your_style_url', initialCameraPosition: CameraPosition( target: LatLng(lat, lng), zoom: zoom, bearing: bearing, tilt: tilt, ), onMapCreated: (controller) { // Handle map creation }, onMapClick: (point, coordinates) { // Handle map clicks }, onStyleLoaded: () { // Handle style loading }, ) ``` --- **Remember**: Always keep your API keys secure and never commit them to version control. Use environment variables or secure storage for production apps. --- # Show Polygon Info on Click in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-show-polygon-info-on-click # Show Polygon Info on Click in Flutter This tutorial shows how to display information about a polygon when the user taps on it — useful for showing zone details, area statistics, or district information. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Tappable Polygons with Info Card Tap a polygon to see its details in a bottom card: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PolygonInfoScreen extends StatefulWidget { @override _PolygonInfoScreenState createState() => _PolygonInfoScreenState(); } class _PolygonInfoScreenState extends State { MapMetricsController? mapController; String? selectedZoneId; final List> zones = [ { 'id': 'zone_a', 'name': '1st Arrondissement', 'description': 'Historic center with the Louvre, Tuileries Garden, and Palais Royal.', 'population': '17,600', 'area': '1.83 km²', 'color': Colors.blue, 'points': [ LatLng(48.865, 2.327), LatLng(48.865, 2.345), LatLng(48.856, 2.345), LatLng(48.856, 2.327), ], }, { 'id': 'zone_b', 'name': '4th Arrondissement', 'description': 'Home to Notre-Dame, Île de la Cité, and the Marais district.', 'population': '28,600', 'area': '1.60 km²', 'color': Colors.green, 'points': [ LatLng(48.858, 2.345), LatLng(48.858, 2.365), LatLng(48.848, 2.365), LatLng(48.848, 2.345), ], }, { 'id': 'zone_c', 'name': '5th Arrondissement', 'description': 'The Latin Quarter with the Panthéon and Luxembourg Gardens.', 'population': '60,200', 'area': '2.54 km²', 'color': Colors.orange, 'points': [ LatLng(48.852, 2.335), LatLng(48.852, 2.360), LatLng(48.842, 2.360), LatLng(48.842, 2.335), ], }, ]; Map? get selectedZone => selectedZoneId != null ? zones.firstWhere((z) => z['id'] == selectedZoneId) : null; Set get polygons => zones.map((zone) { final isSelected = zone['id'] == selectedZoneId; final Color color = zone['color']; return Polygon( polygonId: PolygonId(zone['id']), points: zone['points'], strokeWidth: isSelected ? 3 : 2, strokeColor: isSelected ? color : color.withOpacity(0.6), fillColor: isSelected ? color.withOpacity(0.35) : color.withOpacity(0.15), consumeTapEvents: true, onTap: () { setState(() { selectedZoneId = selectedZoneId == zone['id'] ? null : zone['id']; }); }, ); }).toSet(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('District Info')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, onMapClick: (Point point, LatLng coordinates) { // Dismiss info when tapping outside polygons setState(() => selectedZoneId = null); }, initialCameraPosition: CameraPosition( target: LatLng(48.855, 2.347), zoom: 14.0, ), polygons: polygons, ), // Info card if (selectedZone != null) Positioned( bottom: 24, left: 16, right: 16, child: Card( elevation: 6, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 14, height: 14, decoration: BoxDecoration( color: selectedZone!['color'], borderRadius: BorderRadius.circular(3), ), ), SizedBox(width: 8), Expanded( child: Text( selectedZone!['name'], style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: Icon(Icons.close, size: 20), onPressed: () => setState(() => selectedZoneId = null), padding: EdgeInsets.zero, constraints: BoxConstraints(), ), ], ), SizedBox(height: 8), Text( selectedZone!['description'], style: TextStyle(color: Colors.grey[700], fontSize: 14), ), SizedBox(height: 12), Row( children: [ _statChip(Icons.people, 'Population', selectedZone!['population']), SizedBox(width: 16), _statChip( Icons.square_foot, 'Area', selectedZone!['area']), ], ), ], ), ), ), ), ], ), ); } Widget _statChip(IconData icon, String label, String value) { return Row( children: [ Icon(icon, size: 16, color: Colors.grey), SizedBox(width: 4), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: TextStyle(fontSize: 11, color: Colors.grey)), Text(value, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), ], ), ], ); } } ``` ## Highlighting the Selected Polygon The selected polygon gets: - Thicker border (`strokeWidth: 3`) - More opaque fill (`0.35` vs `0.15`) - Full-color stroke instead of semi-transparent This visual feedback makes it clear which zone is selected. ## Next Steps - [Add a Polygon](./flutter-add-a-polygon) — Basic polygon drawing - [Popup on Click](./flutter-popup-on-click) — Popups on markers - [Multiple Geometries](./flutter-multiple-geometries) — Mix polygons with other shapes --- **Tip**: Use `consumeTapEvents: true` on polygons so taps are captured by the polygon and don't fall through to the map's `onMapClick` handler. --- # Sky, Fog, and Terrain in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-sky-fog-terrain # Sky, Fog, and Terrain in Flutter This tutorial shows how to add atmospheric effects — sky gradient, fog, and terrain — for immersive 3D map experiences. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Sky and Terrain Atmosphere Create an immersive view with sky, fog, and 3D terrain: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SkyFogTerrainScreen extends StatefulWidget { @override _SkyFogTerrainScreenState createState() => _SkyFogTerrainScreenState(); } class _SkyFogTerrainScreenState extends State { MapMetricsController? mapController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Sky, Fog & Terrain')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(46.8182, 8.2275), // Swiss Alps zoom: 10.0, tilt: 70.0, bearing: 30.0, ), onStyleLoaded: () { _addAtmosphericEffects(); }, ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton.small( heroTag: 'immersive', onPressed: _setImmersiveView, child: Icon(Icons.landscape), tooltip: 'Immersive', ), SizedBox(height: 8), FloatingActionButton.small( heroTag: 'overhead', onPressed: _setOverheadView, child: Icon(Icons.map), tooltip: 'Overhead', ), ], ), ); } void _addAtmosphericEffects() { // Add terrain mapController?.addRasterDemSource( 'terrain-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.setTerrain('terrain-source', exaggeration: 1.5); // Add sky layer for a blue sky gradient mapController?.addSkyLayer( 'sky', skyType: 'gradient', skyGradient: [ 'interpolate', ['linear'], ['sky-radial-progress'], 0.8, '#87CEEB', // Light blue at horizon 1.0, '#1E3A5F', // Dark blue at zenith ], skyGradientCenter: [0, 0], skyGradientRadius: 90, ); // Add fog for depth perception mapController?.setFog( color: '#ffffff', highColor: '#87CEEB', horizonBlend: 0.1, range: [0.5, 10.0], ); } void _setImmersiveView() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(46.8182, 8.2275), zoom: 10.0, tilt: 70.0, bearing: 30.0, ), ), ); } void _setOverheadView() { mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng(46.8182, 8.2275), zoom: 10.0, tilt: 0.0, bearing: 0.0, ), ), ); } } ``` ## Customizable Atmosphere Let users control fog density and sky color: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CustomAtmosphereScreen extends StatefulWidget { @override _CustomAtmosphereScreenState createState() => _CustomAtmosphereScreenState(); } class _CustomAtmosphereScreenState extends State { MapMetricsController? mapController; double fogDensity = 0.1; String timeOfDay = 'day'; // day, sunset, night final Map> atmospheres = { 'day': { 'fogColor': '#ffffff', 'skyLow': '#87CEEB', 'skyHigh': '#1E3A5F', 'name': 'Day', }, 'sunset': { 'fogColor': '#FFE0B2', 'skyLow': '#FF6F00', 'skyHigh': '#4A148C', 'name': 'Sunset', }, 'night': { 'fogColor': '#1a1a2e', 'skyLow': '#16213e', 'skyHigh': '#0f0f1a', 'name': 'Night', }, }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Custom Atmosphere')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(45.9763, 7.6586), // Matterhorn zoom: 11.0, tilt: 65.0, bearing: 200.0, ), onStyleLoaded: () { _setupTerrain(); _applyAtmosphere(); }, ), // Controls panel Positioned( bottom: 16, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Time of day selector Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: atmospheres.entries.map((entry) { final isActive = timeOfDay == entry.key; return ChoiceChip( label: Text(entry.value['name']!), selected: isActive, onSelected: (selected) { if (selected) { setState(() => timeOfDay = entry.key); _applyAtmosphere(); } }, ); }).toList(), ), SizedBox(height: 8), // Fog density Row( children: [ Icon(Icons.cloud, size: 18, color: Colors.grey), SizedBox(width: 8), Text('Fog'), Expanded( child: Slider( value: fogDensity, min: 0.0, max: 0.5, onChanged: (val) { setState(() => fogDensity = val); _applyAtmosphere(); }, ), ), ], ), ], ), ), ), ), ], ), ); } void _setupTerrain() { mapController?.addRasterDemSource( 'terrain-source', 'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png', tileSize: 256, ); mapController?.setTerrain('terrain-source', exaggeration: 1.5); } void _applyAtmosphere() { final atm = atmospheres[timeOfDay]!; mapController?.setFog( color: atm['fogColor']!, highColor: atm['skyLow']!, horizonBlend: fogDensity, range: [0.5, 10.0], ); // Remove and re-add sky layer with new colors mapController?.removeLayer('sky'); mapController?.addSkyLayer( 'sky', skyType: 'gradient', skyGradient: [ 'interpolate', ['linear'], ['sky-radial-progress'], 0.8, atm['skyLow']!, 1.0, atm['skyHigh']!, ], skyGradientCenter: [0, 0], skyGradientRadius: 90, ); } } ``` ## Atmosphere Properties | Property | Description | |----------|-------------| | **Sky** | | | `skyType` | `gradient` or `atmosphere` | | `skyGradient` | Color interpolation expression | | `skyGradientRadius` | Radius of the gradient (degrees) | | **Fog** | | | `color` | Fog color near the camera | | `highColor` | Fog color at the horizon | | `horizonBlend` | Blend amount (0.0 = no fog, 0.5 = heavy) | | `range` | Start and end distance of fog | | **Terrain** | | | `exaggeration` | Height multiplier (0.0 - 3.0) | ## Next Steps - [3D Terrain](./flutter-3d-terrain) — Terrain elevation basics - [3D Buildings](./flutter-3d-buildings) — Extruded buildings - [Satellite Terrain](./flutter-satellite-terrain) — Satellite with elevation --- **Tip**: Sky and fog effects are most visible at high tilt angles (60-80 degrees). Set `tilt: 70.0` for the most dramatic atmosphere. At `tilt: 0.0` (overhead view), the sky and fog are invisible. --- # Slowly Fly to a Location in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-slowly-fly-to-location # Slowly Fly to a Location in Flutter This tutorial shows how to create a slow, cinematic camera flight across the map — great for storytelling, presentations, or showcasing a journey. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Basic Slow Flight Use a long `duration` on `animateCamera` for a cinematic effect: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SlowFlyScreen extends StatefulWidget { @override _SlowFlyScreenState createState() => _SlowFlyScreenState(); } class _SlowFlyScreenState extends State { MapMetricsController? mapController; String currentDestination = ''; final List> destinations = [ { 'name': 'Eiffel Tower', 'position': LatLng(48.8584, 2.2945), 'zoom': 16.0, 'bearing': 30.0, 'tilt': 55.0, }, { 'name': 'Colosseum, Rome', 'position': LatLng(41.8902, 12.4922), 'zoom': 16.0, 'bearing': -20.0, 'tilt': 50.0, }, { 'name': 'Santorini, Greece', 'position': LatLng(36.3932, 25.4615), 'zoom': 14.0, 'bearing': 60.0, 'tilt': 45.0, }, { 'name': 'Grand Canyon', 'position': LatLng(36.1069, -112.1129), 'zoom': 14.0, 'bearing': -40.0, 'tilt': 50.0, }, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Cinematic Flight')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8584, 2.2945), zoom: 5.0, ), ), // Destination buttons Positioned( bottom: 24, left: 16, right: 16, child: Column( mainAxisSize: MainAxisSize.min, children: [ // Current destination label if (currentDestination.isNotEmpty) Container( margin: EdgeInsets.only(bottom: 12), padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(8), ), child: Text( 'Flying to $currentDestination...', style: TextStyle(color: Colors.white, fontSize: 15), ), ), // Buttons SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: destinations.map((dest) { return Padding( padding: EdgeInsets.only(right: 8), child: ElevatedButton( onPressed: () => _slowFlyTo(dest), style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: Colors.black87, elevation: 4, ), child: Text(dest['name']), ), ); }).toList(), ), ), ], ), ), ], ), ); } void _slowFlyTo(Map destination) { setState(() { currentDestination = destination['name']; }); mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: destination['position'], zoom: destination['zoom'], bearing: destination['bearing'], tilt: destination['tilt'], ), ), duration: Duration(seconds: 5), // Slow cinematic flight ); // Clear label after flight completes Future.delayed(Duration(seconds: 6), () { if (mounted) { setState(() => currentDestination = ''); } }); } } ``` ## Storytelling Sequence Play a sequence of slow flights with text narration: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; import 'dart:async'; class StoryMapScreen extends StatefulWidget { @override _StoryMapScreenState createState() => _StoryMapScreenState(); } class _StoryMapScreenState extends State { MapMetricsController? mapController; int currentStep = 0; bool isPlaying = false; Timer? storyTimer; final List> story = [ { 'title': 'Our journey begins in Paris', 'subtitle': 'The City of Light', 'position': LatLng(48.8566, 2.3522), 'zoom': 12.0, 'bearing': 0.0, 'tilt': 0.0, }, { 'title': 'The Eiffel Tower', 'subtitle': 'Built in 1889 for the World\'s Fair', 'position': LatLng(48.8584, 2.2945), 'zoom': 16.0, 'bearing': 30.0, 'tilt': 55.0, }, { 'title': 'Along the Seine', 'subtitle': 'The river that divides Paris', 'position': LatLng(48.8580, 2.3200), 'zoom': 15.0, 'bearing': 90.0, 'tilt': 45.0, }, { 'title': 'Notre-Dame Cathedral', 'subtitle': 'A masterpiece of Gothic architecture', 'position': LatLng(48.8530, 2.3499), 'zoom': 17.0, 'bearing': -30.0, 'tilt': 50.0, }, { 'title': 'Sacré-Cœur', 'subtitle': 'The highest point in Paris', 'position': LatLng(48.8867, 2.3431), 'zoom': 16.0, 'bearing': 180.0, 'tilt': 55.0, }, ]; @override Widget build(BuildContext context) { final step = story[currentStep]; return Scaffold( body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: story[0]['position'], zoom: story[0]['zoom'], ), ), // Story overlay Positioned( top: 60, left: 20, right: 20, child: Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( step['title'], style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), SizedBox(height: 4), Text( step['subtitle'], style: TextStyle(color: Colors.white70, fontSize: 14), ), SizedBox(height: 8), Text( '${currentStep + 1} / ${story.length}', style: TextStyle(color: Colors.white54, fontSize: 12), ), ], ), ), ), // Controls Positioned( bottom: 40, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FloatingActionButton.small( heroTag: 'prev', onPressed: currentStep > 0 ? _previous : null, child: Icon(Icons.skip_previous), ), SizedBox(width: 12), FloatingActionButton( heroTag: 'play', onPressed: _toggleAutoPlay, child: Icon(isPlaying ? Icons.pause : Icons.play_arrow), ), SizedBox(width: 12), FloatingActionButton.small( heroTag: 'next', onPressed: currentStep < story.length - 1 ? _next : null, child: Icon(Icons.skip_next), ), ], ), ), ], ), ); } void _navigateToStep(int index) { final step = story[index]; mapController?.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: step['position'], zoom: step['zoom'], bearing: step['bearing'], tilt: step['tilt'], ), ), duration: Duration(seconds: 4), ); } void _next() { if (currentStep < story.length - 1) { setState(() => currentStep++); _navigateToStep(currentStep); } } void _previous() { if (currentStep > 0) { setState(() => currentStep--); _navigateToStep(currentStep); } } void _toggleAutoPlay() { if (isPlaying) { storyTimer?.cancel(); setState(() => isPlaying = false); } else { setState(() => isPlaying = true); _navigateToStep(currentStep); storyTimer = Timer.periodic(Duration(seconds: 6), (_) { if (currentStep < story.length - 1) { _next(); } else { storyTimer?.cancel(); setState(() => isPlaying = false); } }); } } @override void dispose() { storyTimer?.cancel(); mapController?.dispose(); super.dispose(); } } ``` ## Next Steps - [Fly to a Location](./flutter-fly-to-location) — Faster fly-to animations - [Jump to Locations](./flutter-jump-to-locations) — Instant jumps between stops - [Animate Camera Around Point](./flutter-animate-camera-around-point) — Orbiting camera --- **Tip**: Combine slow flights with changing `bearing` and `tilt` at each stop for a truly cinematic experience. Give each flight 4–6 seconds for the best visual effect. --- # Sync Multiple Maps in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-sync-multiple-maps # Sync Multiple Maps in Flutter This tutorial shows how to display two maps side by side and keep their camera positions synchronized so when you move one, the other follows. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Side-by-Side Synced Maps Two maps with different styles, sharing the same camera position: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class SyncMapsScreen extends StatefulWidget { @override _SyncMapsScreenState createState() => _SyncMapsScreenState(); } class _SyncMapsScreenState extends State { MapMetricsController? mapControllerA; MapMetricsController? mapControllerB; bool isSyncing = false; // Prevent infinite sync loops final CameraPosition initialPosition = CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Synced Maps')), body: Column( children: [ // Labels Row( children: [ Expanded( child: Container( padding: EdgeInsets.all(8), color: Colors.blue[50], child: Text('Light Style', textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.bold)), ), ), Expanded( child: Container( padding: EdgeInsets.all(8), color: Colors.grey[800], child: Text('Dark Style', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.bold, color: Colors.white)), ), ), ], ), // Maps Expanded( child: Row( children: [ // Map A (Light) Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_LIGHT_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapControllerA = controller, onCameraMove: (position) => _syncToB(position), onCameraIdle: () => isSyncing = false, initialCameraPosition: initialPosition, ), ), // Divider Container(width: 2, color: Colors.grey[400]), // Map B (Dark) Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_DARK_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapControllerB = controller, onCameraMove: (position) => _syncToA(position), onCameraIdle: () => isSyncing = false, initialCameraPosition: initialPosition, ), ), ], ), ), ], ), ); } void _syncToB(CameraPosition position) { if (isSyncing) return; isSyncing = true; mapControllerB?.moveCamera( CameraUpdate.newCameraPosition(position), ); } void _syncToA(CameraPosition position) { if (isSyncing) return; isSyncing = true; mapControllerA?.moveCamera( CameraUpdate.newCameraPosition(position), ); } } ``` ## Stacked Comparison (Top/Bottom) Compare two styles in a vertical layout: ```dart Expanded( child: Column( children: [ // Map A (top half) Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_A?token=YOUR_API_KEY', onMapCreated: (controller) => mapControllerA = controller, onCameraMove: (position) => _syncToB(position), initialCameraPosition: initialPosition, ), ), Container(height: 2, color: Colors.grey), // Map B (bottom half) Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_B?token=YOUR_API_KEY', onMapCreated: (controller) => mapControllerB = controller, onCameraMove: (position) => _syncToA(position), initialCameraPosition: initialPosition, ), ), ], ), ) ``` ## Before/After Slider A swipeable comparison with a divider line: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class BeforeAfterMapScreen extends StatefulWidget { @override _BeforeAfterMapScreenState createState() => _BeforeAfterMapScreenState(); } class _BeforeAfterMapScreenState extends State { MapMetricsController? mapControllerA; MapMetricsController? mapControllerB; double dividerPosition = 0.5; // 0.0 to 1.0 bool isSyncing = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Before / After')), body: LayoutBuilder( builder: (context, constraints) { final dividerX = constraints.maxWidth * dividerPosition; return GestureDetector( onHorizontalDragUpdate: (details) { setState(() { dividerPosition = (details.localPosition.dx / constraints.maxWidth) .clamp(0.15, 0.85); }); }, child: Stack( children: [ // Map B (full width, underneath) MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_DARK_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapControllerB = controller, onCameraMove: (pos) { if (!isSyncing) { isSyncing = true; mapControllerA?.moveCamera( CameraUpdate.newCameraPosition(pos)); } }, onCameraIdle: () => isSyncing = false, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), ), // Map A (clipped to left of divider) ClipRect( clipper: _LeftClipper(dividerX), child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_LIGHT_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapControllerA = controller, onCameraMove: (pos) { if (!isSyncing) { isSyncing = true; mapControllerB?.moveCamera( CameraUpdate.newCameraPosition(pos)); } }, onCameraIdle: () => isSyncing = false, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), ), ), // Divider line Positioned( left: dividerX - 2, top: 0, bottom: 0, child: Container( width: 4, color: Colors.white, child: Center( child: Container( width: 28, height: 28, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: Icon(Icons.drag_handle, size: 16), ), ), ), ), ], ), ); }, ), ); } } class _LeftClipper extends CustomClipper { final double width; _LeftClipper(this.width); @override Rect getClip(Size size) => Rect.fromLTWH(0, 0, width, size.height); @override bool shouldReclip(_LeftClipper oldClipper) => oldClipper.width != width; } ``` ## Next Steps - [Custom Map Styling](./flutter-custom-styling) — Create different styles to compare - [Fullscreen Map](./flutter-fullscreen-map) — Immersive single-map view - [Set Pitch and Bearing](./flutter-set-pitch-and-bearing) — Synced 3D views --- **Tip**: Use the `isSyncing` flag to prevent infinite update loops where Map A updates Map B, which updates Map A again. Reset it on `onCameraIdle`. --- # Tap to Highlight Features in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-tap-highlight # Tap to Highlight Features in Flutter This tutorial shows how to highlight map features when the user taps on them — the Flutter equivalent of hover effects on web maps. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Highlight Tapped Marker Change a marker's appearance when tapped: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class TapHighlightScreen extends StatefulWidget { @override _TapHighlightScreenState createState() => _TapHighlightScreenState(); } class _TapHighlightScreenState extends State { MapMetricsController? mapController; String? selectedMarkerId; final List> cities = [ {'id': 'paris', 'name': 'Paris', 'lat': 48.8566, 'lng': 2.3522}, {'id': 'london', 'name': 'London', 'lat': 51.5074, 'lng': -0.1276}, {'id': 'berlin', 'name': 'Berlin', 'lat': 52.52, 'lng': 13.405}, {'id': 'rome', 'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964}, {'id': 'madrid', 'name': 'Madrid', 'lat': 40.4168, 'lng': -3.7038}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Tap to Highlight')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 5.0), zoom: 4.0, ), markers: _buildMarkers(), onMapClick: (Point point, LatLng coordinates) { // Deselect when tapping the map background setState(() { selectedMarkerId = null; }); }, ), // Info panel for selected city if (selectedMarkerId != null) Positioned( top: 16, left: 16, right: 16, child: _buildInfoPanel(), ), ], ), ); } Set _buildMarkers() { return cities.map((city) { final isSelected = city['id'] == selectedMarkerId; return Marker( markerId: MarkerId(city['id']), position: LatLng(city['lat'], city['lng']), icon: BitmapDescriptor.defaultMarkerWithHue( isSelected ? BitmapDescriptor.hueBlue : BitmapDescriptor.hueRed, ), infoWindow: InfoWindow(title: city['name']), onTap: () { setState(() { selectedMarkerId = city['id']; }); }, ); }).toSet(); } Widget _buildInfoPanel() { final city = cities.firstWhere((c) => c['id'] == selectedMarkerId); return Card( elevation: 4, child: ListTile( leading: Icon(Icons.location_on, color: Colors.blue, size: 32), title: Text(city['name'], style: TextStyle(fontWeight: FontWeight.bold)), subtitle: Text( 'Lat: ${city['lat']}, Lng: ${city['lng']}', ), trailing: IconButton( icon: Icon(Icons.close), onPressed: () { setState(() { selectedMarkerId = null; }); }, ), ), ); } } ``` ## Highlight GeoJSON Circles on Tap Tap on circle features to highlight them: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class CircleTapHighlightScreen extends StatefulWidget { @override _CircleTapHighlightScreenState createState() => _CircleTapHighlightScreenState(); } class _CircleTapHighlightScreenState extends State { MapMetricsController? mapController; int? highlightedIndex; final List> locations = [ {'name': 'Paris', 'lat': 48.8566, 'lng': 2.3522, 'visitors': '30M'}, {'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'visitors': '20M'}, {'name': 'Berlin', 'lat': 52.52, 'lng': 13.405, 'visitors': '14M'}, {'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964, 'visitors': '10M'}, {'name': 'Madrid', 'lat': 40.4168, 'lng': -3.7038, 'visitors': '7M'}, {'name': 'Amsterdam', 'lat': 52.3676, 'lng': 4.9041, 'visitors': '8M'}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Circle Tap Highlight')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.0, 5.0), zoom: 4.0, ), circles: _buildCircles(), onMapClick: (Point point, LatLng coordinates) { _checkCircleTap(coordinates); }, ), if (highlightedIndex != null) Positioned( bottom: 24, left: 16, right: 16, child: Card( elevation: 6, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(16), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), child: Icon(Icons.location_city, color: Colors.white, size: 24), ), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( locations[highlightedIndex!]['name'], style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold), ), Text( 'Annual visitors: ${locations[highlightedIndex!]['visitors']}', style: TextStyle(color: Colors.grey[600]), ), ], ), ), IconButton( icon: Icon(Icons.close), onPressed: () { setState(() { highlightedIndex = null; }); }, ), ], ), ), ), ), ], ), ); } Set _buildCircles() { return locations.asMap().entries.map((entry) { final i = entry.key; final loc = entry.value; final isHighlighted = i == highlightedIndex; return Circle( circleId: CircleId('circle_$i'), center: LatLng(loc['lat'], loc['lng']), radius: isHighlighted ? 60000 : 40000, // meters fillColor: isHighlighted ? Colors.blue.withOpacity(0.5) : Colors.blue.withOpacity(0.2), strokeColor: isHighlighted ? Colors.blue : Colors.blue.withOpacity(0.6), strokeWidth: isHighlighted ? 3 : 1, ); }).toSet(); } void _checkCircleTap(LatLng tapPosition) { // Find the closest location within a threshold int? closestIndex; double closestDistance = double.infinity; for (int i = 0; i < locations.length; i++) { final loc = locations[i]; final distance = _approximateDistance( tapPosition.latitude, tapPosition.longitude, loc['lat'], loc['lng'], ); if (distance < 0.5 && distance < closestDistance) { // ~0.5 degrees threshold closestDistance = distance; closestIndex = i; } } setState(() { highlightedIndex = closestIndex; }); } /// Simple approximate distance in degrees double _approximateDistance( double lat1, double lng1, double lat2, double lng2) { final dLat = (lat1 - lat2); final dLng = (lng1 - lng2); return (dLat * dLat + dLng * dLng); } } ``` ## Highlight Polygon Region on Tap Tap on a polygon region to highlight it: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PolygonTapHighlightScreen extends StatefulWidget { @override _PolygonTapHighlightScreenState createState() => _PolygonTapHighlightScreenState(); } class _PolygonTapHighlightScreenState extends State { MapMetricsController? mapController; String? selectedRegion; final Map> regions = { 'North': [ LatLng(49.0, 1.0), LatLng(49.0, 4.0), LatLng(51.0, 4.0), LatLng(51.0, 1.0), LatLng(49.0, 1.0), ], 'East': [ LatLng(47.0, 4.0), LatLng(47.0, 8.0), LatLng(49.0, 8.0), LatLng(49.0, 4.0), LatLng(47.0, 4.0), ], 'South': [ LatLng(43.0, 1.0), LatLng(43.0, 4.0), LatLng(45.0, 4.0), LatLng(45.0, 1.0), LatLng(43.0, 1.0), ], }; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Polygon Tap Highlight')), body: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(47.0, 4.0), zoom: 5.0, ), polygons: regions.entries.map((entry) { final isSelected = entry.key == selectedRegion; return Polygon( polygonId: PolygonId(entry.key), points: entry.value, fillColor: isSelected ? Colors.blue.withOpacity(0.5) : Colors.grey.withOpacity(0.2), strokeColor: isSelected ? Colors.blue : Colors.grey, strokeWidth: isSelected ? 3 : 1, consumeTapEvents: true, onTap: () { setState(() { selectedRegion = selectedRegion == entry.key ? null : entry.key; }); }, ); }).toSet(), ), ); } } ``` ## Next Steps - [Popup on Click](./flutter-popup-on-click) — Show popups when tapping features - [Show Polygon Info on Click](./flutter-show-polygon-info-on-click) — Display region details - [Filter Markers](./flutter-filter-markers) — Show/hide markers by category --- **Tip**: On mobile, use tap instead of hover for interactivity. Combine highlighted state with `setState()` to redraw markers, circles, or polygons with different styles instantly. --- # Toggle Map Interactions in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-toggle-interactions # Toggle Map Interactions in Flutter This tutorial shows how to enable and disable individual map interactions like zoom, pan, rotate, and tilt at runtime. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Complete Example: Interaction Control Panel A full control panel that lets users toggle each gesture type independently: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class ToggleInteractionsScreen extends StatefulWidget { @override _ToggleInteractionsScreenState createState() => _ToggleInteractionsScreenState(); } class _ToggleInteractionsScreenState extends State { MapMetricsController? mapController; bool zoomGestures = true; bool scrollGestures = true; bool rotateGestures = true; bool tiltGestures = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Toggle Interactions'), actions: [ TextButton( onPressed: _enableAll, child: Text('All ON', style: TextStyle(color: Colors.white)), ), TextButton( onPressed: _disableAll, child: Text('All OFF', style: TextStyle(color: Colors.white70)), ), ], ), body: Column( children: [ // Interaction toggles Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), color: Colors.grey[100], child: Column( children: [ _interactionToggle( 'Zoom (pinch)', Icons.zoom_in, zoomGestures, (v) => setState(() => zoomGestures = v), ), _interactionToggle( 'Scroll (pan)', Icons.pan_tool, scrollGestures, (v) => setState(() => scrollGestures = v), ), _interactionToggle( 'Rotate (two-finger)', Icons.rotate_right, rotateGestures, (v) => setState(() => rotateGestures = v), ), _interactionToggle( 'Tilt (two-finger vertical)', Icons.view_in_ar, tiltGestures, (v) => setState(() => tiltGestures = v), ), ], ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), zoomGesturesEnabled: zoomGestures, scrollGesturesEnabled: scrollGestures, rotateGesturesEnabled: rotateGestures, tiltGesturesEnabled: tiltGestures, ), ), ], ), ); } Widget _interactionToggle( String label, IconData icon, bool value, ValueChanged onChanged, ) { return SwitchListTile( title: Row( children: [ Icon(icon, size: 20, color: value ? Colors.blue : Colors.grey), SizedBox(width: 8), Text(label, style: TextStyle(fontSize: 14)), ], ), value: value, onChanged: onChanged, dense: true, contentPadding: EdgeInsets.symmetric(horizontal: 8), ); } void _enableAll() { setState(() { zoomGestures = true; scrollGestures = true; rotateGestures = true; tiltGestures = true; }); } void _disableAll() { setState(() { zoomGestures = false; scrollGestures = false; rotateGestures = false; tiltGestures = false; }); } } ``` ## Presentation Mode A common use case: lock all interactions for a presentation or kiosk display, then unlock with a button: ```dart import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class PresentationModeScreen extends StatefulWidget { @override _PresentationModeScreenState createState() => _PresentationModeScreenState(); } class _PresentationModeScreenState extends State { MapMetricsController? mapController; bool isLocked = true; @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (controller) => mapController = controller, initialCameraPosition: CameraPosition( target: LatLng(48.8566, 2.3522), zoom: 13.0, ), zoomGesturesEnabled: !isLocked, scrollGesturesEnabled: !isLocked, rotateGesturesEnabled: !isLocked, tiltGesturesEnabled: !isLocked, ), // Lock indicator and toggle Positioned( top: 50, right: 16, child: GestureDetector( onTap: () => setState(() => isLocked = !isLocked), child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: isLocked ? Colors.red : Colors.green, borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isLocked ? Icons.lock : Icons.lock_open, color: Colors.white, size: 16, ), SizedBox(width: 6), Text( isLocked ? 'Locked' : 'Unlocked', style: TextStyle(color: Colors.white, fontSize: 13), ), ], ), ), ), ), ], ), ); } } ``` ## Interaction Properties Reference | Property | Gesture | Default | |----------|---------|---------| | `zoomGesturesEnabled` | Pinch-to-zoom, double-tap zoom | `true` | | `scrollGesturesEnabled` | Drag/pan to move the map | `true` | | `rotateGesturesEnabled` | Two-finger rotation | `true` | | `tiltGesturesEnabled` | Two-finger vertical swipe (3D tilt) | `true` | ## Next Steps - [Disable Scroll Zoom](./flutter-disable-scroll-zoom) — Focus on zoom control for embedded maps - [Navigation Controls](./flutter-navigation-controls) — Add UI buttons when gestures are off - [Map Interactions](./flutter-interactions) — Full interaction handling with events --- **Tip**: When disabling gestures, always provide alternative controls (buttons, sliders) so users can still navigate the map. --- # Update a Feature in Realtime in Flutter https://docs.mapatlas.xyz/sdk/examples/flutter-update-feature-realtime # Update a Feature in Realtime in Flutter This tutorial shows how to update map features (markers, polylines, circles) in real-time — essential for live tracking, IoT dashboards, and real-time data visualization. ## Prerequisites Before you begin, ensure you have: - Completed the [Flutter Setup Guide](./flutter-setup) - A MapMetrics API key and style URL from the [MapMetrics Portal](https://portal.mapmetrics.org) ## Live Marker Position Update Simulate a moving vehicle by updating a marker's position at regular intervals: ```dart import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RealtimeMarkerScreen extends StatefulWidget { @override _RealtimeMarkerScreenState createState() => _RealtimeMarkerScreenState(); } class _RealtimeMarkerScreenState extends State { MapMetricsController? mapController; Timer? updateTimer; LatLng vehiclePosition = LatLng(48.8566, 2.3522); List trail = []; final random = Random(); bool isTracking = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Live Tracking'), actions: [ Switch( value: isTracking, onChanged: (value) { if (value) { _startTracking(); } else { _stopTracking(); } }, activeColor: Colors.white, ), ], ), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: vehiclePosition, zoom: 14.0, ), markers: { Marker( markerId: MarkerId('vehicle'), position: vehiclePosition, icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue), infoWindow: InfoWindow(title: 'Vehicle'), ), }, polylines: trail.length > 1 ? { Polyline( polylineId: PolylineId('trail'), points: trail, color: Colors.blue.withOpacity(0.5), width: 3, ), } : {}, ), // Status indicator Positioned( top: 16, left: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: isTracking ? Colors.green : Colors.grey, borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), ), SizedBox(width: 6), Text( isTracking ? 'LIVE' : 'OFFLINE', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold), ), ], ), ), ), ], ), ); } void _startTracking() { setState(() { isTracking = true; trail = [vehiclePosition]; }); updateTimer = Timer.periodic(Duration(seconds: 1), (_) { // Simulate GPS update (small random movement) setState(() { vehiclePosition = LatLng( vehiclePosition.latitude + (random.nextDouble() - 0.4) * 0.002, vehiclePosition.longitude + (random.nextDouble() - 0.3) * 0.002, ); trail.add(vehiclePosition); }); }); } void _stopTracking() { updateTimer?.cancel(); setState(() { isTracking = false; }); } @override void dispose() { updateTimer?.cancel(); super.dispose(); } } ``` ## Live Circle Radius Update Update a circle's radius in real-time to show an expanding search area: ```dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class RealtimeCircleScreen extends StatefulWidget { @override _RealtimeCircleScreenState createState() => _RealtimeCircleScreenState(); } class _RealtimeCircleScreenState extends State { MapMetricsController? mapController; Timer? expandTimer; double searchRadius = 200.0; // meters bool isSearching = false; final LatLng center = LatLng(48.8566, 2.3522); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Expanding Search')), body: Stack( children: [ MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: center, zoom: 14.0, ), circles: { Circle( circleId: CircleId('search_area'), center: center, radius: searchRadius, fillColor: Colors.blue.withOpacity(0.1), strokeColor: Colors.blue, strokeWidth: 2, ), }, markers: { Marker( markerId: MarkerId('center'), position: center, infoWindow: InfoWindow(title: 'Search Center'), ), }, ), // Radius display Positioned( bottom: 90, left: 0, right: 0, child: Center( child: Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(20), ), child: Text( 'Radius: ${searchRadius.toInt()}m', style: TextStyle(color: Colors.white), ), ), ), ), // Slider control Positioned( bottom: 24, left: 16, right: 16, child: Card( child: Padding( padding: EdgeInsets.all(8), child: Column( mainAxisSize: MainAxisSize.min, children: [ Slider( value: searchRadius, min: 100, max: 2000, onChanged: (value) { setState(() { searchRadius = value; }); }, ), ElevatedButton( onPressed: isSearching ? null : _startExpanding, child: Text( isSearching ? 'Searching...' : 'Auto Expand'), ), ], ), ), ), ), ], ), ); } void _startExpanding() { setState(() { isSearching = true; searchRadius = 200; }); expandTimer = Timer.periodic(Duration(milliseconds: 50), (_) { if (searchRadius >= 2000) { expandTimer?.cancel(); setState(() { isSearching = false; }); return; } setState(() { searchRadius += 20; }); }); } @override void dispose() { expandTimer?.cancel(); super.dispose(); } } ``` ## Multiple Vehicles Dashboard Track multiple moving objects simultaneously: ```dart import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:mapmetrics/mapmetrics.dart'; class FleetTrackerScreen extends StatefulWidget { @override _FleetTrackerScreenState createState() => _FleetTrackerScreenState(); } class _FleetTrackerScreenState extends State { MapMetricsController? mapController; Timer? updateTimer; final random = Random(); List> vehicles = [ { 'id': 'bus_1', 'name': 'Bus 42', 'position': LatLng(48.860, 2.340), 'color': BitmapDescriptor.hueBlue, 'speed': '35 km/h', }, { 'id': 'bus_2', 'name': 'Bus 76', 'position': LatLng(48.850, 2.360), 'color': BitmapDescriptor.hueGreen, 'speed': '28 km/h', }, { 'id': 'bus_3', 'name': 'Bus 91', 'position': LatLng(48.870, 2.330), 'color': BitmapDescriptor.hueOrange, 'speed': '42 km/h', }, ]; @override void initState() { super.initState(); // Start live updates updateTimer = Timer.periodic(Duration(seconds: 2), (_) { setState(() { for (var vehicle in vehicles) { final pos = vehicle['position'] as LatLng; vehicle['position'] = LatLng( pos.latitude + (random.nextDouble() - 0.5) * 0.002, pos.longitude + (random.nextDouble() - 0.5) * 0.002, ); vehicle['speed'] = '${20 + random.nextInt(30)} km/h'; } }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Fleet Tracker')), body: Column( children: [ // Vehicle list Container( height: 80, child: ListView.builder( scrollDirection: Axis.horizontal, padding: EdgeInsets.all(8), itemCount: vehicles.length, itemBuilder: (context, i) { final v = vehicles[i]; return Card( child: InkWell( onTap: () { final pos = v['position'] as LatLng; mapController?.animateCamera( CameraUpdate.newLatLngZoom(pos, 15.0), ); }, child: Padding( padding: EdgeInsets.all(12), child: Row( children: [ Icon(Icons.directions_bus, color: Colors.blue), SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text(v['name'], style: TextStyle(fontWeight: FontWeight.bold)), Text(v['speed'], style: TextStyle( color: Colors.grey, fontSize: 12)), ], ), ], ), ), ), ); }, ), ), // Map Expanded( child: MapMetrics( styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY', onMapCreated: (MapMetricsController controller) { mapController = controller; }, initialCameraPosition: CameraPosition( target: LatLng(48.857, 2.345), zoom: 13.0, ), markers: vehicles.map((v) { return Marker( markerId: MarkerId(v['id']), position: v['position'] as LatLng, icon: BitmapDescriptor.defaultMarkerWithHue( v['color'] as double), infoWindow: InfoWindow( title: v['name'], snippet: v['speed'], ), ); }).toSet(), ), ), ], ), ); } @override void dispose() { updateTimer?.cancel(); super.dispose(); } } ``` ## Realtime Update Patterns | Pattern | Method | Use Case | |---------|--------|----------| | `Timer.periodic` | Poll at fixed interval | Simulated data, sensors | | `StreamBuilder` | React to stream events | WebSocket, Firebase | | `setState()` | Rebuild with new data | Any data source | ## Next Steps - [Animate Point Along Route](./flutter-animate-point-along-route) — Move along a path - [Animate a Marker](./flutter-animate-marker) — Marker animations - [Locate the User](./flutter-locate-user) — GPS position tracking --- **Tip**: For production real-time apps, use `StreamBuilder` with a WebSocket or Firebase Realtime Database instead of `Timer.periodic`. This is more efficient and battery-friendly as it only updates when new data arrives. --- # Fly to a Location Based on Scroll Position https://docs.mapatlas.xyz/sdk/examples/fly-to-location-on-scroll --- title: "Fly to a Location Based on Scroll Position" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "flyTo", "easeTo", "scroll", "IntersectionObserver"] tags: ["scroll", "flyTo", "camera", "animation", "scroll-driven", "waypoints", "narrative", "storytelling"] description: "Animate the map camera to different locations as the user scrolls down the page" --- # Fly to a Location Based on Scroll Position Trigger map camera animations based on scroll position — useful for narrative maps, story maps, and scroll-driven tours.

New York

The city that never sleeps. Located at the mouth of the Hudson River.

Paris

The City of Light, home to the Eiffel Tower and world-class cuisine.

Tokyo

Japan's bustling capital, blending ultramodern and traditional culture.

Rio de Janeiro

Famous for Carnival, Christ the Redeemer, and stunning beaches.

## Pattern: IntersectionObserver + flyTo The most robust approach uses `IntersectionObserver` to detect when a story section enters the viewport: ```javascript const steps = document.querySelectorAll('.step'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const { lng, lat, zoom } = entry.target.dataset; map.flyTo({ center: [parseFloat(lng), parseFloat(lat)], zoom: parseFloat(zoom), speed: 0.8, }); } }); }, { threshold: 0.5 }); // fire when 50% of the element is visible steps.forEach(step => observer.observe(step)); ``` Each story section holds its target location in `data-*` attributes: ```html

New York City

Description...

``` ## Pattern: Scroll Event + Progress For finer control using raw scroll position: ```javascript const waypoints = [ { center: [-74.0, 40.7], zoom: 10 }, { center: [2.35, 48.85], zoom: 10 }, { center: [139.7, 35.7], zoom: 10 }, ]; window.addEventListener('scroll', () => { const scrollFraction = window.scrollY / (document.body.scrollHeight - window.innerHeight); const index = Math.min( Math.floor(scrollFraction * waypoints.length), waypoints.length - 1 ); const wp = waypoints[index]; map.easeTo({ center: wp.center, zoom: wp.zoom, duration: 800 }); }); ``` ## Disable Map Interaction for Story Maps ```javascript const map = new mapmetricsgl.Map({ // ... interactive: false, // disable all user interaction }); // Or selectively: map.scrollZoom.disable(); map.dragPan.disable(); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Fly to a Location https://docs.mapatlas.xyz/sdk/examples/fly-to-location --- title: "Fly to a Location" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "flyTo"] tags: ["camera", "animation", "flyTo", "navigation"] description: "Smoothly animate the map camera to fly to any location" --- # Fly to a Location Smoothly animate the map camera to fly to any location using `flyTo`.
## How It Works Use `map.flyTo()` to animate the camera to any location. The map smoothly zooms out, pans across, then zooms back in. ## Basic Usage ```javascript map.flyTo({ center: [-74.006, 40.7128], // [longitude, latitude] zoom: 12, speed: 1.5, // Animation speed (default: 1.2) curve: 1.42 // Animation curve (default: 1.42) }); ``` ## Options | Option | Type | Description | |--------|------|-------------| | `center` | `[lng, lat]` | Target coordinates | | `zoom` | `number` | Target zoom level | | `speed` | `number` | Animation speed (higher = faster) | | `curve` | `number` | Zoom out curve during flight | | `bearing` | `number` | Target bearing in degrees | | `pitch` | `number` | Target pitch in degrees | | `essential` | `boolean` | If `true`, animation persists even with `prefers-reduced-motion` | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # View a Fullscreen Map https://docs.mapatlas.xyz/sdk/examples/fullscreen-map --- title: "View a Fullscreen Map" category: "controls" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "FullscreenControl"] tags: ["fullscreen", "controls", "FullscreenControl", "expand", "immersive"] description: "Add a fullscreen button that lets users expand the map to fill the entire screen" --- # View a Fullscreen Map Add a fullscreen button so users can expand the map to fill the entire browser window.

Click the ⤢ fullscreen button (top-right) to expand the map. Press Esc to exit.

## Basic Usage ```javascript // Add fullscreen button to the map map.addControl(new mapmetricsgl.FullscreenControl(), 'top-right'); ``` ## Fullscreen a Specific Container By default, `FullscreenControl` makes the map canvas fullscreen. You can instead fullscreen a parent container (useful if you have overlapping UI elements): ```javascript const container = document.getElementById('map-wrapper'); map.addControl(new mapmetricsgl.FullscreenControl({ container: container // fullscreen this element instead of the map canvas }), 'top-right'); ``` ## Listen to Fullscreen Events ```javascript map.on('resize', () => { // Map automatically resizes when entering/exiting fullscreen console.log('Map resized:', map.getCanvas().width, map.getCanvas().height); }); ``` ## Programmatic Fullscreen (without the control button) You can trigger fullscreen programmatically using the browser Fullscreen API: ```javascript const mapCanvas = map.getCanvas(); // Enter fullscreen mapCanvas.requestFullscreen(); // Exit fullscreen document.exitFullscreen(); // Check if currently fullscreen const isFullscreen = !!document.fullscreenElement; ``` ## Complete Example > ⚠️ **Note:** Fullscreen requires user interaction to trigger (browser security requirement). It will not work if called programmatically without a user gesture. --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Navigate the Map with Game-Like Controls https://docs.mapatlas.xyz/sdk/examples/game-controls-navigation --- title: "Navigate the Map with Game-Like Controls" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "easeTo", "getBearing", "getCenter", "getZoom", "keyboard", "panBy", "rotateTo"] tags: ["game controls", "keyboard", "WASD", "arrow keys", "navigate", "pan", "rotate", "first person"] description: "Control the map camera using keyboard keys like a game — WASD to pan, Q/E to rotate" --- # Navigate the Map with Game-Like Controls Use keyboard events to navigate the map like a game: **WASD** or arrow keys to pan, **Q/E** to rotate, **+/-** to zoom.
Controls: W / ↑ = Pan up  |  S / ↓ = Pan down  |  A / ← = Pan left  |  D / → = Pan right  |  Q = Rotate left  |  E = Rotate right  |  + = Zoom in  |  - = Zoom out
## Disable Built-In Keyboard Handler First, disable the map's default keyboard navigation to avoid conflicts: ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', keyboard: false, // disable default keyboard handler }); // Or at runtime map.keyboard.disable(); ``` ## Track Held Keys For smooth continuous movement, track which keys are currently held: ```javascript const keys = {}; document.addEventListener('keydown', e => keys[e.key] = true); document.addEventListener('keyup', e => keys[e.key] = false); ``` ## Game Loop Use `requestAnimationFrame` to act on held keys every frame: ```javascript function gameLoop() { const speed = 80; // pixels per frame if (keys['w'] || keys['ArrowUp']) map.panBy([0, -speed], { animate: false }); if (keys['s'] || keys['ArrowDown']) map.panBy([0, speed], { animate: false }); if (keys['a'] || keys['ArrowLeft']) map.panBy([-speed, 0], { animate: false }); if (keys['d'] || keys['ArrowRight']) map.panBy([ speed, 0], { animate: false }); if (keys['q']) map.setBearing(map.getBearing() - 2); if (keys['e']) map.setBearing(map.getBearing() + 2); if (keys['+']) map.setZoom(map.getZoom() + 0.05); if (keys['-']) map.setZoom(map.getZoom() - 0.05); requestAnimationFrame(gameLoop); } map.on('load', () => gameLoop()); ``` ## Key Camera Methods ```javascript map.panBy([dx, dy], options) // pan by pixel offset map.getBearing() // current bearing (degrees) map.setBearing(bearing) // set bearing instantly map.getZoom() // current zoom level map.setZoom(zoom) // set zoom instantly map.easeTo({ bearing, zoom, ... }) // smooth transition ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Get Features Under the Mouse Pointer https://docs.mapatlas.xyz/sdk/examples/get-features-under-mouse --- title: "Get Features Under the Mouse Pointer" category: "filtering" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "queryRenderedFeatures", "on('mousemove')", "on('click')"] tags: ["queryRenderedFeatures", "mouse", "pointer", "features", "query", "inspect", "click"] description: "Query and display map features under the mouse pointer using queryRenderedFeatures" --- # Get Features Under the Mouse Pointer Use `queryRenderedFeatures()` to inspect which map features are under the cursor on click or hover.
Click on any visible map feature to inspect it.
## queryRenderedFeatures Query all features visible at a point or within a bounding box: ```javascript // Query at mouse click point map.on('click', (e) => { const features = map.queryRenderedFeatures(e.point); console.log(features); // array of all features at that pixel }); // Query only specific layers map.on('click', (e) => { const features = map.queryRenderedFeatures(e.point, { layers: ['my-layer'] }); }); // Query within a bounding box (pixel area) const bbox = [[x1, y1], [x2, y2]]; const features = map.queryRenderedFeatures(bbox, { layers: ['my-layer'] }); ``` ## querySourceFeatures Query features directly from a source (including off-screen): ```javascript const features = map.querySourceFeatures('my-source', { sourceLayer: 'my-source-layer', // for vector tile sources filter: ['==', 'category', 'restaurant'] }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Google map to Mapmetrics https://docs.mapatlas.xyz/sdk/examples/google-map-to-mapmetrics # Google map to Mapmetrics ## Switch from Google Maps to Mapmetrics --- | **Skill Level** | **Language** | | ------------------- | ------------- | | 🟧🟧🟧 Intermediate | 🧠 JavaScript | --- ### 📝 Prerequisite > `Familiarity with front-end development concepts.` Are you using **Google Maps** and want to switch to **Mapmetrics**? You’ve come to the right place! This tutorial shows you how to use the [**Mapmetrics GL JS**](https://docs.mapatlas.xyz/overview/) JavaScript library to: - ✅ Create a web map - 📍 Add a marker to the map - 💬 Attach a popup to the marker All using methods similar to those in the **Google Maps JavaScript API**. --- #### 🌍 Live Demo 🧪 View the live map demo here: [**Open Map Demo**](https://docs.mapatlas.xyz/overview/sdk/examples/simple-map-cdn) --- ## Getting started This guide assumes that you are already familiar with the Google Maps JavaScript API and with front-end web development concepts including HTML, CSS, and JavaScript. To complete this tutorial, you will need: ### 🔧 Requirements - **A Mapmetrics access token**: Your access tokens are on the [Access Token page](https://portal.mapmetrics.org/keys/create) of your Developer Console. - **Mapmetrics GL JS**: [Mapmetrics GL JS](https://docs.mapatlas.xyz/overview/) is a JavaScript API for building web maps. - **A text editor**: Use the editor of your choice for writing HTML, CSS, and JavaScript. --- ## Create a webpage 1. Open your text editor and create a new file called `index.html`. 2. Paste the following code to set up a map-enabled web page: ```html
``` --- ## Initialize a web map ### Google Maps Example ```html ``` ### Mapmetrics GL JS Example ```html ``` - `container`: HTML element to contain the map. - Style options: 1. AtlasGlow (Light): `https://gateway.mapmetrics-atlas.net/styles/?fileName=fe60a384-0e5a-4960-bf04-b6ca740bc20c/AtlasGlow.json&token=${accessToken}` 2. MoonTrace (Dark): `https://gateway.mapmetrics-atlas.net/styles/?fileName=fe60a384-0e5a-4960-bf04-b6ca740bc20c/MoonTrace.json&token=${accessToken}` - `center` and `zoom` define the map's starting position. 💾 Save your file and open it in a browser to preview. 🔗 [**Open Map Demo**](https://docs.mapatlas.xyz/overview/sdk/examples/simple-map-cdn) --- ## Add a Marker ### Google Maps Example ```html ``` ### Mapmetrics GL JS Example ```html ``` - `setLngLat()`: Set the marker location. - `addTo()`: Attach marker to the map. --- ## Add interactivity ### Google Maps: Using `InfoWindow` ```html ``` ### Mapmetrics GL JS: Using `Popup` ```html ``` 🔗 [**Open Popup Demo**](https://docs.mapatlas.xyz/overview/sdk/examples/add-a-popup.html) --- ## Final Product Example --- ## Next Steps 🎉 **Congratulations!** You've built a fully interactive map using **Mapmetrics GL JS**. ### ✅ Recap - Map setup using Mapmetrics GL JS - Marker and Popup creation --- ### 📚 Keep Exploring - [Add a geometry](https://docs.mapatlas.xyz/overview/sdk/examples/add-a-geometry) - [Create and style cluster](https://docs.mapatlas.xyz/overview/sdk/examples/add-a-cluster) Visit the official documentation: - [Docs](https://docs.mapatlas.xyz/) - [Examples](https://docs.mapatlas.xyz/overview/sdk/) --- # Draw a Gradient Line https://docs.mapatlas.xyz/sdk/examples/gradient-line --- title: "Draw a Gradient Line" category: "lines-polygons" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "line-gradient", "line-progress", "interpolate"] tags: ["gradient", "line", "lineGradient", "color", "interpolate", "progress", "route", "styled"] description: "Draw a line with a color gradient along its length using the line-gradient paint property" --- # Draw a Gradient Line Apply a smooth color gradient along a line using the `line-gradient` paint property and `line-progress` expression.
## Requirements To use `line-gradient`, you **must** set `lineMetrics: true` on the source: ```javascript map.addSource('route', { type: 'geojson', lineMetrics: true, // ← required for line-gradient data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [...] } } }); ``` ## Gradient Paint Property ```javascript map.addLayer({ id: 'gradient-line', type: 'line', source: 'route', layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-width': 6, 'line-gradient': [ 'interpolate', ['linear'], ['line-progress'], // 0 = line start, 1 = line end 0, '#ef4444', // red at start 0.5, '#eab308', // yellow at midpoint 1, '#22c55e', // green at end ] } }); ``` ## Common Gradient Themes ```javascript // Speed (slow → fast) 'line-gradient': ['interpolate', ['linear'], ['line-progress'], 0, '#22c55e', // green (slow) 0.5, '#eab308', // yellow (medium) 1, '#ef4444' // red (fast) ] // Elevation (low → high) 'line-gradient': ['interpolate', ['linear'], ['line-progress'], 0, '#3b82f6', // blue (sea level) 0.5, '#84cc16', // lime (mid) 1, '#7c3aed' // purple (high) ] // Progress indicator 'line-gradient': ['interpolate', ['linear'], ['line-progress'], 0, '#3b82f6', // start color 1, '#a855f7' // end color ] ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Hexagon Layer — Data Aggregation https://docs.mapatlas.xyz/sdk/examples/hexagon-layer --- title: "Hexagon Layer — Data Aggregation" category: "data-visualization" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setPaintProperty", "setData"] tags: ["hexagon", "3d", "aggregation", "fill-extrusion", "heatmap", "density", "geojson"] description: "Aggregate point data into a 3D hexagonal grid — great for visualizing density, accidents, events, or any location-based data" --- # Hexagon Layer — Data Aggregation Aggregate thousands of data points into a 3D hexagonal grid. Each hexagon's height and color represent the number of points (accidents, events, etc.) within that cell. Adjust radius, coverage, and upper percentile in real time.
## How It Works Point data is binned into a **hexagonal grid** computed in JavaScript. Each hexagon's height and color map to the number of points inside it. Sliders update the grid live using `setData()` on the GeoJSON source. ### Key steps **1. Generate dummy accident points near UK cities** ```javascript const hotspots = [ { lng: -0.12, lat: 51.50, weight: 80 }, // London { lng: -2.24, lat: 53.48, weight: 40 }, // Manchester // ... ]; ``` **2. Build a hex grid and bin points into cells** ```javascript function buildHexGrid(radiusKm, coverage, upperPercentile, maxHeight) { const r = radiusKm * 1000; // convert to metres // For each hex cell centre, count points inside using axial coordinates // Apply upper percentile clipping so outliers don't dominate // Return GeoJSON FeatureCollection with height + color per feature } ``` **3. Render as 3D fill-extrusion layer** ```javascript map.addLayer({ id: 'hex-fill', type: 'fill-extrusion', source: 'hexagons', paint: { 'fill-extrusion-color': ['get', 'color'], // data-driven color 'fill-extrusion-height': ['get', 'height'], // data-driven height 'fill-extrusion-opacity': 0.85, } }); ``` **4. Update live when sliders change** ```javascript slider.addEventListener('input', () => { const newGeojson = buildHexGrid(radius, coverage, upperPercentile, maxHeight); map.getSource('hexagons').setData(newGeojson); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Create a Hover Effect https://docs.mapatlas.xyz/sdk/examples/hover-effect --- title: "Create a Hover Effect" category: "interaction" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "on('mouseenter')", "on('mouseleave')", "setFeatureState"] tags: ["hover", "highlight", "featureState", "interaction", "layer", "geojson", "mouseenter"] description: "Highlight map features when the user hovers over them using feature state" --- # Create a Hover Effect Highlight GeoJSON features when the user hovers over them using `setFeatureState`.

🖱️ Hover over the circles to see the highlight effect.

## How It Works 1. Add a GeoJSON source with features that have `id` fields 2. Use `feature-state` expressions in layer paint to change appearance 3. Use `setFeatureState` on `mouseenter`/`mouseleave` to toggle the state ## Key Code Pattern ```javascript // Layer uses feature-state to change color/size on hover map.addLayer({ id: 'my-layer', type: 'circle', source: 'my-source', paint: { // Changes based on hover state 'circle-color': ['case', ['boolean', ['feature-state', 'hover'], false], '#ef4444', // color when hovered '#3b82f6' // default color ], 'circle-radius': ['case', ['boolean', ['feature-state', 'hover'], false], 16, // radius when hovered 10 // default radius ] } }); let hoveredId = null; map.on('mouseenter', 'my-layer', (e) => { map.getCanvas().style.cursor = 'pointer'; if (e.features.length > 0) { if (hoveredId !== null) { map.setFeatureState({ source: 'my-source', id: hoveredId }, { hover: false }); } hoveredId = e.features[0].id; map.setFeatureState({ source: 'my-source', id: hoveredId }, { hover: true }); } }); map.on('mouseleave', 'my-layer', () => { map.getCanvas().style.cursor = ''; if (hoveredId !== null) { map.setFeatureState({ source: 'my-source', id: hoveredId }, { hover: false }); } hoveredId = null; }); ``` ## Important: Features Need IDs ```javascript // Each feature must have an 'id' field for setFeatureState to work const geojson = { type: 'FeatureCollection', features: [ { type: 'Feature', id: 1, properties: { name: 'Paris' }, geometry: {...} }, { type: 'Feature', id: 2, properties: { name: 'London' }, geometry: {...} }, ] }; ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Cluster Points with Custom Styling https://docs.mapatlas.xyz/sdk/examples/html-clusters --- title: "Cluster Points with Custom Styling" category: "geometry" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "cluster", "clusterMaxZoom", "clusterRadius", "cluster-count"] tags: ["cluster", "points", "geojson", "cluster-count", "zoom", "aggregate", "markers", "density"] description: "Group nearby points into clusters that expand on click, with custom circle and count styling" --- # Cluster Points with Custom Styling Group dense point data into clusters that show the count, and expand to individual points on click.
## Enable Clustering on a Source ```javascript map.addSource('points', { type: 'geojson', data: geojsonData, cluster: true, // enable clustering clusterMaxZoom: 14, // max zoom to cluster at clusterRadius: 50, // radius (pixels) to cluster within }); ``` ## Cluster Layers ```javascript // Cluster bubbles — sized by count map.addLayer({ id: 'clusters', type: 'circle', source: 'points', filter: ['has', 'point_count'], paint: { 'circle-radius': [ 'step', ['get', 'point_count'], 15, // radius for count < 10 10, 20, // radius for count >= 10 100, 28 // radius for count >= 100 ], 'circle-color': [ 'step', ['get', 'point_count'], '#3b82f6', 10, '#f97316', 100, '#ef4444' ], } }); // Count labels map.addLayer({ id: 'cluster-count', type: 'symbol', source: 'points', filter: ['has', 'point_count'], layout: { 'text-field': '{point_count_abbreviated}', 'text-size': 13, }, paint: { 'text-color': '#fff' } }); // Unclustered individual points map.addLayer({ id: 'point', type: 'circle', source: 'points', filter: ['!', ['has', 'point_count']], paint: { 'circle-radius': 6, 'circle-color': '#22c55e' } }); ``` ## Expand Cluster on Click ```javascript map.on('click', 'clusters', (e) => { const feature = map.queryRenderedFeatures(e.point, { layers: ['clusters'] })[0]; const clusterId = feature.properties.cluster_id; map.getSource('points').getClusterExpansionZoom(clusterId, (err, zoom) => { if (err) return; map.easeTo({ center: feature.geometry.coordinates, zoom: zoom }); }); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Jump to a Series of Locations https://docs.mapatlas.xyz/sdk/examples/jump-to-locations --- title: "Jump to a Series of Locations" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "jumpTo", "flyTo"] tags: ["camera", "navigation", "jumpTo", "locations", "tour"] description: "Navigate instantly or with animation through a series of map locations" --- # Jump to a Series of Locations Navigate through a series of predefined locations — either instantly with `jumpTo` or with a smooth animation using `flyTo`.
## How It Works - `jumpTo()` — Instantly moves the camera with no animation - `flyTo()` — Smoothly animates the camera to the location - `easeTo()` — Animates the camera with configurable easing ## Instant Jump (No Animation) ```javascript // Jump instantly to a location map.jumpTo({ center: [-74.006, 40.7128], zoom: 12, bearing: 0, pitch: 0 }); ``` ## Animated Jump (With flyTo) ```javascript const locations = [ { name: 'Paris', center: [2.349902, 48.852966], zoom: 12 }, { name: 'New York', center: [-74.006, 40.7128], zoom: 12 }, { name: 'Tokyo', center: [139.6917, 35.6895], zoom: 12 }, ]; function goTo(index) { map.flyTo({ center: locations[index].center, zoom: locations[index].zoom, speed: 2, essential: true }); } ``` ## Auto Tour ```javascript let currentIndex = 0; // Automatically cycle through all locations const tourInterval = setInterval(() => { map.flyTo({ center: locations[currentIndex].center, zoom: locations[currentIndex].zoom, speed: 2 }); currentIndex = (currentIndex + 1) % locations.length; }, 3500); // Move every 3.5 seconds // Stop the tour clearInterval(tourInterval); ``` ## Difference: jumpTo vs flyTo vs easeTo | Method | Animation | Use Case | |--------|-----------|----------| | `jumpTo()` | None (instant) | Quick navigation, no visual transition needed | | `flyTo()` | Zoom out → pan → zoom in | Best for large distances | | `easeTo()` | Smooth pan/zoom | Best for short distances | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # How to Generate an API Key in MapMetrics https://docs.mapatlas.xyz/sdk/examples/key-creation # How to Generate an API Key in MapMetrics To use MapMetrics services in your application, you need to generate an API key. This key allows your app to securely access MapMetrics features and ensures only authorized usage. Follow the steps below to create your API key: ## 1. Navigate to the Keys Section Log in to the [MapMetrics Portal](https://portal.mapmetrics.org) and select the **Keys** section from the sidebar. Here, you will see a list of your existing keys (if any). ![Keys Section](../../overview/assets/images/createkey.png) ## 2. Start the Key Generation Process Click the **New Key** button to begin creating a new API key. ![New Key Button](../../overview/assets/images/setupkey.png) ## 3. Fill in Key Details You will be presented with a form to configure your new API key. The following options are available: - **Name**: Give your key a descriptive name (e.g., "Production Web App", "Test App"). - **Allowed Websites**: (Optional) Specify which website origins are allowed to use this key. This is a security feature to prevent unauthorized use. For production, set your domain(s) (e.g., `https://yourdomain.com`). For development or mobile apps, you can leave this blank to allow all origins. - **Scope**: Select the SDKs and features this key should have access to. Only the selected SDKs/features will be available with this key. For example, you might enable Maps, Search, or Autocomplete depending on your use case. ## 4. Save and Generate Your Key After filling in the details and selecting the appropriate scopes, click **Save** to generate your API key. ![Key Generation Form](../../overview/assets/images/genkey.png) ## 5. Copy and Store Your Key Once your key is generated, it will be displayed on the screen. **Copy and save this key** in a secure location. You will need it to authenticate your application with MapMetrics services. > **Tip:** Treat your API key like a password. Do not share it publicly or commit it to public repositories. --- You are now ready to use your API key! In the next chapter, you will learn how to create and apply custom map styles. --- # Locate the User https://docs.mapatlas.xyz/sdk/examples/locate-user --- title: "Locate the User" category: "interaction" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "GeolocateControl", "Marker", "flyTo"] tags: ["geolocation", "user-location", "locate", "gps", "current-location", "GeolocateControl"] description: "Show the user's current location on the map using the browser Geolocation API" --- # Locate the User Show the user's current GPS location on the map using the built-in `GeolocateControl` or the browser's Geolocation API.
Click the 🎯 button (top-right of map) or the button above to locate yourself.
## Two Ways to Locate the User ### Option 1: GeolocateControl (Recommended - Built-in button) The easiest approach — adds a 🎯 button to the map that handles everything automatically. ```javascript const geolocate = new mapmetricsgl.GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true, // Continuously tracks user movement showUserHeading: true // Shows direction user is facing }); map.addControl(geolocate, 'top-right'); // Listen for location events geolocate.on('geolocate', (e) => { const { longitude, latitude } = e.coords; console.log(`User at: ${longitude}, ${latitude}`); }); geolocate.on('error', (e) => { console.error('Geolocation error:', e); }); ``` ### Option 2: Browser Geolocation API (Manual) Use the browser's `navigator.geolocation` API directly for more control. ```javascript navigator.geolocation.getCurrentPosition( (position) => { const { longitude, latitude } = position.coords; // Fly to user location map.flyTo({ center: [longitude, latitude], zoom: 14 }); // Add marker at user location new mapmetricsgl.Marker({ color: '#22c55e' }) .setLngLat([longitude, latitude]) .setPopup( new mapmetricsgl.Popup({ offset: 25 }) .setHTML('You are here!') ) .addTo(map); }, (error) => { console.error('Error getting location:', error.message); }, { enableHighAccuracy: true } ); ``` ## GeolocateControl Options | Option | Type | Description | |--------|------|-------------| | `positionOptions.enableHighAccuracy` | `boolean` | Use GPS for better accuracy | | `trackUserLocation` | `boolean` | Continuously update position as user moves | | `showUserHeading` | `boolean` | Show compass direction arrow | | `showAccuracyCircle` | `boolean` | Show accuracy radius circle (default: `true`) | ## Complete Example > ⚠️ **Note:** Geolocation requires the user to grant browser permission and only works over HTTPS in production. --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Measure Distances https://docs.mapatlas.xyz/sdk/examples/measure-distances --- title: "Measure Distances" category: "special" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "on('click')", "addSource", "addLayer", "setData"] tags: ["measure", "distance", "click", "haversine", "length", "ruler", "calculate"] description: "Allow users to click on the map to measure distances between points" --- # Measure Distances Click on the map to place points and calculate the distance between them.
Click on the map to start measuring. Click again to add more points.
## Haversine Distance Formula Calculate the great-circle distance between two coordinates: ```javascript function haversineKm(pointA, pointB) { const R = 6371; // Earth radius in km const dLat = (pointB[1] - pointA[1]) * Math.PI / 180; const dLng = (pointB[0] - pointA[0]) * Math.PI / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(pointA[1] * Math.PI / 180) * Math.cos(pointB[1] * Math.PI / 180) * Math.sin(dLng / 2) * Math.sin(dLng / 2); return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); } const distKm = haversineKm([2.35, 48.85], [-0.12, 51.50]); console.log(distKm.toFixed(2) + ' km'); // ~341 km ``` ## Total Route Distance ```javascript let totalKm = 0; for (let i = 1; i < points.length; i++) { totalKm += haversineKm(points[i - 1], points[i]); } console.log('Total:', totalKm.toFixed(2), 'km'); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Get Coordinates of the Mouse Pointer https://docs.mapatlas.xyz/sdk/examples/mouse-coordinates --- title: "Get Coordinates of the Mouse Pointer" category: "interaction" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "on('mousemove')", "on('click')"] tags: ["coordinates", "mouse", "mousemove", "lngLat", "pointer", "interaction", "click"] description: "Display the longitude and latitude coordinates as the user moves the mouse over the map" --- # Get Coordinates of the Mouse Pointer Display real-time longitude and latitude coordinates as the user moves the mouse over the map.
Move your mouse over the map...
## How It Works Listen to the `mousemove` event on the map — each event includes a `lngLat` property with the current mouse position as geographic coordinates. ## Basic Usage ```javascript map.on('mousemove', (e) => { const lng = e.lngLat.lng; const lat = e.lngLat.lat; console.log(`Mouse at: ${lng.toFixed(6)}, ${lat.toFixed(6)}`); }); ``` ## Display in a UI Element ```javascript const display = document.getElementById('coordinates'); map.on('mousemove', (e) => { display.textContent = `Lng: ${e.lngLat.lng.toFixed(6)}, Lat: ${e.lngLat.lat.toFixed(6)}`; }); ``` ## Get Coordinates on Click ```javascript map.on('click', (e) => { const { lng, lat } = e.lngLat; console.log(`Clicked at: ${lng}, ${lat}`); // Add a marker at click location new mapmetricsgl.Marker() .setLngLat([lng, lat]) .addTo(map); }); ``` ## Event Properties | Property | Type | Description | |----------|------|-------------| | `e.lngLat.lng` | `number` | Longitude of mouse position | | `e.lngLat.lat` | `number` | Latitude of mouse position | | `e.point.x` | `number` | Pixel X position on screen | | `e.point.y` | `number` | Pixel Y position on screen | | `e.originalEvent` | `MouseEvent` | Original browser mouse event | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add Multiple Geometries from One GeoJSON Source https://docs.mapatlas.xyz/sdk/examples/multiple-geometries --- title: "Add Multiple Geometries from One GeoJSON Source" category: "geometry" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "filter expression", "FeatureCollection"] tags: ["geojson", "multiple", "geometries", "point", "line", "polygon", "single-source", "layer"] description: "Use a single GeoJSON source to render points, lines, and polygons in separate layers" --- # Add Multiple Geometries from One GeoJSON Source Use one GeoJSON source containing mixed geometry types and render each type in its own layer using geometry type filters.
## Key Concept: Geometry Type Filter Use `['==', ['geometry-type'], 'Point']` to target specific geometry types from a shared source: ```javascript // Single source with mixed geometries map.addSource('mixed', { type: 'geojson', data: { type: 'FeatureCollection', features: [ { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }, { type: 'Feature', geometry: { type: 'LineString', coordinates: [[0,0],[1,1]] }, properties: {} }, { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[[0,0],[1,0],[1,1],[0,0]]] }, properties: {} } ] } }); // Render only Points map.addLayer({ id: 'points', type: 'circle', source: 'mixed', filter: ['==', ['geometry-type'], 'Point'], paint: { 'circle-radius': 8, 'circle-color': '#3b82f6' } }); // Render only Lines map.addLayer({ id: 'lines', type: 'line', source: 'mixed', filter: ['==', ['geometry-type'], 'LineString'], paint: { 'line-color': '#ef4444', 'line-width': 3 } }); // Render only Polygons map.addLayer({ id: 'polygons', type: 'fill', source: 'mixed', filter: ['==', ['geometry-type'], 'Polygon'], paint: { 'fill-color': '#22c55e', 'fill-opacity': 0.3 } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Display Map Navigation Controls https://docs.mapatlas.xyz/sdk/examples/navigation-controls --- title: "Display Map Navigation Controls" category: "controls" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "NavigationControl", "ScaleControl", "FullscreenControl", "GeolocateControl"] tags: ["controls", "navigation", "zoom", "compass", "scale", "fullscreen", "ui"] description: "Add navigation controls to the map including zoom buttons, compass, scale bar, and fullscreen toggle" --- # Display Map Navigation Controls Add built-in UI controls to your map: zoom buttons, compass, scale bar, fullscreen toggle, and geolocation.

The map includes: zoom +/− buttons (top-right), compass, scale bar (bottom-left), and fullscreen button.

## Available Controls | Control | Class | Description | |---------|-------|-------------| | Zoom & Compass | `NavigationControl` | +/− zoom buttons and compass rose | | Fullscreen | `FullscreenControl` | Toggle fullscreen mode | | Geolocation | `GeolocateControl` | Locate the user on the map | | Scale bar | `ScaleControl` | Distance scale indicator | ## Adding Controls ```javascript // Zoom + Compass (most common) map.addControl(new mapmetricsgl.NavigationControl(), 'top-right'); // Fullscreen button map.addControl(new mapmetricsgl.FullscreenControl(), 'top-right'); // Scale bar map.addControl(new mapmetricsgl.ScaleControl({ unit: 'metric' }), 'bottom-left'); // Geolocation button map.addControl(new mapmetricsgl.GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }), 'top-right'); ``` ## Control Positions Controls can be placed in any of 4 corners: ```javascript map.addControl(control, 'top-left'); map.addControl(control, 'top-right'); // default map.addControl(control, 'bottom-left'); map.addControl(control, 'bottom-right'); ``` ## NavigationControl Options ```javascript const nav = new mapmetricsgl.NavigationControl({ showCompass: true, // show compass rose (default: true) showZoom: true, // show +/- zoom buttons (default: true) visualizePitch: true // tilt compass icon based on pitch }); map.addControl(nav, 'top-right'); ``` ## ScaleControl Options ```javascript const scale = new mapmetricsgl.ScaleControl({ maxWidth: 100, // max width in pixels unit: 'metric' // 'metric', 'imperial', or 'nautical' }); map.addControl(scale, 'bottom-left'); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Offset the Vanishing Point Using Padding https://docs.mapatlas.xyz/sdk/examples/offset-vanishing-point-padding --- title: "Offset the Vanishing Point Using Padding" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "flyTo", "easeTo", "fitBounds", "padding", "setPadding"] tags: ["padding", "vanishing point", "offset", "sidebar", "panel", "fitBounds", "flyTo", "camera"] description: "Shift the map's visible center using padding to accommodate UI panels or sidebars" --- # Offset the Vanishing Point Using Padding Use camera `padding` to shift the effective center of the map — so that a point of interest stays visible even when a sidebar or panel overlaps part of the map.

Locations


## What is Padding? Padding shifts the camera's effective center so that a point of interest appears in the *unobscured* area of the map. This is ideal when part of the map is covered by a UI element like a sidebar, bottom sheet, or info panel. ```javascript // The map visually shifts so the center appears // in the area NOT covered by the 300px left panel map.flyTo({ center: [lng, lat], zoom: 12, padding: { left: 300, // left panel width top: 0, right: 0, bottom: 0, } }); ``` ## Set Padding at Initialization ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', center: [0, 20], zoom: 4, padding: { left: 320, top: 0, right: 0, bottom: 0 } }); ``` ## `setPadding` at Runtime ```javascript // Animate padding change (e.g., when a panel opens/closes) map.easeTo({ padding: { left: 320, top: 0, right: 0, bottom: 0 }, duration: 300, }); // Or set it instantly map.setPadding({ left: 0 }); ``` ## `fitBounds` with Padding ```javascript map.fitBounds([[-180, -85], [180, 85]], { padding: { left: 300, top: 40, right: 40, bottom: 40 } }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Display a Popup on Click https://docs.mapatlas.xyz/sdk/examples/popup-on-click --- title: "Display a Popup on Click" category: "interaction" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Popup", "on('click')"] tags: ["popup", "click", "interaction", "info-window", "on-click"] description: "Show a popup with information when user clicks anywhere on the map or on a specific layer" --- # Display a Popup on Click Show a popup with information when the user clicks on the map.

👆 Click anywhere on the map to see coordinates, or click on Paris marker for details.

## How It Works There are two ways to show a popup on click: 1. **Attached to a Marker** — popup always linked to that marker 2. **On map click** — popup appears wherever user clicks ## Popup on Map Click ```javascript const popup = new mapmetricsgl.Popup({ closeButton: true }); map.on('click', (e) => { popup .setLngLat(e.lngLat) .setHTML(` You clicked here!
Lng: ${e.lngLat.lng.toFixed(5)}
Lat: ${e.lngLat.lat.toFixed(5)} `) .addTo(map); }); ``` ## Popup Attached to a Marker ```javascript new mapmetricsgl.Marker() .setLngLat([2.349902, 48.852966]) .setPopup( new mapmetricsgl.Popup({ offset: 25 }) .setHTML('

Paris

Capital of France

') ) .addTo(map); ``` ## Popup Options | Option | Type | Description | |--------|------|-------------| | `closeButton` | `boolean` | Show × close button (default: `true`) | | `closeOnClick` | `boolean` | Close when map is clicked (default: `true`) | | `offset` | `number` | Pixel offset from anchor point | | `anchor` | `string` | Anchor position: `'top'`, `'bottom'`, `'left'`, `'right'` | | `maxWidth` | `string` | Max CSS width (default: `'240px'`) | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Display a Popup on Hover https://docs.mapatlas.xyz/sdk/examples/popup-on-hover --- title: "Display a Popup on Hover" category: "interaction" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "Popup", "on('mouseenter')", "on('mouseleave')"] tags: ["popup", "hover", "mouseenter", "mouseleave", "tooltip", "interaction"] description: "Show a tooltip popup when the user hovers over a marker or map feature" --- # Display a Popup on Hover Show a popup tooltip when the user hovers over a marker, then hide it when the mouse leaves.

🖱️ Hover over the markers to see popups appear.

## How It Works Listen to `mouseenter` and `mouseleave` events on marker elements to show/hide a popup. ## Hover Popup on a Marker ```javascript const popup = new mapmetricsgl.Popup({ closeButton: false, // No × button for hover popups closeOnClick: false, // Don't close on map click offset: 25 }); const marker = new mapmetricsgl.Marker() .setLngLat([2.349902, 48.852966]) .addTo(map); // Show on hover marker.getElement().addEventListener('mouseenter', () => { map.getCanvas().style.cursor = 'pointer'; popup .setLngLat([2.349902, 48.852966]) .setHTML('Paris
Capital of France') .addTo(map); }); // Hide on mouse leave marker.getElement().addEventListener('mouseleave', () => { map.getCanvas().style.cursor = ''; popup.remove(); }); ``` ## Hover Popup on a GeoJSON Layer ```javascript map.on('mouseenter', 'my-layer', (e) => { map.getCanvas().style.cursor = 'pointer'; const props = e.features[0].properties; popup .setLngLat(e.lngLat) .setHTML(`${props.name}`) .addTo(map); }); map.on('mouseleave', 'my-layer', () => { map.getCanvas().style.cursor = ''; popup.remove(); }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # React Map Integration https://docs.mapatlas.xyz/sdk/examples/react-integration # React Map Integration This guide shows you how to integrate MapMetrics into a React application. ## Prerequisites - Node.js and npm installed - Basic knowledge of React - [MapMetrics API key and style URL](https://portal.mapmetrics.org/) ## Quick Start ### 1. Create React App ```bash npx create-react-app my-map-app cd my-map-app ``` ### 2. Install MapMetrics GL Package ```bash npm install @mapmetrics/mapmetrics-gl ``` **Package Details:** - NPM Package: [@mapmetrics/mapmetrics-gl](https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl) - Current Version: ^0.5.7 ### 3. Create Map Component Create a new file `src/MapComponent.jsx`: ```jsx import React, { useEffect, useRef } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; const MapComponent = () => { const mapContainer = useRef(null); const map = useRef(null); useEffect(() => { if (map.current) return; // Initialize map only once // Replace with your complete style URL from MapMetrics Portal const YOUR_STYLE_URL = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_USER_ID/YOUR_STYLE.json&token=YOUR_TOKEN'; map.current = new mapmetricsgl.Map({ container: mapContainer.current, style: YOUR_STYLE_URL, center: [-74.006, 40.7128], // New York City zoom: 12 }); // Add navigation controls map.current.addControl(new mapmetricsgl.NavigationControl(), 'top-right'); // Cleanup on unmount return () => { if (map.current) { map.current.remove(); map.current = null; } }; }, []); return (
); }; export default MapComponent; ``` ### 4. Use Map Component in App Update `src/App.js`: ```jsx import React from 'react'; import './App.css'; import MapComponent from './MapComponent'; function App() { return (

My MapMetrics App

); } export default App; ``` ### 5. Add CSS (Optional) Update `src/App.css`: ```css .App { text-align: center; padding: 20px; } h1 { margin-bottom: 20px; } ``` ### 6. Run Your App ```bash npm start ``` Your map should now appear at `http://localhost:3000` ## Important Notes ### Style URL Replace `YOUR_STYLE_URL` with the complete URL you get from [MapMetrics Portal](https://portal.mapmetrics.org/). It should look like: ```javascript const YOUR_STYLE_URL = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=753b9b14-2fcc-44d3-b273-c8b2b701647a/Bicolor.json&token=eyJhbGc...'; ``` ### Package Information MapMetrics GL is available on NPM: - ✅ Install: `npm install @mapmetrics/mapmetrics-gl` - ✅ Import: `import mapmetricsgl from '@mapmetrics/mapmetrics-gl'` - ✅ CSS: `import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'` - 📦 NPM: [@mapmetrics/mapmetrics-gl](https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl) ### Map Container Height The map container **must have a height**. Set it via inline style or CSS: ```jsx // Option 1: Inline style
// Option 2: CSS class
``` ```css .map-container { height: 500px; width: 100%; } ``` ## Complete Working Example Here's a complete, copy-paste ready React component: ```jsx // MapComponent.jsx import React, { useEffect, useRef, useState } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; const MapComponent = () => { const mapContainer = useRef(null); const map = useRef(null); const [lng] = useState(-74.006); const [lat] = useState(40.7128); const [zoom] = useState(12); useEffect(() => { if (map.current) return; // Get your complete style URL from https://portal.mapmetrics.org/ const styleURL = 'YOUR_COMPLETE_STYLE_URL_HERE'; map.current = new mapmetricsgl.Map({ container: mapContainer.current, style: styleURL, center: [lng, lat], zoom: zoom }); // Add controls map.current.addControl(new mapmetricsgl.NavigationControl(), 'top-right'); map.current.addControl(new mapmetricsgl.FullscreenControl(), 'top-right'); return () => { if (map.current) { map.current.remove(); map.current = null; } }; }, [lng, lat, zoom]); return (
); }; export default MapComponent; ``` ## Troubleshooting ### Map doesn't load - ✅ Check that you replaced `YOUR_COMPLETE_STYLE_URL_HERE` with actual URL from portal - ✅ Verify your API token is valid - ✅ Check browser console for errors ### Map container has no height - ✅ Add `height: '500px'` or `height: '100vh'` to container style - ✅ Ensure parent container also has height ### npm install fails - ✅ Make sure you're using the correct package: `@mapmetrics/mapmetrics-gl` - ✅ Run `npm install @mapmetrics/mapmetrics-gl` - ✅ Check NPM registry: https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl ## Adding Features ### Add a Marker ```jsx // After map initialization const marker = new mapmetricsgl.Marker() .setLngLat([-74.006, 40.7128]) .addTo(map.current); ``` ### Add a Popup ```jsx const popup = new mapmetricsgl.Popup() .setLngLat([-74.006, 40.7128]) .setHTML('

Hello World!

') .addTo(map.current); ``` ### Handle Click Events ```jsx map.current.on('click', (e) => { console.log('Map clicked at:', e.lngLat); }); ``` ## Next Steps - [Add Markers](./add-a-marker) - Display points of interest - [Add Routing](../directions/directions) - Turn-by-turn directions - [Geocoding](../geocoder/autocomplete) - Search for locations --- **Need Help?** Join our [Discord community](https://discord.com/invite/uRXQRfbb7d) or check [more examples](./Intro) --- # React Map Integration https://docs.mapatlas.xyz/sdk/examples/react-map-example --- title: "React Map Integration" category: "getting-started" platform: ["react"] difficulty: "beginner" apis: ["Map", "NavigationControl", "FullscreenControl"] tags: ["react", "hooks", "useEffect", "useRef", "component"] description: "Complete guide to integrating MapMetrics GL into React applications using hooks" --- # React Map Integration This guide shows you how to integrate MapMetrics into a React application. ## Prerequisites - Node.js and npm installed - Basic knowledge of React - [MapMetrics API key and style URL](https://portal.mapmetrics.org/) ## Quick Start ### 1. Create React App ```bash npx create-react-app my-map-app cd my-map-app ``` ### 2. Install MapMetrics GL Package ```bash npm install @mapmetrics/mapmetrics-gl ``` **Package Details:** - NPM Package: [@mapmetrics/mapmetrics-gl](https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl) - Current Version: ^0.5.7 ### 3. Create Map Component Create a new file `src/MapComponent.jsx`: ```jsx import React, { useEffect, useRef } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; const MapComponent = () => { const mapContainer = useRef(null); const map = useRef(null); useEffect(() => { if (map.current) return; // Initialize map only once // Replace with your complete style URL from MapMetrics Portal const YOUR_STYLE_URL = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_USER_ID/YOUR_STYLE.json&token=YOUR_TOKEN'; map.current = new mapmetricsgl.Map({ container: mapContainer.current, style: YOUR_STYLE_URL, center: [-74.006, 40.7128], // New York City zoom: 12 }); // Add navigation controls map.current.addControl(new mapmetricsgl.NavigationControl(), 'top-right'); // Cleanup on unmount return () => { if (map.current) { map.current.remove(); map.current = null; } }; }, []); return (
); }; export default MapComponent; ``` ### 4. Use Map Component in App Update `src/App.js`: ```jsx import React from 'react'; import './App.css'; import MapComponent from './MapComponent'; function App() { return (

My MapMetrics App

); } export default App; ``` ### 5. Add CSS (Optional) Update `src/App.css`: ```css .App { text-align: center; padding: 20px; } h1 { margin-bottom: 20px; } ``` ### 6. Run Your App ```bash npm start ``` Your map should now appear at `http://localhost:3000` ## Important Notes ### Style URL Replace `YOUR_STYLE_URL` with the complete URL you get from [MapMetrics Portal](https://portal.mapmetrics.org/). It should look like: ```javascript const YOUR_STYLE_URL = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=753b9b14-2fcc-44d3-b273-c8b2b701647a/Bicolor.json&token=eyJhbGc...'; ``` ### Package Information MapMetrics GL is available on NPM: - ✅ Install: `npm install @mapmetrics/mapmetrics-gl` - ✅ Import: `import mapmetricsgl from '@mapmetrics/mapmetrics-gl'` - ✅ CSS: `import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'` - 📦 NPM: [@mapmetrics/mapmetrics-gl](https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl) ### Map Container Height The map container **must have a height**. Set it via inline style or CSS: ```jsx // Option 1: Inline style
// Option 2: CSS class
``` ```css .map-container { height: 500px; width: 100%; } ``` ## Error Handling (Recommended) It's **highly recommended** to add error handling to detect if users forget to add their token. This provides a much better user experience than a blank screen: ```jsx // MapComponent.jsx - With Robust Error Handling import React, { useEffect, useRef, useState } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; const MapComponent = () => { const mapContainer = useRef(null); const map = useRef(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { if (map.current) return; const styleURL = 'YOUR_COMPLETE_STYLE_URL_HERE'; // Check if user forgot to replace the placeholder if (styleURL === 'YOUR_COMPLETE_STYLE_URL_HERE' || styleURL.includes('YOUR_')) { setError('⚠️ Please replace YOUR_COMPLETE_STYLE_URL_HERE with your actual style URL from https://portal.mapmetrics.org/'); setLoading(false); return; } try { const mapInstance = new mapmetricsgl.Map({ container: mapContainer.current, style: styleURL, center: [-74.006, 40.7128], zoom: 12 }); // Handle successful map load mapInstance.on('load', () => { setLoading(false); setError(null); }); // Handle CRITICAL errors only (authentication, style loading failures) // Don't show error UI for minor issues like tile loading failures mapInstance.on('error', (e) => { console.error('Map error:', e); // Only show UI errors for critical failures that prevent map from working // Ignore tile loading errors and other minor issues const isCriticalError = e.error?.status === 401 || e.error?.message?.includes('401') || (e.error?.message?.includes('Failed to fetch') && loading) || (e.sourceId === undefined && e.error); // Style loading errors have no sourceId if (isCriticalError) { setLoading(false); // Authentication errors (401) if (e.error?.message?.includes('401') || e.error?.status === 401) { setError('❌ Invalid API token. Get a valid token from https://portal.mapmetrics.org/'); } // Network/style URL errors during initial load else if (e.error?.message?.includes('Failed to fetch') && loading) { setError('❌ Cannot load map style. Check your style URL from https://portal.mapmetrics.org/'); } // Generic critical error before map loads else if (loading) { setError('❌ Map failed to load. Check your style URL and token at https://portal.mapmetrics.org/'); } // If map already loaded successfully, just log the error (don't break the UI) else { console.warn('Map error after successful load (non-critical):', e); } } else { // Non-critical errors (tile loading, etc.) - just log, don't show error UI console.warn('Non-critical map error:', e); } }); mapInstance.addControl(new mapmetricsgl.NavigationControl(), 'top-right'); map.current = mapInstance; return () => { if (map.current) { map.current.remove(); map.current = null; } }; } catch (err) { console.error('Map initialization error:', err); setError('❌ Failed to initialize map. Check your style URL from https://portal.mapmetrics.org/'); setLoading(false); } }, []); // Display error message if something went wrong if (error) { return (
{error}
Need help? Check the Discord community
); } // Optional: Show loading state if (loading) { return (
Loading map...
); } return (
); }; export default MapComponent; ``` ## Complete Working Example Here's a complete, copy-paste ready React component: ```jsx // MapComponent.jsx import React, { useEffect, useRef, useState } from 'react'; import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css'; const MapComponent = () => { const mapContainer = useRef(null); const map = useRef(null); const [lng] = useState(-74.006); const [lat] = useState(40.7128); const [zoom] = useState(12); useEffect(() => { if (map.current) return; // Get your complete style URL from https://portal.mapmetrics.org/ const styleURL = 'YOUR_COMPLETE_STYLE_URL_HERE'; map.current = new mapmetricsgl.Map({ container: mapContainer.current, style: styleURL, center: [lng, lat], zoom: zoom }); // Add controls map.current.addControl(new mapmetricsgl.NavigationControl(), 'top-right'); map.current.addControl(new mapmetricsgl.FullscreenControl(), 'top-right'); return () => { if (map.current) { map.current.remove(); map.current = null; } }; }, [lng, lat, zoom]); return (
); }; export default MapComponent; ``` ## Troubleshooting ### Map doesn't load - ✅ Check that you replaced `YOUR_COMPLETE_STYLE_URL_HERE` with actual URL from portal - ✅ Verify your API token is valid - ✅ Check browser console for errors ### Map container has no height - ✅ Add `height: '500px'` or `height: '100vh'` to container style - ✅ Ensure parent container also has height ### npm install fails - ✅ Make sure you're using the correct package: `@mapmetrics/mapmetrics-gl` - ✅ Run `npm install @mapmetrics/mapmetrics-gl` - ✅ Check NPM registry: https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl ## Adding Features ### Add a Marker ```jsx // After map initialization const marker = new mapmetricsgl.Marker() .setLngLat([-74.006, 40.7128]) .addTo(map.current); ``` ### Add a Popup ```jsx const popup = new mapmetricsgl.Popup() .setLngLat([-74.006, 40.7128]) .setHTML('

Hello World!

') .addTo(map.current); ``` ### Handle Click Events ```jsx map.current.on('click', (e) => { console.log('Map clicked at:', e.lngLat); }); ``` ## Next Steps - [Add Markers](./add-a-marker) - Display points of interest - [Add Routing](../directions/directions) - Turn-by-turn directions - [Geocoding](../geocoder/autocomplete) - Search for locations --- ## Common Pitfalls ### 1. Do not use `map.on('error')` for fatal error detection The `error` event fires for non-fatal issues such as missing tiles, failed sprites, or slow network requests. Using it to show a fatal error screen will cause the error UI to appear even when the map loads successfully. Use a load timeout instead: ```javascript const timeout = setTimeout(() => { // map failed to load within 15 seconds }, 15000); map.on('load', () => clearTimeout(timeout)); ``` ### 2. React 18 StrictMode In development, React StrictMode runs every effect twice (mount → cleanup → mount). This causes `map.remove()` to be called on the first mount before the map is recreated on the second mount. This is expected behaviour. Make sure your cleanup function fully removes the map instance so the second mount can initialise cleanly: ```jsx return () => { if (map.current) { map.current.remove(); map.current = null; // ← required so the second mount starts fresh } }; ``` ### 3. Never unmount the map container div on error If your error state causes the map container `
` to be removed from the DOM, the map ref becomes `null` and the map cannot recover. Always keep the container div mounted and display errors as an overlay on top of it. ```jsx // ❌ Wrong — removes the container div if (error) return
Something went wrong
; // ✅ Correct — overlay on top of the container return ( <>
{error &&
Something went wrong
} ); ``` --- **Need Help?** Join our [Discord community](https://discord.com/invite/uRXQRfbb7d) or check [more examples](./Intro) --- # Render World Copies https://docs.mapatlas.xyz/sdk/examples/render-world-copies --- title: "Render World Copies" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "renderWorldCopies", "setRenderWorldCopies"] tags: ["renderWorldCopies", "world copies", "globe", "tiling", "repeat", "wrap", "flat map"] description: "Control whether the map repeats (tiles) the world horizontally when zoomed out" --- # Render World Copies Control whether the world tiles repeatedly when the map is zoomed out using `renderWorldCopies`.
## `renderWorldCopies` When `true` (the default), the map tiles the world horizontally so there are no empty areas when panned past the antimeridian. When `false`, only a single copy of the world is rendered. ```javascript // Enable world copies (default) const map = new mapmetricsgl.Map({ container: 'map', style: '', renderWorldCopies: true, }); // Disable world copies const map = new mapmetricsgl.Map({ container: 'map', style: '', renderWorldCopies: false, }); ``` ## Toggle at Runtime ```javascript // Enable map.setRenderWorldCopies(true); // Disable map.setRenderWorldCopies(false); // Check current value const isEnabled = map.getRenderWorldCopies(); // boolean ``` ## When to Disable World Copies | Use case | Recommended | |---|---| | General purpose maps | `true` (default) | | Globe/world overview maps | `true` | | Regional maps (single country/city) | `false` | | Preventing data duplication in world-spanning queries | `false` | | Story maps with fixed bounds | `false` | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Restrict Map Panning to an Area https://docs.mapatlas.xyz/sdk/examples/restrict-map-panning --- title: "Restrict Map Panning to an Area" category: "bounds" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "setMaxBounds", "maxBounds", "fitBounds"] tags: ["bounds", "restrict", "pan", "maxBounds", "area", "limit", "region"] description: "Restrict the map so users cannot pan outside a defined geographic boundary" --- # Restrict Map Panning to an Area Limit map panning to a specific geographic region using `setMaxBounds()`.

Try panning outside Europe — the map will bounce back to the allowed area.

## Set Max Bounds at Initialization ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', center: [10, 52], zoom: 3, maxBounds: [ [-15, 34], // Southwest corner [lng, lat] [40, 72] // Northeast corner [lng, lat] ] }); ``` ## Set Max Bounds Dynamically ```javascript // Restrict after initialization map.setMaxBounds([[-15, 34], [40, 72]]); // Remove restriction map.setMaxBounds(null); ``` ## Also Set Min/Max Zoom ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', maxBounds: [[-15, 34], [40, 72]], minZoom: 3, // can't zoom out beyond this maxZoom: 18 // can't zoom in beyond this }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Add Support for Right-to-Left Scripts https://docs.mapatlas.xyz/sdk/examples/rtl-support --- title: "Add Support for Right-to-Left Scripts" category: "labels" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "setRTLTextPlugin", "mapbox-gl-rtl-text"] tags: ["rtl", "right-to-left", "arabic", "hebrew", "persian", "script", "text", "plugin"] description: "Enable right-to-left text rendering for Arabic, Hebrew, and Persian map labels" --- # Add Support for Right-to-Left Scripts Enable correct rendering of right-to-left scripts (Arabic, Hebrew, Persian) on the map using the RTL text plugin.
## Load the RTL Text Plugin Call `setRTLTextPlugin` **before** creating the map instance: ```javascript mapmetricsgl.setRTLTextPlugin( 'https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', null, // callback on load (optional) true // lazy: only loads when RTL text is encountered ); const map = new mapmetricsgl.Map({ container: 'map', style: '', }); ``` ## NPM Installation ```bash npm install @mapbox/mapbox-gl-rtl-text ``` ```javascript import mapmetricsgl from '@mapmetrics/mapmetrics-gl'; mapmetricsgl.setRTLTextPlugin( 'https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', null, true ); ``` ## When to Use The plugin is needed whenever the map style includes labels in: | Script | Language examples | |---|---| | Arabic | Arabic, Urdu | | Hebrew | Hebrew, Yiddish | | Persian | Farsi, Dari | Without the plugin, RTL characters render in the wrong order or direction. ## Plugin Load States ```javascript // Check if plugin is already set if (!mapmetricsgl.getRTLTextPluginStatus || mapmetricsgl.getRTLTextPluginStatus() === 'unavailable') { mapmetricsgl.setRTLTextPlugin(pluginUrl, null, true); } ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Satellite Map with Terrain Elevation https://docs.mapatlas.xyz/sdk/examples/satellite-terrain --- title: "Satellite Map with Terrain Elevation" category: "3d" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "terrain", "raster-dem", "raster", "TerrainControl"] tags: ["satellite", "terrain", "elevation", "DEM", "raster-dem", "3D", "aerial", "hybrid"] description: "Display a satellite imagery basemap combined with 3D terrain elevation for an aerial landscape view" --- # Satellite Map with Terrain Elevation Combine **satellite imagery** with **3D terrain elevation** to get a realistic aerial view of the landscape. Mountains rise up from the satellite photo, making it feel like you're looking at a real landscape from above. > **No three.js or external libraries needed.** Satellite + terrain is built into MapMetrics GL.
## How It Works The setup is simple — just swap the base map tiles from OpenStreetMap to a satellite imagery provider: ```javascript const map = new mapmetricsgl.Map({ container: 'map', pitch: 60, maxPitch: 85, style: { version: 8, sources: { // Satellite imagery (free from ESRI) satellite: { type: 'raster', tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'], tileSize: 256, attribution: 'Tiles © Esri', maxzoom: 19, }, // Elevation data (free AWS terrain tiles — no API key needed) terrainSource: { type: 'raster-dem', tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'], tileSize: 256, encoding: 'terrarium', maxzoom: 15, }, }, layers: [ { id: 'satellite', type: 'raster', source: 'satellite' }, ], // Enable 3D terrain terrain: { source: 'terrainSource', exaggeration: 1.5, }, }, }); ``` ## Free Satellite Tile Sources | Provider | URL pattern | Notes | |---|---|---| | ESRI World Imagery | `...World_Imagery/MapServer/tile/{z}/{y}/{x}` | Free, global coverage | | OpenStreetMap | `https://a.tile.openstreetmap.org/{z}/{x}/{y}.png` | Standard road map | > Note: ESRI tiles use `{z}/{y}/{x}` (y before x), not the standard `{z}/{x}/{y}`. ## Add Hillshade Over Satellite Adding a semi-transparent hillshade on top of satellite tiles gives extra depth: ```javascript { id: 'hillshade', type: 'hillshade', source: 'terrainSource', paint: { 'hillshade-shadow-color': '#000000', 'hillshade-exaggeration': 0.3, // subtle — don't overpower the satellite image 'hillshade-illumination-anchor': 'viewport', }, } ``` ## Toggle 3D vs Flat ```javascript // Switch to 3D view map.easeTo({ pitch: 60, duration: 800 }); // Switch to flat (top-down) view map.easeTo({ pitch: 0, duration: 800 }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Set Pitch and Bearing https://docs.mapatlas.xyz/sdk/examples/set-pitch-and-bearing --- title: "Set Pitch and Bearing" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "setPitch", "setBearing", "easeTo"] tags: ["camera", "pitch", "bearing", "3d", "tilt", "rotation"] description: "Control the map camera tilt (pitch) and rotation (bearing) to create 3D perspectives" --- # Set Pitch and Bearing Control the map's **pitch** (tilt angle) and **bearing** (rotation) to create 3D perspectives and directional views.
## How It Works - **Pitch** — Tilt the camera from 0° (top-down) to 85° (nearly horizontal) - **Bearing** — Rotate the map from -180° to 180° (0° = North up) ## Basic Usage ```javascript // Set pitch (tilt) - 0 is flat, 60 is a strong tilt map.setPitch(60); // Set bearing (rotation) - 0 is North up, 90 is East up map.setBearing(45); // Set both at once with animation map.easeTo({ pitch: 60, bearing: -30, duration: 1000 // milliseconds }); ``` ## Initial Map Configuration ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', center: [2.349902, 48.852966], zoom: 14, pitch: 45, // Start tilted bearing: -17 // Start slightly rotated }); ``` ## Common Presets ```javascript // Flat top-down view map.easeTo({ pitch: 0, bearing: 0 }); // Street-level perspective map.easeTo({ pitch: 60, bearing: 0 }); // Bird's eye diagonal map.easeTo({ pitch: 45, bearing: -45 }); // North-up reset map.setBearing(0); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Show Polygon Information on Click https://docs.mapatlas.xyz/sdk/examples/show-polygon-info-on-click --- title: "Show Polygon Information on Click" category: "special" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "on('click')", "Popup", "addSource", "addLayer", "queryRenderedFeatures"] tags: ["polygon", "click", "popup", "info", "properties", "fill", "interaction"] description: "Display a popup with polygon feature properties when the user clicks on a filled polygon" --- # Show Polygon Information on Click Click on a polygon to display its properties in a popup.
## Click Polygon to Show Popup ```javascript map.on('click', 'my-polygon-layer', (e) => { const properties = e.features[0].properties; new mapmetricsgl.Popup() .setLngLat(e.lngLat) .setHTML(`

${properties.name}

Area: ${properties.area}

Population: ${properties.population}

`) .addTo(map); }); // Change cursor on hover map.on('mouseenter', 'my-polygon-layer', () => { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', 'my-polygon-layer', () => { map.getCanvas().style.cursor = ''; }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Simple Map with CDN https://docs.mapatlas.xyz/sdk/examples/simple-map-cdn --- title: "Simple Map with CDN" category: "getting-started" platform: ["web"] difficulty: "beginner" apis: ["Map", "NavigationControl"] tags: ["cdn", "quickstart", "setup", "basic-map"] description: "Quick setup guide for creating a basic MapMetrics map using CDN links" --- # Simple Map with CDN Installation This guide demonstrates how to quickly set up a MapMetrics map using our CDN. Follow these steps to get started. ## Prerequisites Before you begin, ensure you have: - **Generated a style and API key:** [How to create an API key](./key-creation.md) and [How to create a map style](./style-creation.md). ## Installation with CDN ### 1. Include the CDN Links Add the following lines to the `` of your HTML file: ```html ``` > **Tip:** Using the CDN is ideal for quick prototypes and demos. For production, consider using a package manager for better version control. ### 2. Create the Map Container Add a `
` element to your HTML where the map will be rendered: ```html
``` ### 3. Style the Map Container Add the following CSS to ensure the map displays correctly: ```html ``` > ⚠️ **Important** > > Don’t forget to add **``** during intialization of map. > You can get them from: **http://portal.mapmetrics.org/** ### 4. Initialize the Map Use the following JavaScript to initialize the map. Replace `` with style URL and Connect API Key that you get from https://portal.mapmetrics.org/: ```html ``` ## Error Handling (Recommended) For better user experience, add error handling to detect missing or invalid tokens: ```html
``` ## Complete Example (Basic - Without Error Handling) Here's a basic HTML example for reference (without error handling): ## Map Container Example Here's an example of how the map container looks in action:
--- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Simple Map with NPM https://docs.mapatlas.xyz/sdk/examples/simple-map-npm --- title: "Simple Map with NPM" category: "getting-started" platform: ["web", "node"] difficulty: "beginner" apis: ["Map", "NavigationControl"] tags: ["npm", "package-manager", "setup", "basic-map"] description: "Setup guide for creating a MapMetrics map using the NPM package" --- # Simple Map with NPM Installation This guide demonstrates how to set up a MapMetrics map using the NPM package. Follow these steps to get started. ## Prerequisites Before you begin, ensure you have: - **Generated a style and API key:** [How to create an API key](./key-creation.md) and [How to create a map style](./style-creation.md). - **Node.js and npm installed:** If you haven't installed Node.js, you can download it from [nodejs.org](https://nodejs.org/). ## Installation with NPM ### 1. Install the Package Run the following command in your project directory to install the MapMetrics-gl package: ```bash npm install @mapmetrics/mapmetrics-gl ``` **Package Information:** - NPM Package: [@mapmetrics/mapmetrics-gl](https://www.npmjs.com/package/@mapmetrics/mapmetrics-gl) - Current Version: ^0.5.7 - Works with both JavaScript and React applications ### 2. Import the Package In your JavaScript or TypeScript file, import the MapMetrics-gl package and its CSS: ```javascript import mapmetricsgl from "@mapmetrics/mapmetrics-gl"; import "@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css"; ``` ### 3. Create the Map Container Add a `
` element to your HTML where the map will be rendered: ```html
``` ### 4. Initialize the Map Use the following JavaScript to initialize the map. Replace `` with style URL and Connect API Key that you get from https://portal.mapmetrics.org/: ## Complete Example This example demonstrates the full NPM-based implementation using React shown above. --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). ## Map Container Example Here's an example of how the map container looks in action:
--- # Sky, Fog, and Terrain https://docs.mapatlas.xyz/sdk/examples/sky-fog-terrain --- title: "Sky, Fog, and Terrain" category: "3d" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "terrain", "sky", "fog", "raster-dem", "setFog", "setSky"] tags: ["3D", "terrain", "sky", "fog", "atmosphere", "elevation", "DEM", "immersive"] description: "Combine 3D terrain with a sky background and atmospheric fog for an immersive landscape view" --- # Sky, Fog, and Terrain Combine **3D terrain**, a **sky layer**, and **atmospheric fog** to create a realistic, immersive landscape. Adjust the sky colors and blend values live using the controls below. > **No external libraries needed.** Sky, fog, and terrain are all built into MapMetrics GL.
## The Three Layers Explained Think of these as three separate things stacked on top of each other: ``` ☁️ Sky → paints the area above the horizon (blue sky, sunrise colors) 🌫️ Fog → adds haze to distant mountains and the horizon 🏔️ Terrain → pushes the land surface into 3D ``` ## Sky Properties The `sky` style property controls the background above the horizon. Use `map.setSky()` to update it at runtime: | Property | What it controls | |---|---| | `sky-color` | The main sky color (top of the sky) | | `horizon-color` | Color at the horizon line | | `fog-color` | Sky color blending into the fog zone | | `sky-horizon-blend` | `0` = sharp sky edge · `1` = gradual blend into horizon | | `horizon-fog-blend` | `0` = sharp horizon · `1` = smooth blend into fog zone | | `fog-ground-blend` | `0` = fog starts high · `1` = fog blends all the way to ground | ```javascript // Set initial sky in the style style: { sky: { 'sky-color': '#199EF3', 'sky-horizon-blend': 0.5, 'horizon-color': '#fbe4ff', 'horizon-fog-blend': 0.5, 'fog-color': '#ffffff', 'fog-ground-blend': 0.5, } } // Update sky at runtime map.setSky({ 'sky-color': '#ff6b35', // sunset orange 'horizon-color': '#ffd700', 'fog-color': '#ff8c69', }); ``` ## Fog Properties The `fog` property adds atmospheric haze. Toggle it with `map.setFog()`: ```javascript // Enable fog map.setFog({ color: '#ffffff', // fog color at ground level 'high-color': '#245cdf', // fog color at altitude 'horizon-blend': 0.05, // how fast fog kicks in near horizon 'space-color': '#000000', // outer space color (globe view) 'star-intensity': 0.15, // star brightness in space zone }); // Disable fog map.setFog(null); ``` ## Preset Sky Themes | Theme | sky-color | horizon-color | fog-color | |---|---|---|---| | Day | `#199EF3` | `#fbe4ff` | `#ffffff` | | Sunset | `#ff6b35` | `#ffd700` | `#ff8c69` | | Night | `#0a0a2e` | `#1a1a4e` | `#0d0d2b` | ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Slowly Fly to a Location https://docs.mapatlas.xyz/sdk/examples/slowly-fly-to-location --- title: "Slowly Fly to a Location" category: "camera" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "flyTo", "speed", "curve", "duration"] tags: ["flyTo", "slow", "camera", "animation", "speed", "curve", "duration", "cinematic"] description: "Use flyTo with slow speed and long duration for cinematic camera movements" --- # Slowly Fly to a Location Create a cinematic, slow-panning camera movement using `flyTo` with reduced `speed` and a long `duration`.
## The Key Parameters ```javascript map.flyTo({ center: [lng, lat], zoom: 12, // Speed: fraction of default (1.2). Lower = slower. speed: 0.25, // ~5× slower than default // Curve: how high the arc flies. Higher = more zoom-out. curve: 1.8, // Or override with a fixed duration in ms // duration: 8000, // Respect user's reduced-motion preference (optional) // essential: true means it ignores prefers-reduced-motion essential: true, }); ``` ## `speed` vs `duration` | Option | Description | |---|---| | `speed` | Multiplier relative to default (1.2). `0.25` ≈ 5× slower. Scales with distance. | | `duration` | Fixed time in milliseconds, regardless of distance. | | Both set | `duration` takes precedence over `speed`. | ## Stop an In-Progress Animation ```javascript // Cancel any ongoing camera animation map.stop(); ``` ## Listen for Animation End ```javascript map.flyTo({ center: [lng, lat], zoom: 12, speed: 0.3 }); map.once('moveend', () => { console.log('Arrived at destination'); }); ``` ## Cinematic Pan (no zoom change) ```javascript // Slow pan without changing zoom map.easeTo({ center: [lng, lat], duration: 6000, easing: t => t, // linear }); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Creating a Custom Map Style in MapMetrics https://docs.mapatlas.xyz/sdk/examples/style-creation # Creating a Custom Map Style in MapMetrics With MapMetrics, you can fully customize the appearance of your maps using the Studio. Follow these steps to create and manage your own map style: ## 1. Access the Studio - Go to [portal.mapmetrics.org](https://portal.mapmetrics.org). - Select **Studio** from the sidebar to view your existing map designs (if any). ![Studio Section](../../overview/assets/images/studio.png) ## 2. Start a New Design - Click the **Start Designing** button to begin creating a new map style. ## 3. Design Your Map Style In the Studio Portal, you can: - Open your own styles or explore templates - Edit all map layers (roads, water, buildings, etc.) - Adjust colors, fonts, icons, and visibility for each layer - Make changes according to your preferences. ![Start Designing](../../overview/assets/images/studioportal.png) ## 4. Save and View Your Style - When you are satisfied with your design, click **Save**. ![Design Portal](../../overview/assets/images/save-style.png) - Your new style will be saved and will appear in your portal. ![Style Saved](../../overview/assets/images/saved-style.png) ## 5. Manage and Share Your Style - Click the menu (three dots) on your saved style to: - Open and edit the style - Delete or duplicate it - Generate a **style link** to use with your API key (see the previous chapter) ![Style Menu](../../overview/assets/images/save-menu-style.png) --- Congratulations! You have now created a custom map style and linked it with your API key. In the next chapter, you will learn how to use your style and key in your application. ```text https://gateway.mapmetrics.org/styles/?token= ``` --- # Sync Movement of Multiple Maps https://docs.mapatlas.xyz/sdk/examples/sync-multiple-maps --- title: "Sync Movement of Multiple Maps" category: "camera" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "on", "jumpTo", "getCenter", "getZoom", "getBearing", "getPitch", "move"] tags: ["sync", "multiple maps", "side by side", "compare", "mirror", "move", "jumpTo"] description: "Keep two maps in sync so panning or zooming one updates the other" --- # Sync Movement of Multiple Maps Display two maps side by side and keep their camera positions synchronized — useful for before/after comparisons or style comparisons.
Pan or zoom either map — both stay in sync.
## Sync Pattern The simplest approach: listen to the `move` event on one map and call `jumpTo` on the other. Use a `syncing` flag to prevent infinite loops. ```javascript function syncMaps(source, target) { let syncing = false; source.on('move', () => { if (syncing) return; // prevent feedback loop syncing = true; target.jumpTo({ center: source.getCenter(), zoom: source.getZoom(), bearing: source.getBearing(), pitch: source.getPitch(), }); syncing = false; }); } // Sync both ways syncMaps(mapA, mapB); syncMaps(mapB, mapA); ``` ## Sync Multiple Maps ```javascript const maps = [mapA, mapB, mapC]; maps.forEach((source, i) => { source.on('move', () => { maps.forEach((target, j) => { if (i === j) return; // skip self target.jumpTo({ center: source.getCenter(), zoom: source.getZoom(), bearing: source.getBearing(), pitch: source.getPitch(), }); }); }); }); ``` ## Camera State Methods ```javascript map.getCenter() // → LngLat { lng, lat } map.getZoom() // → number map.getBearing() // → number (degrees) map.getPitch() // → number (degrees) ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Toggle Map Interactions https://docs.mapatlas.xyz/sdk/examples/toggle-interactions --- title: "Toggle Map Interactions" category: "controls" platform: ["web", "react"] difficulty: "beginner" apis: ["Map", "scrollZoom", "dragPan", "dragRotate", "doubleClickZoom", "keyboard", "touchZoomRotate"] tags: ["interactions", "toggle", "disable", "enable", "scrollZoom", "dragPan", "controls", "readonly"] description: "Enable or disable individual map interactions like scroll zoom, drag pan, and rotation" --- # Toggle Map Interactions Enable or disable individual map interactions at runtime. Useful for embedded maps, read-only views, or custom interaction modes.
## Interaction Handlers Each interaction has an `enable()` and `disable()` method: ```javascript // Scroll / pinch zoom map.scrollZoom.enable(); map.scrollZoom.disable(); // Mouse drag to pan map.dragPan.enable(); map.dragPan.disable(); // Right-click drag to rotate map.dragRotate.enable(); map.dragRotate.disable(); // Touch rotate map.touchZoomRotate.enableRotation(); map.touchZoomRotate.disableRotation(); // Double-click zoom map.doubleClickZoom.enable(); map.doubleClickZoom.disable(); // Keyboard shortcuts map.keyboard.enable(); map.keyboard.disable(); ``` ## Disable All at Init ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', interactive: false, // disables all interactions at once }); ``` ## Read-Only Embedded Map ```javascript const map = new mapmetricsgl.Map({ container: 'map', style: '', interactive: false, attributionControl: false, }); // Or selectively disable map.scrollZoom.disable(); map.dragPan.disable(); map.dragRotate.disable(); map.touchZoomRotate.disableRotation(); map.doubleClickZoom.disable(); map.keyboard.disable(); ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Update a Feature in Realtime https://docs.mapatlas.xyz/sdk/examples/update-feature-realtime --- title: "Update a Feature in Realtime" category: "special" platform: ["web", "react"] difficulty: "intermediate" apis: ["Map", "addSource", "addLayer", "setData", "setInterval"] tags: ["realtime", "update", "setData", "live", "dynamic", "interval", "moving"] description: "Update a GeoJSON feature's position in real time using setInterval and setData" --- # Update a Feature in Realtime Simulate real-time data updates by periodically updating a GeoJSON source with `setData()`.
Updating every second...
## Real-time Update Pattern ```javascript // 1. Add a GeoJSON source map.addSource('vehicle', { type: 'geojson', data: { type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: {} } }); map.addLayer({ id: 'vehicle', type: 'circle', source: 'vehicle', paint: { 'circle-radius': 10, 'circle-color': '#3b82f6' } }); // 2. Update position on an interval (or WebSocket message) setInterval(() => { const { lng, lat } = getLatestPosition(); // your data source map.getSource('vehicle').setData({ type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: {} }); }, 1000); ``` ## With WebSocket (Real Production Pattern) ```javascript const ws = new WebSocket('wss://your-api.com/vehicles'); ws.onmessage = (event) => { const { id, lng, lat, speed } = JSON.parse(event.data); map.getSource('vehicles').setData({ type: 'FeatureCollection', features: [{ type: 'Feature', geometry: { type: 'Point', coordinates: [lng, lat] }, properties: { id, speed } }] }); }; ``` ## Complete Example --- For more information, visit the [MapMetrics GitHub repository](https://github.com/MapMetrics/mapmetrics-gl). --- # Web SDK — Moved https://docs.mapatlas.xyz/web-sdk/ --- title: "Web SDK — Moved" --- # Web SDK — Content Has Moved All SDK examples are now at **`/sdk/examples/{name}`**. | Old (wrong) path | New correct path | |---|---| | `/web-sdk/simple-map-npm` | [/sdk/examples/simple-map-npm](/sdk/examples/simple-map-npm) | | `/web-sdk/react-map-example` | [/sdk/examples/react-map-example](/sdk/examples/react-map-example) | | `/web-sdk/simple-map-cdn` | [/sdk/examples/simple-map-cdn](/sdk/examples/simple-map-cdn) | | `/web-sdk/add-a-marker` | [/sdk/examples/add-a-marker](/sdk/examples/add-a-marker) | | `/web-sdk/getting-started` | [/getting-started](/getting-started) | → [Browse all examples](/examples) → [Getting Started](/getting-started) ---