履歴資料。現行仕様ではない。 Issue #60 Stage 1〜2 で実装した直線/曲線 A-B 取得・候補経路生成
(CandidateRouteGenerator/ReferenceRoute)・A-B 用 WorkDock 操作・GuidancePlanState状態機械・
GuidanceAvailability・pc-tool guidance-planは、Issue #107 Pattern 1 実装前に撤去した(Issue #112 / Track #113)。現行の耕うん計画は P6 UI仕様 の「基本耕耘計画・圃場全体ナビゲーション」節と
隣接耕基本経路パターン1 を正本とする。
本ファイルは当時の仕様・判断(Track #62 / Track #63)を後から追えるように残したもので、内容は更新しない。
Issue #60(P6ナビゲーション全体実装)のStage 1として、Stage 0(Track #61)で確定した作業ピッチ契約を使い、筆polygonと基準経路(直線/曲線A-B)から候補経路を生成する幾何ドメインをtrack-coreへ追加し、pc-toolのCLIから実筆データで反復検証できるようにした。タブレットUI・A/B取得操作・走行順ジェネレータ・案内状態機械・周回経路はこのStageの対象外(Stage 2以降)。
org.locationtech.jts:jts-coreをtrack-coreへ導入した(implementation。Android非依存のJVMライブラリで、:appのビルドへ影響しない)。track-coreのdev.drogger.tablet.navパッケージ): Field(筆、polygonUuid+FieldPolygonのリスト。MultiPolygon・穴に対応)、ReferenceRoute(Straight(a, b)/Curve(points))、CandidateRoute(routeId・relativeIndex・beforeClipParts・segments)、RouteSide(候補経路の生成側)、NavGeometryWarning(短区間/空結果/自己交差/急曲率/無効polygon)。CandidateRouteGenerator: WGS84を局所メートル座標へ投影(LocalProjection、原点は筆の代表点)し、筆polygonをJTS Geometryへ正規化(FieldGeometryOps、自己交差等はbuffer(0)で修復を試み、それでも無効ならInvalidPolygon警告として扱い例外を投げない)した上で、基準経路をrelativeIndex × 作業ピッチだけoffsetし筆でclipする。
OffsetCurve.getCurve(LineString, distance)を採用した(利用者確認済み方針)。fixture testで、直線側の符号規約(進行方向基準で正=左側)と一致することを実測確認済み(Track #62のtrack_record参照)。折返し・急曲率のfixtureでも例外を投げずSharpCurvature/ReferenceSelfIntersection警告として検出する。intersection)が複数区間に分かれる場合、区間同士を圃場外で自動接続しない(segmentsはそのまま複数要素になる)。NavGeometrySettings)。Stage 5/6で実データを見ながら確定する前提(P6-11のCOG/旋回閾値が辿った経緯と同様、未確定のまま実装だけ先行させている)。pc-tool guidance-plan): 既存の耕耘あと表示用途(Track #54)とは明示的に分離したサブコマンド。筆は--field-geojson(tools/map/static/fude-overlay.geojson等、--polygon-uuidで1件を選択)またはJSON、A-Bは--a-lat等のCLIフラグまたは--configのJSON(曲線A-Bは--configのみ対応)で指定する。--save-configで解決済みの入力条件をJSONへ保存でき、同じ内容を--configで再実行すると常に同一のGeoJSONが得られる(決定的、判断条件(8))。出力GeoJSONは対象筆・基準経路・候補経路のclip前(candidateRouteBeforeClip)/clip後(candidateRouteSegment)・警告(warnings)を含み、tools/map・QGISで確認できる。tools/map/extract_nav_fixtures.pyがtools/map/static/fude-overlay.geojson(211件)から形状の複雑さ・穴の有無で代表4件を選び、track-core/src/test/resources/nav-fixtures/real-fields.geojsonへ抽出する(元データ・選定理由・抽出方法を同ファイルのdroggerNavFixtureMetaに記録、再作成可能)。211件全件へテストを直接依存させないための構成。guidance-planの実行例PC側 耕耘あと表示検証ツールのpc-toolと同じく、installDistでビルドした実行ファイルを直接呼ぶ(apps/android-tabletが作業ディレクトリ)。./gradlew :pc-tool:run --args="..."は:pc-toolモジュール配下が作業ディレクトリになる(相対パスの起点がずれる)ため、こちらでは使わない。
apps/android-tablet$ ./gradlew :pc-tool:installDist
apps/android-tablet$ ./pc-tool/build/install/pc-tool/bin/pc-tool guidance-plan \
--field-geojson=../../tools/map/static/fude-overlay.geojson \
--polygon-uuid=e9eb8f2e-1673-403c-b3f0-9d399a2c04d1 \
--a-lat=33.2106099 --a-lon=133.10263325 \
--b-lat=33.2105813 --b-lon=133.1027208 \
--implement-width-cm=200 --overlap-width-cm=60 \
--side=both --count-per-side=2 \
--save-config=/tmp/guidance-plan-input.json \
--out=/tmp/guidance-plan-output.geojson
--save-configで書き出した/tmp/guidance-plan-input.jsonは、そのまま--config=/tmp/guidance-plan-input.json --out=<別の出力先>で再実行でき、同じ入力からは常に同一のGeoJSONが得られる(決定性の確認方法)。出力GeoJSONはtools/map(server.pyを経由せずファイルを直接ブラウザ等で開く、またはQGISで開く)で確認できる。
track-core(人工fixture12件+実筆fixture5件、矩形/凹形/斜行/曲線/MultiPolygon/穴/境界接触/分割/自己交差/無効polygon/決定性を検証)・pc-tool(JSON入出力往復・GeoJSON出力構造・筆GeoJSON読み込みの8件)、全件成功。Issue #60(P6ナビゲーション全体実装)のStage 2として、Stage 1の幾何domainへ渡せるナビゲーション計画domain(track-core)、直線/曲線A-Bの現地取得、A/Bの取消・やり直し・取り直し、GuidanceAvailability、WorkDockの段階別主操作、A/Bマーカーと基準経路previewを実装した。筆選択、候補経路の筆clip preview、横ずれ案内、予定/現在/完了表示、走行順、周回経路はStage 3以降の対象外。
track-core、dev.drogger.tablet.nav.GuidancePlanState/GuidancePlanEvent/GuidancePlanReducer): Idle→ModeSelect→AwaitingA→CapturingB→ReferencePreview→Confirmedを表す単一のsealed class(複数Booleanの組合せでは表現しない)。純粋関数のreducerで、GNSS購読・時刻取得等の副作用は呼び出し側(:app)に持たせる。取消(Cancel、状態によらず常にIdleへ)、A設定後のやり直し(RedoA、CapturingB→AwaitingA)、B設定後のA-B取り直し(RecaptureAb、ReferencePreview/Confirmed→AwaitingA)に対応する。通常の.dtrk軌跡記録の開始/停止状態(isRecordingTrajectory)とは完全に独立しており、記録中でもA-B取得を開始できる(利用者確認済み、Track #63)。WorkDockの主操作スロットは1つしかないため、ナビ計画が進行中の間だけ一時的にナビ操作へ切り替わり、記録自体はバックグラウンドで継続する(両者は同時に画面へ出さない設計だが機能的には非干渉)。GuidancePlanSettings.minAbDistanceM(既定10.0m)未満だと確定せず、状態は変えずに理由(現在の距離/必要距離)を返す。.dtrk記録とは別の点列で、保存ファイルには影響しない)。B設定で停止しCurve(points)を確定する。最低点数minCurvePoints(既定3点)未満、または収集点列の総延長がminAbDistanceM未満だと確定しない(点数不足を距離不足より先に判定する)。連続する点がduplicatePointToleranceM(既定0.5m)未満しか離れていない場合は追加しない(GNSS jitterやB位置が直前のcapture点と同一になるケースで、ゼロ長に近いsegmentが混入したり重複点だけで最低点数を満たしてしまう問題を防ぐ)。最低点数判定は重複除去後の実点数で行う。NavGeometrySettings.minSegmentLengthM(1.0m、Stage 1)とは別の閾値。A-B間の方位はGNSS測位誤差の影響を直接受けるため、単独/DGNSS等の低精度測位時でも方位のぶれを実用上抑えられる値として、利用者レビューで5.0mから10.0mへ引き上げた。実データが無いまま決めた暫定値であり、Stage 1の閾値と同じくStage 5/6で実データを見ながら調整する前提。track-core、ProfileSnapshot): profileId/profileName/作業機幅/重ね幅/作業ピッチ/前後・左右offsetのコピー。:appのProfile(android.content依存)は依存方向(:app→:track-core)によりtrack-coreから直接参照できないため、値だけをコピーして持つ(Stage 0のWorkPitch.ktと同じ設計)。B設定時点のProfile値をReferencePreview/Confirmedへ焼き込み、計画作成後にProfileの実測値が変わっても既存計画のオフセット・作業ピッチは変えない。:app側のProfile.toNavProfileSnapshot()(ProfileSnapshotMapping.kt)が変換を行い、重ね幅未設定またはisValidOverlapWidthを満たさない場合はnull(ナビゲーション計画開始不可)を返す。:app、dev.drogger.tablet.nav.GuidanceAvailability): Available/Suspended(reason)。理由はBluetoothDisconnected(BluetoothLinkState.Connected以外すべて)・PositionUnavailable(無効Fix)・PositionStale(stale)・PositionQualityInsufficient(製品として区分を割り当てていない測位品質)・ProfileWorkPitchInvalid(作業機幅/重ね幅が未実測・無効)。単独測位・DGNSS自体はSuspendedにしない(A-B専用の最低距離10.0mをこの精度帯でも安全な値として設定済みのため)。A/B設定時にSuspendedなら座標を採用せず、状態と入力済みデータ(捕捉済みのA点・曲線点列)を保持したまま、WorkDockの警告欄に復旧理由を表示する。最後の良好位置を現在位置として流用しない(FixState.currentLatLonOrNull()がlat/lon欠落時にnullを返す防御的チェックを含む)。decideWorkDockPrimaryAction(WorkDock.kt)がナビ計画状態を記録の開始/停止判定より優先する。ModeSelectは方式選択シート(直線/曲線)を開き、AwaitingA/CapturingBはタップで即座に現在位置をA/Bとして設定し(確認シートを挟まない捕捉操作)、ReferencePreviewは「このA-Bを確認」で確定する。Confirmed状態はStage 2の対象外(案内表示はStage 3)のため記録操作へフォールバックする。A点/B点/解除は常時横並びにせず、取消・やり直し・取り直しは既存のWorkMenuへ置く(ModeSelect/AwaitingAは即取消、CapturingB/ReferencePreviewは確認ダイアログ付きの取消、CapturingBは「Aをやり直す」、ReferencePreview/Confirmedは「A-Bを取り直す」)。ナビ計画開始の入口「A-Bを設定」もWorkMenuに置く(地図上の常設ボタンと上部ステータスの4列目は追加しない)。OfflineMapStyle.kt): A/Bマーカーは円+ラベル文字("A"/"B")で色だけでなく文言でも区別する(NAV_AB_MARKER_COLOR)。曲線capture中の点列は破線、確定/preview中の基準経路は実線(NAV_REFERENCE_ROUTE_COLOR)で、いずれも既存の現在地(青)・軌跡(ピンク)・耕耘あと(黄)・筆輪郭(橙)と異なる色を割り当てた。GuidancePlanState→表示要素の変換はNavMapPresentation.kt(:app、純粋関数)が担い、候補経路・横ずれ・予定/現在/完了レイヤーはStage 3以降のため含めない。GuidancePlanStateはCompose のremember(rememberSaveableではない、メモリ上のみ)で保持する。アプリ再起動(プロセス再作成)後は常にIdleへ戻り、未確認・確認済み計画とも自動再開しない。計画の永続化と明示的な再開はStage 3の責務として分離する(利用者レビューで訂正、Track #63。当初「永続化するが自動遷移はしない」と記録していたが、Stage 2/3の境界と矛盾するため訂正した)。viewModel.logUserAction("nav", ...)でDEBUGレベル記録する(既存のonLogUserAction方針と同じ)。track-core(GuidancePlanReducerTest16件、ProfileSnapshotTest5件)・:app(GuidanceAvailabilityTest・ProfileSnapshotMappingTest・NavRejectionMessageTest・NavMapPresentationTest・WorkDockTest拡張分)。testDebugUnitTest(全モジュール484件)・assembleReleaseとも成功。実機(Redmi Pad SE 8.7)でのA→B取得・取消・取り直し・測位利用不可時の表示・通常軌跡記録の継続は本Track時点では未確認(このセッションでは実施していない)。