Construct a KML document and save it as a KMZ file.
Use case
If you need to create and save data on the fly, you can use KML to create points, lines, and polygons by sketching on the map, customizing the style, and serializing them as KML nodes in a KML Document. Once complete, you can share the KML data with others that are using a KML reading application, such as ArcGIS Earth.
How to use the sample
Tap on the sketch button in the toolbar to add a new KML. Select a type of feature and choose its color or icon. Tap on the map to sketch the KML. Tap the sketch button and tap "Save Sketch" to complete the sketch. Tap the file export button in the toolbar to export the file. Tap the sketch button and the "Clear Saved Sketches" button to clear the current KML document.
How it works
- Create an
KMLDocument
. - Create an
KMLDataset
using theKMLDocument
. - Create an
KMLLayer
using theKMLDataset
and add it to the map'soperationalLayers
array. - Create
Geometry
usingGeometryEditor
. - Project that
Geometry
to WGS84 usingGeometryEngine.project(_:into:)
. - Create an
KMLGeometry
object using that projectedGeometry
. - Create an
KMLPlacemark
using theKMLGeometry
. - Add the
KMLPlacemark
to theKMLDocument
. - Set the
KMLStyle
for theKMLPlacemark
. - When finished with adding
KMLPlacemark
nodes to theKMLDocument
, save theKMLDocument
to a file using theKMLNode.save(to:)
method.
Relevant API
- GeometryEditor
- GeometryEngine
- KMLDataset
- KMLDocument
- KMLGeometry
- KMLLayer
- KMLNode
- KMLPlacemark
- KMLStyle
Tags
Keyhole, KML, KMZ, OGC
Sample Code
// 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 ArcGIS
import SwiftUI
import UniformTypeIdentifiers
struct CreateAndSaveKMLView: View {
/// The view model for this sample.
@StateObject var model = Model()
var body: some View {
MapView(map: model.map)
.geometryEditor(model.geometryEditor)
.errorAlert(presentingError: $model.error)
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Menu {
if !model.isStarted {
// If the geometry editor is not started, show the main menu.
mainMenuContent
} else {
// If the geometry editor is started, show the edit menu.
editMenuContent
}
} label: {
Label("Geometry Editor", systemImage: "pencil.tip.crop.circle")
}
Spacer()
Button {
model.showingFileExporter = true
} label: {
Label("Export File", systemImage: "square.and.arrow.up")
}
.disabled(model.fileExporterButtonIsDisabled)
}
}
.task {
for await geometry in model.geometryEditor.$geometry {
model.geometry = geometry
}
}
.fileExporter(isPresented: $model.showingFileExporter, document: model.kmzFile, contentType: .kmz) { result in
switch result {
case .success:
// We no longer need the file locally.
model.kmzFile.deleteFile()
// We no longer have a local file so disable file exporter button.
model.fileExporterButtonIsDisabled = true
case .failure(let error):
model.error = error
}
}
}
}
extension CreateAndSaveKMLView {
/// A KMZ file that can be used with the native file exporter.
final class KMZFile: FileDocument {
/// The KML document that is used to create the KMZ file.
private let document: KMLDocument
/// The temporary directory where the KMZ file will be stored.
private let temporaryDirectory = FileManager.createTemporaryDirectory()
/// The temporary URL to the KMZ file.
private var temporaryDocumentURL: URL? {
temporaryDirectory
.appendingPathComponent("\(document.name).kmz")
}
static var readableContentTypes: [UTType] { [.kmz] }
/// Creates a KMZ file with a KML document.
/// - Parameter document: The KML document that is used when creating the KMZ file.
init(document: KMLDocument) {
self.document = document
}
// This initializer loads data that has been saved previously.
init(configuration: ReadConfiguration) throws {
fatalError("Loading KML files is not supported by this sample")
}
// This will be called when the system wants to write our data to disk
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
guard let temporaryDocumentURL else { return FileWrapper() }
return try FileWrapper(url: temporaryDocumentURL)
}
/// Deletes the temporarily stored KMZ file.
func deleteFile() {
try? FileManager.default.removeItem(at: temporaryDirectory)
}
/// Saves the KML document as a KMZ file to a temporary location.
func saveFile() async throws {
if document.name.isEmpty {
document.name = "Untitled"
}
try await document.save(to: temporaryDocumentURL!)
}
}
}
private extension FileManager {
/// Creates a temporary directory and returns the URL of the created directory.
static func createTemporaryDirectory() -> URL {
// swiftlint:disable:next force_try
try! FileManager.default.url(
for: .itemReplacementDirectory,
in: .userDomainMask,
appropriateFor: FileManager.default.temporaryDirectory,
create: true
)
}
}
private extension UTType {
/// A type that represents a KMZ file.
static let kmz = UTType(filenameExtension: "kmz")!
}
#Preview {
NavigationStack {
CreateAndSaveKMLView()
}
}