📍 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: "<YOUR_STYLE_URL_HERE>")
)
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")
}
}