Project geometry

View on GitHub

Project a point to another spatial reference.

Screenshot of project geometry sample

Use case

Being able to project between spatial references is fundamental in GIS. An example of when you would need to re-project data is if you had data in two different spatial references, but wanted to perform an intersect analysis with the static GeometryEngine.intersection(_:_:) method. This method takes two geometries as parameters, and both geometries must be in the same spatial reference. If they are not, you could first use static GeometryEngine.project(_:into:) to convert the geometries so they match.

How to use the sample

Tap anywhere on the map. A callout will display the tapped location's coordinate in the original basemap's spatial reference webMercator and in the projected spatial reference wgs84.

How it works

  1. Call the method, static GeometryEngine.project(_:into:), passing in the original geometry and a spatial reference system to be projected to.

Relevant API

  • GeometryEngine
  • Point
  • SpatialReference
  • static GeometryEngine.project(_:into:)

Additional information

In cases where the output spatial reference uses a different geographic coordinate system than that of the input spatial reference, see the static GeometryEngine.project(_:into:datumTransformation:) method that additionally takes in a DatumTransformation parameter.

Tags

coordinate system, coordinates, latitude, longitude, projected, projection, spatial reference, Web Mercator, WGS 84

Sample Code

ProjectGeometryView.swift
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright 2022 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 ArcGIS
import SwiftUI

struct ProjectGeometryView: View {
    /// A location callout placement.
    @State private var calloutPlacement: CalloutPlacement?

    /// The point where the map was tapped in its original spatial reference (Web Mercator).
    @State private var originalPoint: Point!

    /// The projected location after normalization.
    @State private var projectedPoint: Point!

    /// The view model for the sample.
    @StateObject private var model = Model()

    var body: some View {
        MapView(map: model.map, graphicsOverlays: [model.graphicsOverlay])
            .onSingleTapGesture { _, mapPoint in
                if calloutPlacement == nil {
                    // Sets the original point to where the map was tapped.
                    originalPoint = GeometryEngine.normalizeCentralMeridian(of: mapPoint) as? Point

                    // Projects the original point from Web Mercator to WGS 84.
                    projectedPoint = GeometryEngine.project(originalPoint!, into: .wgs84)!

                    // Updates the geometry of the point graphic.
                    model.pointGraphic.geometry = projectedPoint

                    // Updates the location callout placement.
                    calloutPlacement = CalloutPlacement.location(projectedPoint)
                } else {
                    // Hides the callout and point graphic.
                    calloutPlacement = nil
                    model.pointGraphic.geometry = nil
                }
            }
            .callout(placement: $calloutPlacement.animation(.default.speed(2))) { _ in
                VStack(alignment: .leading) {
                    Group {
                        Text("Coordinates")
                            .fontWeight(.medium)
                        Text("Original: \(originalPoint.xyCoordinates)")
                        Text("Projected: \(projectedPoint.xyCoordinates)")
                    }
                    .font(.callout)
                }
                .padding(6)
            }
            .overlay(alignment: .top) {
                Text("Tap on the map.")
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 6)
                    .background(.thinMaterial, ignoresSafeAreaEdges: .horizontal)
            }
    }
}

private extension ProjectGeometryView {
    /// The model used to store the geo model and other expensive objects
    /// used in this view.
    class Model: ObservableObject {
        /// A map with a topographic basemap style and an initial viewpoint.
        let map: Map = {
            let map = Map(basemapStyle: .arcGISTopographic)
            map.initialViewpoint = Viewpoint(
                center: Point(x: -1.2e7, y: 5e6, spatialReference: .webMercator),
                scale: 4e7
            )
            return map
        }()

        /// A graphics overlay containing a graphic with a circular, red marker symbol.
        let graphicsOverlay = GraphicsOverlay(graphics: [
            Graphic(symbol: SimpleMarkerSymbol(color: .red, size: 8))
        ])

        /// The graphic with a circular, red marker symbol.
        var pointGraphic: Graphic { graphicsOverlay.graphics.first! }
    }
}

private extension FormatStyle where Self == FloatingPointFormatStyle<Double> {
    /// Formats the double with four decimals places of precision.
    static var decimal: Self {
        Self.number.precision(.fractionLength(4))
    }
}

private extension Point {
    /// The point's decimal-formatted x and y coordinates.
    var xyCoordinates: Text {
        Text("\(self.x, format: .decimal), \(self.y, format: .decimal)")
    }
}

#Preview {
    ProjectGeometryView()
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.