Я использую Mapbox для создания приложения для iOS. Приложение получает запрос к моему API, который возвращает количество событий, происходящих в ограничивающей рамке карты в формате JSON.
Раньше я не использовал кластеризацию, поэтому некоторые аннотации карты просто покрывали другие. Я использую этот учебник Mapbox , который создает MGLShapeCollectionFeature
из файла GeoJSON, создает MGLShapeSource
из элемента коллекции фигур, а затем создает маркерный слой как MGLSymbolStyleLayer
, слой с кругом как MGLCircleStyleLayer
и слой чисел как MGLSymbolStyleLayer
. Слой маркера показывает каждое отдельное событие географически, круговой слой и слой чисел объединяются для представления количества маркеров каждого кластера.
Конечный продукт должен выглядеть аналогично примеру Mapbox:
Это файл GeoJSON, который в примере используется для отображения морских портов кластеризации на карте мира.
Ниже приведен соответствующий код, который используется в примере для преобразования указанного GeoJSON в соответствующий источник и слои для заполнения карты:
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "ports", ofType: "geojson")!)
let source = MGLShapeSource(identifier: "clusteredPorts",
url: url,
options: [.clustered: true, .clusterRadius: icon.size.width])
style.addSource(source)
// Use a template image so that we can tint it with the `iconColor` runtime styling property.
style.setImage(icon.withRenderingMode(.alwaysTemplate), forName: "icon")
// Show unclustered features as icons. The `cluster` attribute is built into clustering-enabled
// source features.
let ports = MGLSymbolStyleLayer(identifier: "ports", source: source)
ports.iconImageName = NSExpression(forConstantValue: "icon")
ports.iconColor = NSExpression(forConstantValue: UIColor.darkGray.withAlphaComponent(0.9))
ports.predicate = NSPredicate(format: "cluster != YES")
style.addLayer(ports)
// Color clustered features based on clustered point counts.
let stops = [
20: UIColor.lightGray,
50: UIColor.orange,
100: UIColor.red,
200: UIColor.purple
]
// Show clustered features as circles. The `point_count` attribute is built into
// clustering-enabled source features.
let circlesLayer = MGLCircleStyleLayer(identifier: "clusteredPorts", source: source)
circlesLayer.circleRadius = NSExpression(forConstantValue: NSNumber(value: Double(icon.size.width) / 2))
circlesLayer.circleOpacity = NSExpression(forConstantValue: 0.75)
circlesLayer.circleStrokeColor = NSExpression(forConstantValue: UIColor.white.withAlphaComponent(0.75))
circlesLayer.circleStrokeWidth = NSExpression(forConstantValue: 2)
circlesLayer.circleColor = NSExpression(format: "mgl_step:from:stops:(point_count, %@, %@)", UIColor.lightGray, stops)
circlesLayer.predicate = NSPredicate(format: "cluster == YES")
style.addLayer(circlesLayer)
// Label cluster circles with a layer of text indicating feature count. The value for
// `point_count` is an integer. In order to use that value for the
// `MGLSymbolStyleLayer.text` property, cast it as a string.
let numbersLayer = MGLSymbolStyleLayer(identifier: "clusteredPortsNumbers", source: source)
numbersLayer.textColor = NSExpression(forConstantValue: UIColor.white)
numbersLayer.textFontSize = NSExpression(forConstantValue: NSNumber(value: Double(icon.size.width) / 2))
numbersLayer.iconAllowsOverlap = NSExpression(forConstantValue: true)
numbersLayer.text = NSExpression(format: "CAST(point_count, 'NSString')")
numbersLayer.predicate = NSPredicate(format: "cluster == YES")
style.addLayer(numbersLayer)
Это формат GeoJSON, в котором мои события возвращаются из моего API как . Это форматирование должно быть правильным, так как Mapbox принимает его и создает MGLShapeCollectionFeature
из его данных.
Мой код очень похож на тот, что показан в примере с Mapbox. Сначала я создаю файл GeoJSON
//geoJson is my GeoJSON file as [String: Any]
var shapes: MGLShapeCollectionFeature!
if let data = try? JSONSerialization.data(withJSONObject: geoJson, options: .prettyPrinted) {
do {
shapes = try MGLShape(data: data, encoding: String.Encoding.utf8.rawValue) as! MGLShapeCollectionFeature
} catch {
print(error.localizedDescription)
}
}
Я знаю, что этот GeoJSON преобразуется в MGLShapeCollectionFeature
, так как приложение может аварийно завершить работу, если оно не сработало, а созданный MGLShapeCollectionFeature
успешно создает источник, из которого создаются слои или заполняются карта. Поэтому я создаю MGLShapeSource
из этого MGLShapeCollectionFeature
:
let marker = UIImage(named: "redPin")?.resize(targetSize: CGSize(width: 25, height: 25))
let source = MGLShapeSource(identifier: "clusteredPoints", shape: shapes, options: [.clustered: true, .clusterRadius: 0.5])
self.mapStyle!.addSource(source)
// Use a template image so that we can tint it with the `iconColor` runtime styling property.
self.mapStyle!.setImage(marker!, forName: "marker")
Затем я создаю слои из «источника» и добавляю их в стиль моей карты.
// Show unclustered features as icons. The `cluster` attribute is built into clustering-enabled
// source features.
let events = MGLSymbolStyleLayer(identifier: "events", source: source)
events.iconImageName = NSExpression(forConstantValue: "marker")
events.iconColor = NSExpression(forConstantValue: UIColor.darkGray.withAlphaComponent(0.9))
events.predicate = NSPredicate(format: "cluster != YES")
self.mapStyle!.addLayer(events)
// Color clustered features based on clustered point counts.
let stops = [
5: UIColor.lightGray,
10: UIColor.orange,
20: UIColor.red,
30: UIColor.purple
]
// Show clustered features as circles. The `point_count` attribute is built into
// clustering-enabled source features.
let circlesLayer = MGLCircleStyleLayer(identifier: "clusteredEvents", source: source)
circlesLayer.circleRadius = NSExpression(forConstantValue: NSNumber(value: Double(self.mapStyle!.image(forName: "marker")!.size.width) / 2))
circlesLayer.circleOpacity = NSExpression(forConstantValue: 0.75)
circlesLayer.circleStrokeColor = NSExpression(forConstantValue: UIColor.white.withAlphaComponent(0.75))
circlesLayer.circleStrokeWidth = NSExpression(forConstantValue: 2)
circlesLayer.circleColor = NSExpression(format: "mgl_step:from:stops:(point_count, %@, %@)", UIColor.lightGray, stops)
circlesLayer.predicate = NSPredicate(format: "cluster == YES")
self.mapStyle!.addLayer(circlesLayer)
// Label cluster circles with a layer of text indicating feature count. The value for
// `point_count` is an integer. In order to use that value for the
// `MGLSymbolStyleLayer.text` property, cast it as a string.
let numbersLayer = MGLSymbolStyleLayer(identifier: "clusteredEventsNumbers", source: source)
numbersLayer.textColor = NSExpression(forConstantValue: UIColor.white)
numbersLayer.textFontSize = NSExpression(forConstantValue: NSNumber(value: Double(self.mapStyle!.image(forName: "marker")!.size.width) / 2))
numbersLayer.iconAllowsOverlap = NSExpression(forConstantValue: true)
numbersLayer.text = NSExpression(format: "CAST(point_count, 'NSString')")
numbersLayer.predicate = NSPredicate(format: "cluster == YES")
self.mapStyle!.addLayer(numbersLayer)
Таким образом, код по сути такой же, просто вводимый GeoJSON отличается. И все же, слой круга и слой чисел не появляются, когда кластеры маркеров событий. Смотрите ниже:
Я знаю, что проблема не в том, что источник примера Mapbox загружается из URL, в то время как источник моей реализации загружается из MGLShapeCollectionFeature
, потому что я попытался загрузить порт GeoJSON примера Mapbox как MGLShapeCollectionFeature
и морские порты все еще показывают слои кругов / чисел при кластеризации.