Project a point to another spatial reference.
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
- 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
// 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()
}