diff options
| author | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-10-24 12:29:24 +0900 |
|---|---|---|
| committer | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-10-24 12:29:24 +0900 |
| commit | a71727538f65ab4f10f5239550be82590f9d47d1 (patch) | |
| tree | 4608d37faead4f253c3311982a400a8fb88fba9e /src/components/ParksTable.astro | |
| parent | c7efe674b34a2949b76d62bb71a972427965af92 (diff) | |
parks: 公園一覧と地図、公共施設の情報を更新
- 近隣公園のクリックを市内公園と同じ挙動にし、Googleマップを公園名で開く
- 公共施設のトイレを○にし、ルネこだいらを追加
- 未取得だった施設の座標を追加し、再ジオコーディング時の上書きを防止
- 手入力ポリゴン、近隣公園、PWA、ライセンス表示を追加
Diffstat (limited to 'src/components/ParksTable.astro')
| -rw-r--r-- | src/components/ParksTable.astro | 114 |
1 files changed, 99 insertions, 15 deletions
diff --git a/src/components/ParksTable.astro b/src/components/ParksTable.astro index 6f8fdbd..bea8ff2 100644 --- a/src/components/ParksTable.astro +++ b/src/components/ParksTable.astro @@ -5,16 +5,74 @@ // 絞り込み・並べ替え・カード表示は利用者の端末で実行する。 import parksData from "@/data/kodaira-parks.json"; import osmData from "@/data/parks-geojson"; +import nearbyData from "@/data/kodaira-nearby-parks.json"; type Park = Record<string, string>; -const parks = parksData as Park[]; + +function nearbyCentroid(feature: any): [number, number] { + const g = feature.geometry; + const ring = g.type === "Polygon" ? g.coordinates[0] : g.coordinates[0][0]; + if (!ring?.length) return [0, 0]; + let lat = 0; + let lon = 0; + for (const [x, y] of ring) { + lon += x; + lat += y; + } + return [lat / ring.length, lon / ring.length]; +} + +// 市境500m以内の市外公園。小平市の公園と同じ表に「市外」バッジ付きで並べる。 +const nearbyRows: Park[] = ((nearbyData as any).features ?? []).map((f: any) => { + const p = f.properties || {}; + const [lat, lon] = nearbyCentroid(f); + const area = Math.round(Number(p.area_m2) || 0); + const name = String(p.name || "").trim(); + // 市外の公園も公園名で開く。市名が分かる場合は市名を付ける。 + const mapsQuery = name + ? p.city + ? `${p.city}${name}` + : name + : `${lat},${lon}`; + return { + "公園名": name, + "住所": p.city ? `${p.city}(市外)` : "市外", + "町名": "市外", + "面積㎡": area ? String(area) : "", + "500㎡以上": "", + "種別": "市外", + "南西部指定管理": "", + "トイレ": "", + "水飲み場": "", + "ベンチ・椅子": "", + "テーブル": "", + "パーゴラ": "", + "東屋": "", + "防災用具": "", + "ボール遊び": "", + "遊具": "", + "駐車場": "", + "その他設備": "", + "緯度": String(lat), + "経度": String(lon), + "備考": `市外・小平市境から約${Math.round(Number(p.distance_m) || 0)}m`, + "設置者": "市外", + "状態": "", + "地図キー": `${p.osm_type || ""}/${p.osm_id || ""}`, + "地図URL": `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(mapsQuery)}`, + }; +}); + +const parks = [...(parksData as Park[]), ...nearbyRows]; // OpenStreetMapにポリゴンがあり、地図から位置を表示できる公園名。 -const mappedNames = new Set<string>( - (osmData as any).features - .map((f: any) => f.properties?.our_name) +// 市外の近隣公園も地図に表示しているので、同じように地図で見られる。 +const mappedNames = new Set<string>([ + ...(osmData as any).features.map((f: any) => f.properties?.our_name).filter(Boolean), + ...((nearbyData as any).features ?? []) + .map((f: any) => String(f.properties?.name || "").trim()) .filter(Boolean), -); +]); const EMPTY = "—"; @@ -28,6 +86,7 @@ const TYPE_LABEL: Record<string, string> = { 緑地: "緑地", 団地内公園: "団地内公園", 児童遊園: "児童遊園", + 市外: "市外の近隣公園", }; const TYPE_CLASS: Record<string, string> = { 街区: "parks-type--block", @@ -38,6 +97,7 @@ const TYPE_CLASS: Record<string, string> = { 緑地: "parks-type--green", 団地内公園: "parks-type--danchi", 児童遊園: "parks-type--jidou", + 市外: "parks-type--green", }; // 市立以外の公園に付けるバッジ(設置者が空欄なら市立として扱う)。 @@ -143,16 +203,17 @@ function sizeTitle(area: number): string { return "目安: 10,000㎡以上(広め)"; } -// 座標があればその位置を開く。ない場合は公園名で検索する(地番は地図側で正しく解決されないため)。 -// 公園名だけだと別の自治体の同名公園に飛ぶので「小平市」を必ず付ける。 +// 公園名で検索する。公園名だけだと別の自治体の同名公園に飛ぶので「小平市」を必ず付ける。 +// 公園名がない場合だけ住所、それもなければ座標を使う。 function mapsUrl(p: Park): string { - const lat = p["緯度"]; - const lng = p["経度"]; - if (lat && lng) { - return `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`; - } - const query = encodeURIComponent(`小平市${p["公園名"]}`); - return `https://www.google.com/maps/search/?api=1&query=${query}`; + if (p["地図URL"]) return p["地図URL"]; + const name = (p["公園名"] || "").trim(); + const query = name + ? `小平市${name}` + : p["住所"] + ? `小平市${p["住所"]}` + : `${p["緯度"]},${p["経度"]}`; + return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(query)}`; } // 検索対象(公園名・住所・町名・種別・設備・備考)。 @@ -164,6 +225,7 @@ function searchIndex(p: Park): string { p["種別"], TYPE_LABEL[p["種別"]], p["設置者"], + p["状態"], p["トイレ"], p["水飲み場"], p["ボール遊び"], @@ -243,11 +305,15 @@ const total = parks.length; type="button" class="park-name-link" data-focus-park={park["公園名"]} + data-focus-key={park["地図キー"] || ""} title="地図でこの公園の位置を表示" >{park["公園名"]}</button> ) : ( park["公園名"] )} + {park["状態"] === "閉鎖" ? ( + <span class="parks-status" title="閉鎖中">閉鎖</span> + ) : null} {park["設置者"] && park["設置者"] !== "小平市" ? ( <span class:list={["parks-owner", ownerClass(park["設置者"])]} @@ -440,6 +506,20 @@ const total = parks.length; .parks-owner--unknown { color: #757575; } + .parks-status { + display: inline-block; + margin-left: 0.35rem; + padding: 0.02rem 0.35rem; + border: 1px solid #ef9a9a; + border-radius: 999px; + background: #ffebee; + color: #b71c1c; + font-size: 0.7rem; + font-weight: 700; + line-height: 1.5; + vertical-align: 0.08em; + white-space: nowrap; + } .park-name-link { padding: 0; border: 0; @@ -607,7 +687,11 @@ const total = parks.length; btn.addEventListener("click", () => { const name = btn.dataset.focusPark; if (!name) return; - document.dispatchEvent(new CustomEvent("park:focus", { detail: { name } })); + document.dispatchEvent( + new CustomEvent("park:focus", { + detail: { name, key: btn.dataset.focusKey || "" }, + }), + ); }); }); |
