From 17f6009bfc6589d43e10233040b125675d24c5c8 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:44:19 +0900 Subject: parks: 複数ポリゴンとOSMポリゴンの置き換えに対応 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同じ公園名の複数ポリゴンをまとめてズーム・アイコン表示する。import-manual-parks.py に --override-name を追加し、手入力ポリゴンでOSMを置き換えられるようにした。草地広場・多目的広場を一覧の検索・ピクニック絞り込みに反映。 --- src/components/ParkMap.astro | 41 +++++++++++++++++++++++++++++++++++++---- src/components/ParksTable.astro | 8 +++++++- src/data/parks-geojson.ts | 19 ++++++++++++++++++- 3 files changed, 62 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/components/ParkMap.astro b/src/components/ParkMap.astro index 7ca85ab..ac85c0e 100644 --- a/src/components/ParkMap.astro +++ b/src/components/ParkMap.astro @@ -629,7 +629,21 @@ lyr._parkName = ourName; lyr._osmName = name; allLayers.push(lyr); - if (ourName) layersByName.set(ourName, lyr); + if (ourName) { + const existing = layersByName.get(ourName); + if (!existing) { + layersByName.set(ourName, lyr); + } else if ( + typeof existing.getLayers === "function" && + typeof existing.addLayer === "function" + ) { + existing.addLayer(lyr); + } else { + const group = L.featureGroup([existing, lyr]); + group._osmName = existing._osmName; + layersByName.set(ourName, group); + } + } if (ourName) { lyr.on("add", () => { @@ -760,6 +774,25 @@ addFacilities(nearbyFacilities as any[], true); publicFacilityLayer.addTo(map); + // 道路で分かれた公園など、同じ公園に複数のポリゴンがある場合は + // いちばん大きいポリゴンの中心にアイコンを置く。 + const centerOfLayer = (lyr: any) => { + if (lyr && typeof lyr.getLayers === "function") { + let best: any = null; + let bestArea = -1; + for (const child of lyr.getLayers()) { + const b = child.getBounds(); + const area = (b.getNorth() - b.getSouth()) * (b.getEast() - b.getWest()); + if (area > bestArea) { + bestArea = area; + best = child; + } + } + if (best) return best.getBounds().getCenter(); + } + return lyr.getBounds().getCenter(); + }; + // 拡大時だけ表示する設備アイコン(トイレ・水飲み場) // 一覧と一致した公園のポリゴン内にだけ表示する。 const facilityLayer = L.layerGroup(); @@ -778,7 +811,7 @@ ); } const clock = (p["時計"] || "").trim(); - if (clock === "○" || clock === "あり" || clock === "有") { + if (clock === "○" || clock === "あり" || clock === "有" || clock === "1") { icons.push( '⏰', ); @@ -795,7 +828,7 @@ ); } if (!icons.length) return; - const center = lyr.getBounds().getCenter(); + const center = centerOfLayer(lyr); L.marker([center.lat, center.lng], { icon: L.divIcon({ className: "park-fac-markers", @@ -854,7 +887,7 @@ ); } if (!icons.length) return; - const center = lyr.getBounds().getCenter(); + const center = centerOfLayer(lyr); L.marker([center.lat, center.lng], { icon: L.divIcon({ className: "park-play-markers", diff --git a/src/components/ParksTable.astro b/src/components/ParksTable.astro index f0864d6..17841cc 100644 --- a/src/components/ParksTable.astro +++ b/src/components/ParksTable.astro @@ -123,13 +123,15 @@ function facTokens(p: Park): string { const ball = p["ボール遊び"]; if (ball && ball !== "禁止") tokens.push("ball"); const clock = (p["時計"] || "").trim(); - if (clock === "○" || clock === "あり" || clock === "有") tokens.push("clock"); + if (clock === "○" || clock === "あり" || clock === "有" || clock === "1") tokens.push("clock"); if (p["南西部指定管理"]) tokens.push("cm"); // ピクニック広場・芝生広場・テーブルのある公園。 const picnicText = `${p["公園名"] || ""} ${p["種別"] || ""} ${p["その他設備"] || ""} ${p["備考"] || ""}`; if ( (p["テーブル"] || "").trim() || p["種別"] === "広場" || + p["草地広場"] || + p["多目的広場"] || picnicText.includes("芝生") || picnicText.includes("ピクニック") || picnicText.includes("広場") @@ -145,6 +147,8 @@ const DETAIL_FIELDS: { key: string; icon: string }[] = [ { key: "ベンチ・椅子", icon: "🪑" }, { key: "テーブル", icon: "🍽" }, { key: "シェルター等", icon: "⛱" }, + { key: "草地広場", icon: "🌿" }, + { key: "多目的広場", icon: "🏟" }, { key: "防災用具", icon: "🧯" }, { key: "遊具", icon: "🎠" }, { key: "時計", icon: "⏰" }, @@ -211,6 +215,8 @@ function searchIndex(p: Park): string { p["トイレ"], p["水飲み場"], p["給水"], + p["草地広場"] ? "草地広場" : "", + p["多目的広場"] ? "多目的広場" : "", p["ボール遊び"], ...DETAIL_FIELDS.map((f) => p[f.key]), ] diff --git a/src/data/parks-geojson.ts b/src/data/parks-geojson.ts index e12d297..9fd9d4f 100644 --- a/src/data/parks-geojson.ts +++ b/src/data/parks-geojson.ts @@ -20,6 +20,23 @@ function isHiddenUnlisted(props: any): boolean { const osmFeatures = ((osm as any).features ?? []).filter( (f: any) => !isHiddenUnlisted(f.properties), ); -const features = [...osmFeatures, ...((manual as any).features ?? [])]; +const manualFeatures = (manual as any).features ?? []; + +// 手入力ポリゴンでOSM側の形を置き換える公園(例: 道路で分かれた公園)。 +// QGISの属性 override=true、または import-manual-parks.py の --override-name で付く。 +const overrideNames = new Set( + manualFeatures + .filter((f: any) => { + const value = f?.properties?.override; + return value === true || value === 1 || value === "true" || value === "1"; + }) + .map((f: any) => f?.properties?.our_name) + .filter(Boolean), +); + +const features = [ + ...osmFeatures.filter((f: any) => !overrideNames.has(f.properties?.our_name)), + ...manualFeatures, +]; export default { type: "FeatureCollection", features }; -- cgit v1.3.1