Skip to content

Commit 1222d8a

Browse files
rtcode337claude
andauthored
訪問予定リストの経路表示で別種別スポットも座標解決する (#46)
地図の絞り込み「訪問予定リスト」経路表示は buildPlanListPath が本体種別の spotById だけで解決していたため、別スポット種別を重ねて追加したスポットが 経路線から抜けていた。選択中リストの未解決スポットを api.spots.get で補完する planPathSpotById を用意し、経路の生成・経由地の表示免除・地図フィットに使う。 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent bf962b1 commit 1222d8a

1 file changed

Lines changed: 55 additions & 5 deletions

File tree

components/MapView.tsx

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,12 @@ export default function MapView({
911911
const [visits, setVisits] = useState<Visit[]>([]);
912912
// 訪問予定リスト(絞り込みモーダルの「訪問予定リスト」セレクトで経路表示に使う)
913913
const [planLists, setPlanLists] = useState<VisitPlanList[]>([]);
914+
// 経路表示するリストに、本体種別に無い(別スポット種別を重ねて追加した)スポットが
915+
// あるとき、その座標を api.spots.get で補完して経路に含める。resolvedRefで再取得を防ぐ
916+
const [planListExtraSpots, setPlanListExtraSpots] = useState<Map<string, Spot>>(
917+
new Map()
918+
);
919+
const planListResolvedRef = useRef<Set<string>>(new Set());
914920
const visitedIds = useMemo(
915921
() => new Set(visits.map((v) => v.spot_id)),
916922
[visits]
@@ -920,6 +926,12 @@ export default function MapView({
920926
for (const s of spots) m.set(s.id, s);
921927
return m;
922928
}, [spots]);
929+
// 訪問予定リストの経路を組むときのスポット解決用。本体スポットに、別種別スポットの
930+
// 補完(planListExtraSpots)を足す。補完が無いときは spotById をそのまま使う(参照維持)
931+
const planPathSpotById = useMemo(() => {
932+
if (planListExtraSpots.size === 0) return spotById;
933+
return new Map([...spotById, ...planListExtraSpots]);
934+
}, [spotById, planListExtraSpots]);
923935
/**
924936
* 訪問順の経路の対象日を選ぶドロップダウン用に、この種別のスポットへ訪問した
925937
* 日の一覧(新しい順)。他の種別の訪問しかない日は経路が0件になるため除く。
@@ -1003,10 +1015,38 @@ export default function MapView({
10031015
const planListId = value || null;
10041016
setFilters({ ...filters, planListId });
10051017
if (!planListId) return;
1006-
fitMapToSpots(buildPlanListPath(planLists, { ...filters, planListId }, spotById));
1018+
fitMapToSpots(
1019+
buildPlanListPath(planLists, { ...filters, planListId }, planPathSpotById)
1020+
);
10071021
},
1008-
[filters, setFilters, planLists, spotById, fitMapToSpots]
1022+
[filters, setFilters, planLists, planPathSpotById, fitMapToSpots]
10091023
);
1024+
1025+
// 経路表示中のリストに本体種別で解決できないスポット(別種別を重ねて追加したもの)が
1026+
// あれば、api.spots.get で座標を補完する(経路線から抜けないように)
1027+
useEffect(() => {
1028+
const listId = filters.planListId;
1029+
if (!listId) return;
1030+
const list = planLists.find((l) => l.id === listId);
1031+
if (!list) return;
1032+
const missing = list.spot_ids.filter(
1033+
(id) => !spotById.has(id) && !planListResolvedRef.current.has(id)
1034+
);
1035+
if (missing.length === 0) return;
1036+
missing.forEach((id) => planListResolvedRef.current.add(id));
1037+
let cancelled = false;
1038+
Promise.all(missing.map((id) => api.spots.get(id))).then((results) => {
1039+
if (cancelled) return;
1040+
setPlanListExtraSpots((prev) => {
1041+
const next = new Map(prev);
1042+
for (const { data } of results) if (data) next.set(data.id, data);
1043+
return next;
1044+
});
1045+
});
1046+
return () => {
1047+
cancelled = true;
1048+
};
1049+
}, [filters.planListId, planLists, spotById]);
10101050
// マウント時と、マウント中に種別が切り替わった場合に、その種別の保存済み条件を読む
10111051
useEffect(() => {
10121052
setFiltersState(loadSavedFilters(spotTypeKey));
@@ -1779,7 +1819,7 @@ export default function MapView({
17791819
// 絞り込みで外れていても必ず表示する(経路を辿るための表示のため全条件を免除)
17801820
const pathIds = new Set([
17811821
...buildVisitPath(visits, filters, spotById).map((s) => s.id),
1782-
...buildPlanListPath(planLists, filters, spotById).map((s) => s.id),
1822+
...buildPlanListPath(planLists, filters, planPathSpotById).map((s) => s.id),
17831823
]);
17841824
const filteredSpots = spots.filter(
17851825
(spot) =>
@@ -1832,6 +1872,7 @@ export default function MapView({
18321872
}, [
18331873
spots,
18341874
spotById,
1875+
planPathSpotById,
18351876
visits,
18361877
planLists,
18371878
visitedIds,
@@ -1850,7 +1891,7 @@ export default function MapView({
18501891

18511892
const visibleRoutes = filterVisibleRoutes(routes, filters, seriesStyles, spotById);
18521893
const visitPath = buildVisitPath(visits, filters, spotById);
1853-
const planListPath = buildPlanListPath(planLists, filters, spotById);
1894+
const planListPath = buildPlanListPath(planLists, filters, planPathSpotById);
18541895

18551896
runWhenMapReady(() => {
18561897
ensureRouteLayers(map, setDetailRouteId);
@@ -1864,7 +1905,16 @@ export default function MapView({
18641905
])
18651906
);
18661907
});
1867-
}, [routes, filters, seriesStyles, runWhenMapReady, visits, planLists, spotById]);
1908+
}, [
1909+
routes,
1910+
filters,
1911+
seriesStyles,
1912+
runWhenMapReady,
1913+
visits,
1914+
planLists,
1915+
spotById,
1916+
planPathSpotById,
1917+
]);
18681918

18691919
// 別種別の重ね表示の描画。絞り込み・経由地ピンの免除は本体と同じロジックを、
18701920
// その種別の保存済み設定・シリーズ設定で適用する(訪問順の経路(緑)は

0 commit comments

Comments
 (0)