Show different kinds of multilayer symbols on a map similar to some pre-defined 2D simple symbol styles.

Use case
Allows you to customize a graphic with a multilayer symbol. For example, you may want more customizable symbols than the one that is provided with the API to display a unique representation of a landmark.
How to use the sample
The sample loads multilayer symbols for points, polylines, and polygons.
How it works
- Create multilayer symbols for each predefined 2D simple symbol style.
- For multilayer point symbols, use
MultilayerPointSymbol(symbolLayers:referenceProperties:). - For multilayer polyline symbols, use
MultilayerPolylineSymbol(symbolLayers:referenceProperties:). - For multilayer polygon symbols, use
MultiLayerPolygonSymbol(symbolLayers:referenceProperties:).
- For multilayer point symbols, use
- Create graphics by passing in a geometry and the associated symbol.
- Add graphics to the graphics overlay with
graphicsOverlay.addGraphics(_:)
Relevant API
- Graphic
- GraphicsOverlay
- MultiLayerPointSymbol
- MultiLayerPolygonSymbol
- MultiLayerPolylineSymbol
- PictureMarkerSymbolLayer
- SolidFillSymbolLayer
- SolidStrokeSymbolLayer
- VectorMarkerSymbolLayer
About the data
The campsite picture marker symbol is constructed from a URL.
Tags
graphic, marker, multilayer, picture, symbol
Sample code
RenderMultilayerSymbolsView.swift
// Copyright 2023 Esri//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import ArcGISimport SwiftUI
struct RenderMultilayerSymbolsView: View { /// A map with a light gray basemap. @State private var map: Map = { let map = Map(basemapStyle: .arcGISLightGray) map.initialViewpoint = Viewpoint(boundingGeometry: Point(x: -713_800, y: 0, spatialReference: .webMercator)) return map }()
/// The graphics overlay containing the multilayer graphics and associated text graphics. @State private var graphicsOverlay: GraphicsOverlay = { let graphicsOverlay = GraphicsOverlay()
// Create graphics and add them to the graphics overlay. graphicsOverlay.addGraphics(makeMultilayerPointSimpleMarkerGraphics()) graphicsOverlay.addGraphics(makeMultilayerPointPictureMarkerGraphics()) graphicsOverlay.addGraphics(makeMultilayerPolylineGraphics()) graphicsOverlay.addGraphics(makeMultilayerPolygonGraphics()) graphicsOverlay.addGraphics(makeComplexMultilayerSymbolGraphics())
return graphicsOverlay }()
/// The offset used to keep a consistent distance between symbols in the same column. private static let offsetBetweenSymbols = 20.0
var body: some View { MapView(map: map, graphicsOverlays: [graphicsOverlay]) }}
private extension RenderMultilayerSymbolsView { // MARK: Methods
/// Creates a text symbol with the text to be displayed on the map. /// - Parameter text: The text used to create the text symbol. /// - Returns: A new `TextSymbol` object. private static func makeTextSymbol(text: String) -> TextSymbol { // Create text symbol with a white background. let textSymbol = TextSymbol(text: text, color: .black, size: 10) textSymbol.backgroundColor = .white return textSymbol }
// MARK: MultilayerPoint Simple Markers
/// Creates the multilayer point simple marker graphics. /// - Returns: The new `Graphic` objects to display on the map. private static func makeMultilayerPointSimpleMarkerGraphics() -> [Graphic] { // Create a text graphic. var graphics = [ Graphic( geometry: Point(x: -150, y: 50, spatialReference: .wgs84), symbol: makeTextSymbol(text: "MultilayerPoint\nSimple Markers") ) ]
// Create a red diamond graphic from JSON and a multilayer polygon symbol. let redFillLayer = SolidFillSymbolLayer(color: .red) let polygonSymbol = MultilayerPolygonSymbol(symbolLayers: [redFillLayer]) if let diamondGeometry = try? Geometry.fromJSON(.diamondGeometryJSON) { graphics.append(makeGraphicWithVectorMarkerSymbolElement( symbol: polygonSymbol, geometry: diamondGeometry, offset: 0 )) }
// Create a triangle graphic from JSON and a multilayer polygon symbol. if let triangleGeometry = try? Geometry.fromJSON(.triangleGeometryJSON) { graphics.append(makeGraphicWithVectorMarkerSymbolElement( symbol: polygonSymbol, geometry: triangleGeometry, offset: offsetBetweenSymbols )) }
// Create a cross graphic from JSON and a multilayer polyline symbol. let redStrokeLayer = SolidStrokeSymbolLayer(width: 1, color: .red) let polylineSymbol = MultilayerPolylineSymbol(symbolLayers: [redStrokeLayer]) if let crossGeometry = try? Geometry.fromJSON(.crossGeometryJSON) { graphics.append(makeGraphicWithVectorMarkerSymbolElement( symbol: polylineSymbol, geometry: crossGeometry, offset: offsetBetweenSymbols * 2 )) }
return graphics }
/// Creates a graphic of multilayer point symbol using a vector marker symbol element. /// - Parameters: /// - symbol: The symbol to create the graphic of. /// - geometry: The geometry used to make the vector marker symbol element. /// - offset: The offset used to keep a consistent distance between symbols in the same column. /// - Returns: A new `Graphic` object of the multilayer point symbol. private static func makeGraphicWithVectorMarkerSymbolElement( symbol: MultilayerSymbol, geometry: Geometry, offset: Double ) -> Graphic { // Create a vector element using the passed symbol and geometry. let vectorElement = VectorMarkerSymbolElement(geometry: geometry, multilayerSymbol: symbol)
// Create a vector layer using the vector element. let vectorLayer = VectorMarkerSymbolLayer(vectorMarkerSymbolElements: [vectorElement])
// Create a point symbol using the vector layer. let pointSymbol = MultilayerPointSymbol(symbolLayers: [vectorLayer])
// Create a graphic of the multilayer point symbol. return Graphic(geometry: Point(x: -150, y: 20 - offset, spatialReference: .wgs84), symbol: pointSymbol) }
// MARK: MultilayerPoint Picture Markers
/// Creates the multilayer point picture marker graphics . /// - Returns: The new `Graphic` objects to display on the map. private static func makeMultilayerPointPictureMarkerGraphics() -> [Graphic] { // Create a text graphic. var graphics = [ Graphic( geometry: Point(x: -80, y: 50, spatialReference: .wgs84), symbol: makeTextSymbol(text: "MultilayerPoint\nPicture Markers") ) ]
// Create a campsite graphic using a URL. let campsiteLayer = PictureMarkerSymbolLayer(url: .campsiteImage) graphics.append( makeGraphicFromPictureMarkerSymbol(layer: campsiteLayer, offset: 0) )
// Create a pin graphic using an image in the project assets. let pinLayer = PictureMarkerSymbolLayer(image: .pinBlueStar) graphics.append( makeGraphicFromPictureMarkerSymbol(layer: pinLayer, offset: 40) )
return graphics }
/// Creates a graphic from a picture marker symbol layer using a multilayer point symbol. /// - Parameters: /// - layer: The layer used to create the multilayer point symbol. /// - offset: The offset used to keep a consistent distance between symbols in the same column. /// - Returns: A new `Graphic` object of the multilayer point symbol. private static func makeGraphicFromPictureMarkerSymbol(layer: PictureMarkerSymbolLayer, offset: Double) -> Graphic { // Create a multilayer point symbol using the picture marker symbol layer. layer.size = 40 let symbol = MultilayerPointSymbol(symbolLayers: [layer])
// Create the location for symbol using a point. let symbolPoint = Point(x: -80, y: 20 - offset, spatialReference: .wgs84)
// Create the graphic for multilayer point symbol. return Graphic(geometry: symbolPoint, symbol: symbol) }
// MARK: Multilayer Polylines
/// Creates the multilayer polyline graphics. /// - Returns: The new `Graphic` objects to display on the map. private static func makeMultilayerPolylineGraphics() -> [Graphic] { // Create a text graphic. var graphics = [ Graphic( geometry: Point(x: 0, y: 50, spatialReference: .wgs84), symbol: makeTextSymbol(text: "Multilayer\nPolylines") ) ]
// Create the polyline graphics. graphics.append(contentsOf: [ // Dash, dot, dot. makeMultilayerPolylineSymbolGraphic(dashSpacing: [4, 6, 0.5, 6, 0.5, 6], offset: 0), // Dashes. makeMultilayerPolylineSymbolGraphic(dashSpacing: [4, 6], offset: offsetBetweenSymbols), // Dash, dot. makeMultilayerPolylineSymbolGraphic(dashSpacing: [7, 9, 0.5, 9], offset: offsetBetweenSymbols * 2) ])
return graphics }
/// Creates a graphic of a dashed multilayer polyline symbol. /// - Parameters: /// - dashSpacing: The pattern of spaces and dashes used to create a dash effect. /// - offset: The offset used to keep a consistent distance between symbols in the same column. /// - Returns: A new `Graphic` object of the multilayer polyline symbol. private static func makeMultilayerPolylineSymbolGraphic(dashSpacing: [Double], offset: Double) -> Graphic { // Create a dash effect with the passed dash spacing. let dashEffect = DashGeometricEffect(dashTemplate: dashSpacing)
// Create a solid stroke symbol layer for the line symbol. let strokeLayer = SolidStrokeSymbolLayer(width: 3, color: .red, geometricEffects: [dashEffect]) strokeLayer.capStyle = .round
// Create a multilayer polyline symbol from the stroke layer. let lineSymbol = MultilayerPolylineSymbol(symbolLayers: [strokeLayer])
// Create a polyline for the graphic. let polyline = Polyline( points: [ Point(x: -30, y: 20 - offset), Point(x: 30, y: 20 - offset) ], spatialReference: .wgs84 )
// Create a graphic of the multilayer polyline symbol. return Graphic(geometry: polyline, symbol: lineSymbol) }
// MARK: Multilayer Polygons
/// Creates the multilayer polygon graphics. /// - Returns: The new `Graphic` objects to display on the map. private static func makeMultilayerPolygonGraphics() -> [Graphic] { // Create a text graphic. var graphics = [ Graphic( geometry: Point(x: 65, y: 50, spatialReference: .wgs84), symbol: makeTextSymbol(text: "Multilayer\nPolygons") ) ]
// Create multilayer polygon symbols. graphics.append(contentsOf: [ // Cross-hatched diagonal lines. makeMultilayerPolygonSymbolGraphic(angles: [-45, 45], offset: 0), // Hatched diagonal lines. makeMultilayerPolygonSymbolGraphic(angles: [-45], offset: offsetBetweenSymbols), // Hatched vertical lines. makeMultilayerPolygonSymbolGraphic(angles: [90], offset: offsetBetweenSymbols * 2) ])
return graphics }
/// Creates a graphic of a multilayer polygon symbol. /// - Parameters: /// - angles: The angles at which to draw the fill lines within the polygon. /// - offset: The offset used to keep a consistent distance between symbols in the same column. /// - Returns: A new `Graphic` object of the multilayer polygon symbol. private static func makeMultilayerPolygonSymbolGraphic(angles: [Double], offset: Double) -> Graphic { // Create a stroke symbol layer to make the symbol layer. let hatchStrokeLayer = SolidStrokeSymbolLayer(width: 2, color: .red)
// Create a list to hold all necessary symbol layers. var symbolLayers: [SymbolLayer] = []
// For each angle, create a hatch fill symbol layer with hatched lines at the given angle. for angle in angles { let hatchLayer = HatchFillSymbolLayer( polylineSymbol: MultilayerPolylineSymbol(symbolLayers: [hatchStrokeLayer]), angle: angle )
// Set separation distance for lines. hatchLayer.separation = 9 symbolLayers.append(hatchLayer) }
// Create a stroke symbol layer to be used as an outline. let outlineStrokeLayer = SolidStrokeSymbolLayer(width: 1, color: .black) symbolLayers.append(outlineStrokeLayer)
// Create a multilayer polygon symbol from the symbol layer list. let polygonSymbol = MultilayerPolygonSymbol(symbolLayers: symbolLayers)
// Create rectangle polygon for the graphic. let polygon = Polygon( points: [ Point(x: 60, y: 25 - offset), Point(x: 70, y: 25 - offset), Point(x: 70, y: 20 - offset), Point(x: 60, y: 20 - offset) ], spatialReference: .wgs84 )
// Create a graphic of the multilayer polygon symbol. return Graphic(geometry: polygon, symbol: polygonSymbol) }
// MARK: Complex Multilayer Symbols
/// Creates the complex multilayer symbol graphics. /// - Returns: The new `Graphic` objects to display on the map. private static func makeComplexMultilayerSymbolGraphics() -> [Graphic] { // Create a text graphic. var graphics = [ Graphic( geometry: Point(x: 130, y: 50, spatialReference: .wgs84), symbol: makeTextSymbol(text: "Complex Multilayer\nSymbols") ) ]
// Create the complex multilayer graphics: a point, polygon and polyline. if let complexPointGeometry = try? Geometry.fromJSON(.complexPointGeometryJSON) { graphics.append(makeComplexPointGraphic(geometry: complexPointGeometry)) } graphics.append(contentsOf: [ makeComplexPolygonGraphic(), makeComplexPolylineGraphic() ])
return graphics }
/// Creates a graphic of a complex multilayer point from multiple symbol layers and given geometry. /// - Parameter geometry: The geometry used to create a multilayer polygon symbol. /// - Returns: A new `Graphic` object of the multilayer point symbol. private static func makeComplexPointGraphic(geometry: Geometry) -> Graphic { // Create the vector marker symbol layers for the complex point. let orangeSquareLayer = makeLayerForComplexPoint(fillColor: .orange, outlineColor: .blue, size: 11) orangeSquareLayer.anchor = SymbolAnchor(x: -4, y: -6, placementMode: .absolute)
let blackSquareLayer = makeLayerForComplexPoint(fillColor: .black, outlineColor: .cyan, size: 6) blackSquareLayer.anchor = SymbolAnchor(x: 2, y: 1, placementMode: .absolute)
let purpleSquareLayer = makeLayerForComplexPoint(fillColor: .clear, outlineColor: .magenta, size: 14) purpleSquareLayer.anchor = SymbolAnchor(x: 4, y: 2, placementMode: .absolute)
// Create a layer of a yellow hexagon with a black outline from the passed geometry. let yellowFillLayer = SolidFillSymbolLayer(color: .yellow) let blackOutline = SolidStrokeSymbolLayer(width: 2, color: .black) let hexagonVectorElement = VectorMarkerSymbolElement( geometry: geometry, multilayerSymbol: MultilayerPolygonSymbol(symbolLayers: [yellowFillLayer, blackOutline]) ) let yellowHexagonLayer = VectorMarkerSymbolLayer(vectorMarkerSymbolElements: [hexagonVectorElement]) yellowHexagonLayer.size = 35
// Create the multilayer point symbol from the symbol layers. let pointSymbol = MultilayerPointSymbol(symbolLayers: [ yellowHexagonLayer, orangeSquareLayer, blackSquareLayer, purpleSquareLayer ])
// Create a graphic of the multilayer point symbol. return Graphic(geometry: Point(x: 130, y: 20, spatialReference: .wgs84), symbol: pointSymbol) }
/// Creates a vector marker symbol layer for use in the composition of a complex point. /// - Parameters: /// - fillColor: The fill color of the symbol. /// - outlineColor: The outline color of the symbol. /// - size: The size of the symbol. /// - Returns: A new `VectorMarkerSymbolLayer` object of the created symbol. private static func makeLayerForComplexPoint( fillColor: UIColor, outlineColor: UIColor, size: Double ) -> VectorMarkerSymbolLayer { // Create a fill layer and outline. let fillLayer = SolidFillSymbolLayer(color: fillColor) let outlineLayer = SolidStrokeSymbolLayer(width: 2, color: outlineColor)
// Create a geometry from an envelope. let geometry = Envelope( min: Point(x: -0.5, y: -0.5, spatialReference: .wgs84), max: Point(x: 0.5, y: 0.5, spatialReference: .wgs84) )
// Create a vector marker symbol element using the geometry and a multilayer polygon symbol. let symbolElement = VectorMarkerSymbolElement( geometry: geometry, multilayerSymbol: MultilayerPolygonSymbol(symbolLayers: [fillLayer, outlineLayer]) )
// Create a symbol layer containing the symbol element. let symbolLayer = VectorMarkerSymbolLayer(vectorMarkerSymbolElements: [symbolElement]) symbolLayer.size = size
return symbolLayer }
/// Creates a graphic of a complex polygon made with multiple symbol layers. /// - Returns: A new `Graphic` object of the multilayer polygon symbol. private static func makeComplexPolygonGraphic() -> Graphic { // Create a multilayer polygon symbol from the symbol layers. let polygonSymbol = MultilayerPolygonSymbol( symbolLayers: makeLayersForComplexMultilayerSymbols(includeRedFill: true) )
// Create a polygon for the graphic. let polygon = Polygon( points: [ Point(x: 120, y: 0), Point(x: 140, y: 0), Point(x: 140, y: -10), Point(x: 120, y: -10) ], spatialReference: .wgs84 )
// Create a graphic of the multilayer polygon symbol. return Graphic(geometry: polygon, symbol: polygonSymbol) }
/// Creates a graphic of the complex polyline made with multiple layers. /// - Returns: A new `Graphic` object of the multilayer polyline symbol. private static func makeComplexPolylineGraphic() -> Graphic { // Create a multilayer polyline symbol from the symbol layers. let polylineSymbol = MultilayerPolylineSymbol( symbolLayers: makeLayersForComplexMultilayerSymbols(includeRedFill: false) )
// Create a polyline geometry of the graphic. let polyline = Polyline( points: [ Point(x: 120, y: -25), Point(x: 140, y: -25) ], spatialReference: .wgs84 )
// Create a graphic of the multilayer polygon symbol. return Graphic(geometry: polyline, symbol: polylineSymbol) }
/// Creates the symbol layers used to create the complex multilayer symbols. /// - Parameter includeRedFill: A Boolean that indicates whether to include a red fill layer. /// - Returns: The new `SymbolLayer` objects. private static func makeLayersForComplexMultilayerSymbols(includeRedFill: Bool) -> [SymbolLayer] { // Create a black dash effect. let dashEffect = DashGeometricEffect(dashTemplate: [5, 3]) let blackDashesLayer = SolidStrokeSymbolLayer(width: 1, color: .black, geometricEffects: [dashEffect]) blackDashesLayer.capStyle = .square
// Create a black outline. let blackOutlineLayer = SolidStrokeSymbolLayer(width: 7, color: .black) blackOutlineLayer.capStyle = .round
// Create a yellow stroke. let yellowStrokeLayer = SolidStrokeSymbolLayer(width: 5, color: .yellow) yellowStrokeLayer.capStyle = .round
let symbolLayers = [blackOutlineLayer, yellowStrokeLayer, blackDashesLayer] return includeRedFill ? symbolLayers + [SolidFillSymbolLayer(color: .red)] : symbolLayers }}
private extension URL { /// The URL to a campsite image on ArcGIS online. static let campsiteImage = URL( string: "https://static.arcgis.com/images/Symbols/OutdoorRecreation/Camping.png" )!}
private extension Data { /// The JSON for a complex point geometry. static let complexPointGeometryJSON = Data("{\"rings\":[[[-2.89,5],[2.89,5],[5.77,0],[2.89,-5],[-2.89,-5],[-5.77,0],[-2.89,5]]]}".utf8)
/// The JSON for a diamond geometry. static let diamondGeometryJSON = Data("{\"rings\":[[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0],[0,2.5]]]}".utf8)
/// The JSON for a triangle geometry. static let triangleGeometryJSON = Data("{\"rings\":[[[0,5],[5,-5],[-5,-5],[0,5]]]}".utf8)
/// The JSON for a cross geometry. static let crossGeometryJSON = Data("{\"paths\":[[[-1,1],[0,0],[1,-1]],[[1,1],[0,0],[-1,-1]]]}".utf8)}
#Preview { RenderMultilayerSymbolsView()}