mcpField
G
geotoolbox

geotoolbox

github.com/rshankras開発83874 回インストール14 週間前に更新
リポジトリ

geotoolbox について

Swift で場所を表現し、住所を解決する開発者向けの GeoToolbox スキルです。PlaceDescriptor、MapKit との変換、非同期ジオコーディング、複数の場所サービス間で使う識別子を扱います。

主な機能

  1. 01座標や住所で場所を表現
  2. 02PlaceDescriptor と MKMapItem を相互変換
  3. 03非同期の順方向・逆方向ジオコーディング
  4. 04複数の場所サービスの識別子を保存
  5. 05場所のオプショナル属性を安全に処理

活用シーン

  1. 01住所と座標を使って場所を表現
  2. 02住所を地図アイテムに解決
  3. 03座標から住所を検索
  4. 04システム間でサービス識別子を引き継ぐ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rshankras/claude-code-apple-skills --skill geotoolbox

インストール後、Claude Code で/geotoolboxを入力して読み込まれたか確認します。

詳細

GeoToolbox と PlaceDescriptor のパターン

GeoToolbox フレームワークの PlaceDescriptor を使用した、移植可能な位置情報の表現方法です。座標、住所、MapKit 項目から場所を構築する方法、新しい非同期 API を使ったフォワードジオコーディングとリバースジオコーディング、クロスプラットフォーム相互運用のための複数サービスの場所識別子について説明します。

このスキルが有効になる場合

次の場合にこのスキルを使用します。

  • GeoToolbox または PlaceDescriptor について質問された場合
  • 座標、住所、一般的な名称を使って場所を表現したい場合
  • MKMapItem と PlaceDescriptor の相互変換が必要な場合
  • フォワードジオコーディング(住所から座標への変換)または リバースジオコーディング(座標から住所への変換)について質問された場合
  • 複数サービスの場所識別子(Apple Maps、Google Maps など)を保存または送信したい場合
  • PlaceRepresentation、SupportingPlaceRepresentation、または MKGeocodingRequest に言及された場合
  • 移植可能または相互運用可能な位置情報データ構造について質問された場合

判断ツリー

What do you need?
|
+-- Represent a place with coordinates and/or address
|   +-- From a known coordinate
|   |   +-- PlaceRepresentation.coordinate(CLLocationCoordinate2D)
|   +-- From a known address string
|   |   +-- PlaceRepresentation.address(String)
|   +-- Both coordinate and address
|   |   +-- Pass multiple representations to PlaceDescriptor
|   +-- From an existing MKMapItem
|       +-- PlaceDescriptor(item: MKMapItem)
|
+-- Geocode an address to coordinates
|   +-- MKGeocodingRequest(addressString:)
|   +-- try await request.mapItems
|
+-- Reverse geocode coordinates to an address
|   +-- MKReverseGeocodingRequest(location:)
|   +-- try await request.mapItems
|
+-- Attach service identifiers (Apple Maps, Google, etc.)
|   +-- SupportingPlaceRepresentation.serviceIdentifiers([String: String])
|
+-- Read place properties
    +-- descriptor.coordinate, descriptor.address, descriptor.commonName
    +-- descriptor.serviceIdentifier(for: "com.apple.maps")

API の利用可能状況

API最小 OS バージョンインポート
PlaceDescriptoriOS 26 / macOS 26GeoToolbox
PlaceRepresentationiOS 26 / macOS 26GeoToolbox
SupportingPlaceRepresentationiOS 26 / macOS 26GeoToolbox
MKGeocodingRequestiOS 26 / macOS 26MapKit
MKReverseGeocodingRequestiOS 26 / macOS 26MapKit
PlaceDescriptor(item:)iOS 26 / macOS 26GeoToolbox + MapKit
MKMapItemiOS 6 / macOS 10.9MapKit
CLLocationCoordinate2DiOS 2 / macOS 10.6CoreLocation

クイックスタート

座標から PlaceDescriptor を作成する
import GeoToolbox
import CoreLocation

let coordinate = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090)

let descriptor = PlaceDescriptor(
    representations: [.coordinate(coordinate)],
    commonName: "Apple Park"
)

// Read back
if let coord = descriptor.coordinate {
    print("Lat: \(coord.latitude), Lon: \(coord.longitude)")
}
print(descriptor.commonName ?? "No name")
住所から PlaceDescriptor を作成する
import GeoToolbox

let descriptor = PlaceDescriptor(
    representations: [.address("One Apple Park Way, Cupertino, CA 95014")],
    commonName: "Apple Park"
)

if let address = descriptor.address {
    print("Address: \(address)")
}
複数の表現とサービス識別子
import GeoToolbox
import CoreLocation

let coordinate = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090)

let descriptor = PlaceDescriptor(
    representations: [
        .coordinate(coordinate),
        .address("One Apple Park Way, Cupertino, CA 95014")
    ],
    commonName: "Apple Park",
    supportingRepresentations: [
        .serviceIdentifiers([
            "com.apple.maps": "apple-maps-id-12345",
            "com.google.maps": "ChIJ-bfVTh8_j4ARDMPaL2Njo3I"
        ])
    ]
)

// Access a specific service identifier
if let appleId = descriptor.serviceIdentifier(for: "com.apple.maps") {
    print("Apple Maps ID: \(appleId)")
}
MKMapItem から変換する
import GeoToolbox
import MapKit

func descriptorFromMapItem(_ mapItem: MKMapItem) -> PlaceDescriptor {
    PlaceDescriptor(item: mapItem)
}

フォワードジオコーディング(住所から座標への変換)

import MapKit

func geocodeAddress(_ addressString: String) async throws -> [MKMapItem] {
    let request = MKGeocodingRequest(addressString: addressString)
    let mapItems = try await request.mapItems
    return mapItems
}

// Usage
let items = try await geocodeAddress("One Apple Park Way, Cupertino, CA")
if let first = items.first {
    let coord = first.placemark.coordinate
    print("Found: \(coord.latitude), \(coord.longitude)")
}

リバースジオコーディング(座標から住所への変換)

import MapKit
import CoreLocation

func reverseGeocode(_ coordinate: CLLocationCoordinate2D) async throws -> [MKMapItem] {
    let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
    let request = MKReverseGeocodingRequest(location: location)
    let mapItems = try await request.mapItems
    return mapItems
}

// Usage
let coordinate = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090)
let items = try await reverseGeocode(coordinate)
if let first = items.first {
    print("Address: \(first.placemark.title ?? "Unknown")")
}

統合例

住所をジオコーディングし、結果をサービス識別子付きの PlaceDescriptor に変換して、すべてのプロパティを読み取ります。

import GeoToolbox
import MapKit
import CoreLocation

func buildPlaceDescriptor(from addressString: String) async throws -> PlaceDescriptor? {
    // Forward geocode
    let request = MKGeocodingRequest(addressString: addressString)
    let mapItems = try await request.mapItems
    guard let mapItem = mapItems.first else { return nil }

    // Convert MKMapItem to PlaceDescriptor
    var descriptor = PlaceDescriptor(item: mapItem)

    // Or build manually with extra data
    let coordinate = mapItem.placemark.coordinate
    descriptor = PlaceDescriptor(
        representations: [
            .coordinate(coordinate),
            .address(addressString)
        ],
        commonName: mapItem.name,
        supportingRepresentations: [
            .serviceIdentifiers([
                "com.apple.maps": "resolved-id-\(coordinate.latitude)"
            ])
        ]
    )

    return descriptor
}

func displayDescriptor(_ descriptor: PlaceDescriptor) {
    if let name = descriptor.commonName {
        print("Name: \(name)")
    }
    if let coord = descriptor.coordinate {
        print("Coordinate: \(coord.latitude), \(coord.longitude)")
    }
    if let address = descriptor.address {
        print("Address: \(address)")
    }
    if let appleId = descriptor.serviceIdentifier(for: "com.apple.maps") {
        print("Apple Maps ID: \(appleId)")
    }
}

よくある間違い

#間違い問題対処法
1PlaceDescriptor を使う際に MapKit だけをインポートするPlaceDescriptor は MapKit ではなく GeoToolbox にありますimport MapKit と合わせて import GeoToolbox を追加します
2サービス識別子のキーをハードコードするサービス識別子のキーは文字列のため、タイプミスがあってもエラーが通知されずに失敗します"com.apple.maps" のようなサービスキーには定数を定義します
3ジオコーディングエラーを処理しないネットワークやサービスの障害によりエラーがスローされます適切なエラー処理とともに do/catch または try await を使用します

パターン

サービス識別子の定数

サービス識別子のキーのタイプミスを防ぐため、定数を定義します。

// Good -- constants prevent typos
enum PlaceService {
    static let appleMaps = "com.apple.maps"
    static let googleMaps = "com.google.maps"
    static let foursquare = "com.foursquare"
}

if let id = descriptor.serviceIdentifier(for: PlaceService.appleMaps) {
    // use id
}
// Bad -- raw string literals are error-prone
if let id = descriptor.serviceIdentifier(for: "com.apple.map") { // typo: "map" not "maps"
    // silently nil
}
nil に安全なプロパティアクセス
// Good -- check each optional property
func formatPlace(_ descriptor: PlaceDescriptor) -> String {
    var parts: [String] = []
    if let name = descriptor.commonName {
        parts.append(name)
    }
    if let address = descriptor.address {
        parts.append(address)
    }
    if let coord = descriptor.coordinate {
        parts.append("\(coord.latitude), \(coord.longitude)")
    }
    return parts.joined(separator: " -- ")
}
// Bad -- force-unwrapping optional properties
let name = descriptor.commonName! // crashes if nil
let coord = descriptor.coordinate! // crashes if no coordinate representation
フォールバック付きジオコーディング
// Good -- handle empty results and errors
func resolvePlace(_ address: String) async -> PlaceDescriptor? {
    do {
        let request = MKGeocodingRequest(addressString: address)
        let items = try await request.mapItems
        guard let item = items.first else {
            print("No results for address: \(address)")
            return nil
        }
        return PlaceDescriptor(item: item)
    } catch {
        print("Geocoding failed: \(error.localizedDescription)")
        return nil
    }
}
// Bad -- no error handling, no empty check
func resolvePlace(_ address: String) async -> PlaceDescriptor {
    let request = MKGeocodingRequest(addressString: address)
    let items = try! await request.mapItems // crashes on failure
    return PlaceDescriptor(item: items.first!) // crashes if empty
}

レビューチェックリスト

  • PlaceDescriptor、PlaceRepresentation、または SupportingPlaceRepresentation を使う場合、import GeoToolbox が含まれている
  • MKGeocodingRequest、MKReverseGeocodingRequest、または MKMapItem を使う場合、import MapKit が含まれている
  • CLLocationCoordinate2D または CLLocation を使う場合、import CoreLocation が含まれている
  • PlaceDescriptor のプロパティ(coordinate、address、commonName)を使用する前に nil かどうかを確認している
  • サービス識別子のキーには、生の文字列リテラルではなく定義済みの定数を使用している
  • MKReverseGeocodingRequest に生の CLLocationCoordinate2D ではなく CLLocation を渡している
  • ジオコーディング結果の要素にアクセスする前に、配列が空でないことを確認している
  • ジオコーディング呼び出しで、do/catch を使った適切な async/await エラー処理を行っている
  • 座標と住所の両方がわかっている場合、複数の表現を指定している
  • サービス間の相互運用が必要な場合、Supporting representation にサービス識別子を含めている

相互参照

  • MapKit のマップビューとアノテーションについては、MapKit のドキュメントを参照してください
  • CoreLocation の権限と位置情報の更新については、CoreLocation のドキュメントを参照してください
  • マップとの SwiftUI の統合については、SwiftUI の Map ビューを参照してください

参考資料

コメント