Use a symbol to display a geometry on a map.

Use case
Customize the appearance of a geometry type with a symbol style suitable for the data. For example, a tourism office may use pictures of landmarks as symbols on an online map or app to help prospective visitors orient themselves more easily around a city. A point on the map styled with a circle could represent a drilled borehole location, whereas a cross could represent the location of an old coal mine shaft. A red line with a dashed style could represent a geological fault mapped on a geological map. A polygon with a brown ‘forward-diagonal’ fill style could represent an area of artificial ground mapped on a geological map.
How to use the sample
Tap “Edit Styles” and select a geometry to edit with the picker. Use the controls to change the symbol properties for the geometry.
How it works
- Create a
PictureMarkerSymbolorSimpleMarkerSymbolto style aPoint.- For the picture marker symbol, create it using a URL or image and set its height property.
- For the simple marker symbol, set the
style,color, andsizeproperties.
- Create a
SimpleLineSymbolto style aPolyline.- Set the
style,color, andsizeproperties.
- Set the
- Create a
SimpleFillSymbolto style aPolygon.- Set the
style,color, andoutlineproperties.
- Set the
- Create
Graphics using the geometries and symbols and add them to aGraphicsOverlay. - Add the graphics overlay to a
MapView.
Relevant API
- Geometry
- Graphic
- GraphicsOverlay
- PictureMarkerSymbol
- SimpleFillSymbol
- SimpleLineSymbol
- SimpleMarkerSymbol
Tags
display, fill, graphics, line, marker, overlay, picture, point, symbol, visualization
Sample code
// Copyright 2024 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 StyleGeometryTypesWithSymbolsView: View { /// The view model for the sample. @StateObject private var model = Model()
/// A Boolean value indicating whether the edit styles view is presented. @State private var isEditStyles = false
var body: some View { MapView(map: model.map, graphicsOverlays: [model.graphicsOverlay]) .toolbar { ToolbarItem(placement: .bottomBar) { editStylesButton } } }
/// The button for presenting the edit styles view. private var editStylesButton: some View { Button("Edit Styles") { isEditStyles = true } .popover(isPresented: $isEditStyles) { editStyles .presentationDetents([.fraction(0.5)]) .frame(idealWidth: 320, idealHeight: 380) } }
/// The view for editing the styles of the geometries. private var editStyles: some View { NavigationStack { SymbolsEditor(model: model) .navigationTitle("Edit Styles") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Done") { isEditStyles = false } } } } }}
extension StyleGeometryTypesWithSymbolsView { /// The view model for the sample. final class Model: ObservableObject { /// A map with topographic basemap initially centered on Woolgarston, England. let map: Map = { let map = Map(basemapStyle: .arcGISTopographic) map.initialViewpoint = Viewpoint(center: Point(x: -225e3, y: 6_553e3), scale: 88e3) return map }()
/// A graphics overlay for displaying the geometry graphics on the map view. let graphicsOverlay = GraphicsOverlay()
/// The simple marker symbol for styling the point. let pointSymbol = SimpleMarkerSymbol(style: .circle, color: .purple, size: 12)
/// The simple line symbol for styling the polyline. let polylineSymbol = SimpleLineSymbol(style: .dashDotDot, color: .red, width: 6)
/// The simple fill symbol for styling the polygon. let polygonSymbol = SimpleFillSymbol( style: .forwardDiagonal, color: .blue, outline: SimpleLineSymbol(style: .solid, color: .green, width: 3) )
init() { // Creates and adds graphics to the graphics overlay. let graphics = makeGraphics() graphicsOverlay.addGraphics(graphics) }
/// Creates the graphics for the sample. /// - Returns: Graphics with a geometry and symbol. private func makeGraphics() -> [Graphic] { // Creates graphics using a geometry and symbol. let point = Point(x: -225e3, y: 6_560e3) let pointGraphic = Graphic(geometry: point, symbol: pointSymbol)
let polyline = Polyline(points: [ Point(x: -223e3, y: 6_559e3), Point(x: -227e3, y: 6_559e3) ]) let polylineGraphic = Graphic(geometry: polyline, symbol: polylineSymbol)
let polygon = Polygon(points: [ Point(x: -222e3, y: 6_558e3), Point(x: -228e3, y: 6_558e3), Point(x: -228e3, y: 6_555e3), Point(x: -222e3, y: 6_555e3) ]) let polygonGraphic = Graphic(geometry: polygon, symbol: polygonSymbol)
// Creates graphics using points and picture marker symbols. let pinSymbol = makePictureMarkerSymbolFromImage() let pinPoint = Point(x: -226_770, y: 6_550_470) let pinGraphic = Graphic(geometry: pinPoint, symbol: pinSymbol)
let campsiteSymbol = makePictureMarkerSymbolFromURL() let campsitePoint = Point(x: -223_560, y: 6_552_020) let campsiteGraphic = Graphic(geometry: campsitePoint, symbol: campsiteSymbol)
return [pointGraphic, polylineGraphic, polygonGraphic, pinGraphic, campsiteGraphic] }
/// Creates a picture marker symbol from an image in the project assets. /// - Returns: A new `PictureMarkerSymbol` object. private func makePictureMarkerSymbolFromImage() -> PictureMarkerSymbol { let pinSymbol = PictureMarkerSymbol(image: .pinBlueStar)
// Changes the symbol's offset, so the symbol aligns properly to the point. pinSymbol.offsetY = pinSymbol.image!.size.height / 2
return pinSymbol }
/// Creates a picture marker symbol using a remote image. /// - Returns: A new `PictureMarkerSymbol` object. private func makePictureMarkerSymbolFromURL() -> PictureMarkerSymbol { let imageURL = URL( string: "https://static.arcgis.com/images/Symbols/OutdoorRecreation/Camping.png" )! let campsiteSymbol = PictureMarkerSymbol(url: imageURL)
campsiteSymbol.width = 25 campsiteSymbol.height = 25
return campsiteSymbol } }}
#Preview { StyleGeometryTypesWithSymbolsView()}// Copyright 2024 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
extension StyleGeometryTypesWithSymbolsView { /// Controls for editing the symbols contained in a given model. struct SymbolsEditor: View { /// The view model for the sample containing the symbols to edit. @ObservedObject var model: Model
/// The type of geometry currently being edited. @State private var selectedGeometryType: GeometryType = .point
var body: some View { Form { Section { EnumerationPicker("Geometry", selection: $selectedGeometryType) .pickerStyle(.segmented) .animation(.default, value: selectedGeometryType) }
switch selectedGeometryType { case .point: SimpleMarkerSymbolEditor(symbol: model.pointSymbol) case .polyline: SimpleLineSymbolEditor(symbol: model.polylineSymbol) case .polygon: SimpleFillSymbolEditor(symbol: model.polygonSymbol) } } } }
/// The types of the geometries supported by this sample. private enum GeometryType: CaseIterable, LabeledEnumeration { case point, polyline, polygon
/// A human-readable label for the geometry type. var label: String { switch self { case .point: "Point" case .polyline: "Polyline" case .polygon: "Polygon" } } }}
/// Controls for editing a simple marker symbol's properties.private struct SimpleMarkerSymbolEditor: View { /// The simple marker symbol to edit. let symbol: SimpleMarkerSymbol
/// The symbol's style selected by the picker. @State private var selectedStyle: SimpleMarkerSymbol.Style = .circle
/// The symbol's color selected by the picker. @State private var selectedColor: Color = .clear
/// The symbol's size selected by the stepper. @State private var selectedSize: Int = .zero
var body: some View { EnumerationPicker("Style", selection: $selectedStyle) .onChange(of: selectedStyle) { symbol.style = selectedStyle }
ColorPicker("Color", selection: $selectedColor) .onChange(of: selectedColor) { symbol.color = UIColor(selectedColor) }
Stepper("Size: \(selectedSize)", value: $selectedSize, in: 5...15) .onChange(of: selectedSize) { symbol.size = CGFloat(selectedSize) } .onAppear { selectedStyle = symbol.style selectedColor = Color(uiColor: symbol.color) selectedSize = Int(symbol.size) } }}
/// Controls for editing a simple line symbol's properties.private struct SimpleLineSymbolEditor: View { /// The simple line symbol to edit. let symbol: SimpleLineSymbol
/// The symbol's style selected by the picker. @State private var selectedStyle: SimpleLineSymbol.Style = .noLine
/// The symbol's color selected by the picker. @State private var selectedColor: Color = .clear
/// The symbol's width selected by the stepper. @State private var selectedWidth: Int = .zero
var body: some View { EnumerationPicker("Style", selection: $selectedStyle) .onChange(of: selectedStyle) { symbol.style = selectedStyle }
ColorPicker("Color", selection: $selectedColor) .onChange(of: selectedColor) { symbol.color = UIColor(selectedColor) }
Stepper("Width: \(selectedWidth)", value: $selectedWidth, in: 1...10) .onChange(of: selectedWidth) { symbol.width = CGFloat(selectedWidth) } .onAppear { selectedStyle = symbol.style selectedColor = Color(uiColor: symbol.color) selectedWidth = Int(symbol.width) } }}
/// Controls for editing a simple fill symbol's properties.private struct SimpleFillSymbolEditor: View { /// The simple fill symbol to edit. let symbol: SimpleFillSymbol
/// The symbol's style selected by the picker. @State private var selectedStyle: SimpleFillSymbol.Style = .noFill
/// The symbol's color selected by the picker. @State private var selectedColor: Color = .clear
var body: some View { EnumerationPicker("Style", selection: $selectedStyle) .onChange(of: selectedStyle) { symbol.style = selectedStyle }
ColorPicker("Color", selection: $selectedColor) .onChange(of: selectedColor) { symbol.color = UIColor(selectedColor) }
Section("Outline") { SimpleLineSymbolEditor(symbol: symbol.outline as! SimpleLineSymbol) } .onAppear { selectedStyle = symbol.style selectedColor = Color(uiColor: symbol.color) } }}
/// A picker for selecting a value from an enumeration's cases.private struct EnumerationPicker<T: LabeledEnumeration>: View { /// The title of the picker. private let title: String
/// A binding to a property that determines the currently-selected style. @Binding private var selection: T
init(_ title: String, selection: Binding<T>) { self.title = title self._selection = selection }
var body: some View { Picker(title, selection: $selection) { ForEach(T.allCases, id: \.self) { style in Text(style.label) } }#if targetEnvironment(macCatalyst) // Workaround for bug where the picker selection doesn't update when the // binding value changes on Mac Catalyst. .id(selection)#endif }}
// MARK: LabeledEnumeration
/// A protocol describing an enumeration with labels.private protocol LabeledEnumeration: Hashable { static var allCases: [Self] { get } var label: String { get }}
extension SimpleMarkerSymbol.Style: LabeledEnumeration { fileprivate static var allCases: [Self] { return [.circle, .cross, .diamond, .square, .triangle, .x] }
/// A human-readable label for the simple marker symbol style. fileprivate var label: String { switch self { case .circle: "Circle" case .cross: "Cross" case .diamond: "Diamond" case .square: "Square" case .triangle: "Triangle" case .x: "X" @unknown default: "Unknown" } }}
extension SimpleLineSymbol.Style: LabeledEnumeration { fileprivate static var allCases: [Self] { return [.dash, .dashDot, .dashDotDot, .dot, .longDash, .longDashDot, .noLine, .shortDash, .shortDashDot, .shortDashDotDot, .shortDot, .solid] }
/// A human-readable label for the simple line symbol style. fileprivate var label: String { switch self { case .dash: "Dash" case .dashDot: "Dash Dot" case .dashDotDot: "Dash Dot Dot" case .dot: "Dot" case .longDash: "Long Dash" case .longDashDot: "Long Dash Dot" case .noLine: "No Line" case .shortDash: "Short Dash" case .shortDashDot: "Short Dash Dot" case .shortDashDotDot: "Short Dash Dot Dot" case .shortDot: "Short Dot" case .solid: "Solid" @unknown default: "Unknown" } }}
extension SimpleFillSymbol.Style: LabeledEnumeration { fileprivate static var allCases: [Self] { return [.backwardDiagonal, .cross, .diagonalCross, .forwardDiagonal, .horizontal, .noFill, .solid, .vertical] }
/// A human-readable label for the simple fill symbol style. fileprivate var label: String { switch self { case .backwardDiagonal: "Backward Diagonal" case .cross: "Cross" case .diagonalCross: "Diagonal Cross" case .forwardDiagonal: "Forward Diagonal" case .horizontal: "Horizontal" case .noFill: "No Fill" case .solid: "Solid" case .vertical: "Vertical" @unknown default: "Unknown" } }}