aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/components/ParksTable.astro
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/ParksTable.astro')
-rw-r--r--src/components/ParksTable.astro114
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 || "" },
+ }),
+ );
});
});