From a01062f43d36112c00d5c007ac9d488c14d8fb0d Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:24:24 +0900 Subject: parks: アイコン・フィルタ・編集画面を更新し、公開を一時停止 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 遊具アイコンをSVGRepo素材に差し替え、施設種別をアイコン表示 - フィルタに種類・遊具・ピクニックを追加し、適用時の地図移動を停止 - 現在地ボタンを追跡トグルに変更し、初回以外は地図を動かさない - 編集画面を市内公園・周辺公園・公共施設の4タブに拡張 - 公園ページをドラフト化し、サイドバーとPWAの公開を停止 --- scripts/park-editor.mjs | 106 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 16 deletions(-) (limited to 'scripts/park-editor.mjs') diff --git a/scripts/park-editor.mjs b/scripts/park-editor.mjs index 7a6c2fa..a872008 100644 --- a/scripts/park-editor.mjs +++ b/scripts/park-editor.mjs @@ -1,9 +1,10 @@ -// 公園データの簡易エディタ(ローカル専用)。 +// 公園・公共施設データの簡易エディタ(ローカル専用)。 // // 使い方: -// node scripts/park-editor.mjs +// npm run park-editor // → 表示された URL(既定 http://localhost:8787/)をブラウザで開く -// → 表のセルを直接編集し、「保存」を押すと src/data/kodaira-parks.json を更新 +// → タブで「市内公園 / 周辺の公園 / 市内の公共施設 / 周辺の公共施設」を切り替え +// → 表のセルを直接編集し、「保存」を押すと該当するJSONを更新 // → 反映確認後、git にコミットする // // 注意: ローカル(127.0.0.1)にのみ公開します。追加パッケージは不要です。 @@ -13,36 +14,109 @@ import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; const root = join(dirname(fileURLToPath(import.meta.url)), ".."); -const DATA = join(root, "src/data/kodaira-parks.json"); const HTML = join(root, "scripts/park-editor.html"); const PORT = Number(process.env.PORT || 8787); +const DATASETS = { + parks: { + label: "市内公園", + path: join(root, "src/data/kodaira-parks.json"), + pretty: false, + }, + nearbyParks: { + label: "周辺の公園", + path: join(root, "src/data/kodaira-nearby-parks.json"), + pretty: false, + }, + facilities: { + label: "市内の公共施設", + path: join(root, "src/data/kodaira-facilities.json"), + pretty: true, + }, + nearbyFacilities: { + label: "周辺の公共施設", + path: join(root, "src/data/kodaira-nearby-facilities.json"), + pretty: true, + }, +}; + function send(res, code, type, body) { res.writeHead(code, { "content-type": type }); res.end(body); } +function datasetFromUrl(url) { + const match = /^\/api\/data\/([A-Za-z]+)$/.exec(url); + if (!match) return null; + return DATASETS[match[1]] ? { id: match[1], dataset: DATASETS[match[1]] } : null; +} + const server = createServer(async (req, res) => { try { const url = (req.url || "").split("?")[0]; if (req.method === "GET" && (url === "/" || url === "/index.html")) { send(res, 200, "text/html; charset=utf-8", await readFile(HTML, "utf-8")); - } else if (req.method === "GET" && url === "/api/parks") { - send(res, 200, "application/json; charset=utf-8", await readFile(DATA, "utf-8")); - } else if (req.method === "POST" && url === "/api/parks") { + return; + } + + if (req.method === "GET" && url === "/api/datasets") { + send( + res, + 200, + "application/json; charset=utf-8", + JSON.stringify( + Object.entries(DATASETS).map(([id, dataset]) => ({ id, label: dataset.label })), + ), + ); + return; + } + + const target = datasetFromUrl(url); + if (!target) { + send(res, 404, "text/plain; charset=utf-8", "not found"); + return; + } + + if (req.method === "GET") { + send( + res, + 200, + "application/json; charset=utf-8", + await readFile(target.dataset.path, "utf-8"), + ); + return; + } + + if (req.method === "POST") { let body = ""; for await (const chunk of req) body += chunk; - const parks = JSON.parse(body); - if (!Array.isArray(parks) || parks.length === 0) { + const data = JSON.parse(body); + + if (target.id === "nearbyParks") { + if (!data || data.type !== "FeatureCollection" || !Array.isArray(data.features)) { + throw new Error("周辺の公園は FeatureCollection ではありません"); + } + } else if (!Array.isArray(data) || data.length === 0) { throw new Error("データが配列ではありません"); } - // 元ファイルと同じコンパクトな1行JSONで保存する(日本語はそのまま)。 - await writeFile(DATA, JSON.stringify(parks), "utf-8"); - console.log(`保存しました: ${parks.length} 件 → ${DATA}`); - send(res, 200, "application/json; charset=utf-8", JSON.stringify({ ok: true, count: parks.length })); - } else { - send(res, 404, "text/plain; charset=utf-8", "not found"); + + const text = target.dataset.pretty + ? `${JSON.stringify(data, null, 2)}\n` + : `${JSON.stringify(data)}\n`; + await writeFile(target.dataset.path, text, "utf-8"); + + const count = target.id === "nearbyParks" ? data.features.length : data.length; + console.log(`保存しました: ${target.dataset.label} ${count}件 → ${target.dataset.path}`); + send( + res, + 200, + "application/json; charset=utf-8", + JSON.stringify({ ok: true, count }), + ); + return; } + + send(res, 405, "text/plain; charset=utf-8", "method not allowed"); } catch (e) { console.error(e); send(res, 500, "text/plain; charset=utf-8", String(e)); @@ -50,5 +124,5 @@ const server = createServer(async (req, res) => { }); server.listen(PORT, "127.0.0.1", () => { - console.log(`公園データ編集: http://localhost:${PORT}/ (Ctrl+C で終了)`); + console.log(`公園・施設データ編集: http://localhost:${PORT}/ (Ctrl+C で終了)`); }); -- cgit v1.3.1