{"id":7753,"date":"2021-12-11T10:18:41","date_gmt":"2021-12-11T01:18:41","guid":{"rendered":"https:\/\/gunmagisgeek.com\/blog\/?p=7753"},"modified":"2021-12-11T19:34:24","modified_gmt":"2021-12-11T10:34:24","slug":"deck-gl%e3%81%a7%e5%85%ac%e5%85%b1%e4%ba%a4%e9%80%9a%e3%83%87%e3%83%bc%e3%82%bfgtfs-%e3%83%87%e3%83%bc%e3%82%bf%e3%82%92%e5%8f%af%e8%a6%96%e5%8c%96%e3%81%99%e3%82%8b","status":"publish","type":"post","link":"https:\/\/gunmagisgeek.com\/blog\/deck-gl\/7753","title":{"rendered":"deck.gl\u3067\u516c\u5171\u4ea4\u901a\u30c7\u30fc\u30bf(GTFS) \u30c7\u30fc\u30bf\u3092\u53ef\u8996\u5316\u3059\u308b"},"content":{"rendered":"\n<p>\u3053\u306e\u8a18\u4e8b\u306f<a href=\"https:\/\/qiita.com\/advent-calendar\/2021\/deckgl\">deck.gl Advent Calendar 2021&nbsp;<\/a>\u53c2\u52a0\u8a18\u4e8b\u3067\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u6982\u8981<\/h2>\n\n\n\n<p><a href=\"https:\/\/www.gtfs.jp\/\" data-type=\"URL\" data-id=\"https:\/\/www.gtfs.jp\/\">GTFS<\/a>\uff08General Transit Feed Specification\uff09\u306f\u3001\u516c\u5171\u4ea4\u901a\u306b\u95a2\u3059\u308b\u30c7\u30fc\u30bf\u3092\u6271\u3046\u305f\u3081\u306e\u4e16\u754c\u6a19\u6e96\u30c7\u30fc\u30bf\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3059\u3002\u591a\u304f\u306e\u5730\u57df\u3067\u4ea4\u901a\u30c7\u30fc\u30bf\u3092\u516c\u958b\u3059\u308b\u969b\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3068\u3057\u3066\u63a1\u7528\u3055\u308c\u3066\u304a\u308a\u3001\u65e5\u672c\u3067\u3082\u5f90\u3005\u306b\u666e\u53ca\u304c\u9032\u3093\u3067\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u305d\u3053\u3067\u3001\u3053\u306e\u8a18\u4e8b\u3067\u306fdeck.gl\u3092\u4f7f\u3063\u3066\u3001GTFS\u306b\u542b\u307e\u308c\u308b\u7d4c\u8def\u60c5\u5831\u3068\u505c\u7559\u6240\u60c5\u5831\u3092\u5730\u56f3\u306b\u8868\u793a\u3059\u308b\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u30b5\u30f3\u30d7\u30eb\u30b3\u30fc\u30c9<\/h2>\n\n\n\n<iframe src=\"https:\/\/codesandbox.io\/embed\/deck-gl-gtfs-detanodu-miip-mi-och8l?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;view=split\" style=\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\" title=\"deck.gl - GTFS \u30c7\u30fc\u30bf\u306e\u8aad\u307f\u8fbc\u307f\" allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\" sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"><\/iframe>\n\n\n\n<p>\u300c<a href=\"https:\/\/www.rosenzu.com\/~gtfs\/aoikotsu\/index.html\" data-type=\"URL\" data-id=\"https:\/\/www.rosenzu.com\/~gtfs\/aoikotsu\/index.html\">\u3042\u304a\u3044\u4ea4\u901a\u3000\u30aa\u30fc\u30d7\u30f3\u30c7\u30fc\u30bf<\/a>\u300d\u3088\u308a\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3055\u305b\u3066\u3044\u305f\u3060\u3044\u305faoikotsu_GTFS_20211031.zip\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u3001\u7d4c\u8def\u30c7\u30fc\u30bf\u3068\u505c\u7559\u6240\u30c7\u30fc\u30bf\u3092\u5730\u56f3\u306b\u63b2\u8f09\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u7d4c\u8def\u30c7\u30fc\u30bf\u306f<a href=\"https:\/\/loaders.gl\/\" data-type=\"URL\" data-id=\"https:\/\/loaders.gl\/\">shape_id<\/a>\u3054\u3068\u306b\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0\u3057\u30e9\u30f3\u30c0\u30e0\u306b\u8272\u3092\u5857\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u80cc\u666f\u5730\u56f3\u306f\u8868\u793a\u3059\u308b\u3068\u7d4c\u8def\u304c\u898b\u3048\u306b\u304f\u304f\u306a\u308b\u306e\u3067\u5207\u308a\u66ff\u3048\u5f0f\u306b\u3057\u307e\u3057\u305f\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u89e3\u8aac<\/h2>\n\n\n\n<p>GTFS\u30c7\u30fc\u30bf\u306f\u3001\u8907\u6570\u306ecsv\u30c7\u30fc\u30bf\u3092zip\u306b\u5727\u7e2e\u3057\u305f\u3082\u306e\u3068\u3057\u3066\u516c\u958b\u3055\u308c\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u53c2\u8003:<a href=\"https:\/\/www.gtfs.jp\/developpers-guide\/format-reference.html\" data-type=\"URL\" data-id=\"https:\/\/www.gtfs.jp\/developpers-guide\/format-reference.html\">\u9759\u7684\u30d0\u30b9\u60c5\u5831\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\uff08GTFS-JP\uff09\u306e\u6982\u8981<\/a><\/p>\n\n\n\n<p>\u305d\u3053\u3067\u3001loaders.gl\u3092\u7528\u3044\u3066zip\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\u3001\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u3067\u89e3\u51cd\u3057\u305f\u4e0a\u3067\u5404csv\u30c7\u30fc\u30bf\u3092\u30d1\u30fc\u30b9\u3057\u3066JavaScrpit\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3078\u3068\u5909\u63db\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u307e\u305f\u3001stops.txt(\u505c\u7559\u6240\u30c7\u30fc\u30bf)\u3068shapes.txt(\u7d4c\u8def\u30c7\u30fc\u30bf)\u306b\u3064\u3044\u3066\u306f\u3001<a href=\"https:\/\/turfjs.org\/\" data-type=\"URL\" data-id=\"https:\/\/turfjs.org\/\">turf.js<\/a>\u3092\u7528\u3044\u3066GeoJSON\u3078\u3068\u5909\u63db\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u7d4c\u8def\u30c7\u30fc\u30bf\u3092GeoJSON\u306b\u5909\u63db\u3059\u308b\u904e\u7a0b\u3067\u3001trips.txt(\u4fbf\u60c5\u5831)\u3092\u7d4c\u8def\u30c7\u30fc\u30bf\u306e\u5c5e\u6027\u30c7\u30fc\u30bf\u3068\u3057\u3066\u4ed8\u52a0\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u4e00\u901a\u308a\u306e\u5909\u63db\u304c\u7d42\u308f\u3063\u305f\u3089\u3001deck.gl\u306e<a href=\"https:\/\/gunmagisgeek.com\/blog\/deck-gl\/7654\" data-type=\"URL\" data-id=\"https:\/\/gunmagisgeek.com\/blog\/deck-gl\/7654\">GeojsonLayer<\/a>\u3092\u4f7f\u3063\u3066\u53ef\u8996\u5316\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u4ee5\u4e0b\u306f\u3001 GTFS\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u8fbc\u307f\u3068\u3001\u4e00\u9023\u306e\u30b3\u30f3\u30d0\u30fc\u30c8\u51e6\u7406\u306e\u30b3\u30fc\u30c9\u3067\u3059\u3002<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:js decode:true \">\/\/\u914d\u5217\u3092\u30b0\u30eb\u30fc\u30d3\u30f3\u30b0\u3059\u308b\nconst groupBy = (array, getKey) =&gt;\n  array.reduce((obj, cur, idx, src) =&gt; {\n    const key = getKey(cur, idx, src);\n    (obj[key] || (obj[key] = [])).push(cur);\n    return obj;\n  }, {});\n\n\nuseEffect(() =&gt; {\n    \/\/\u7d4c\u8def\u30c7\u30fc\u30bf\u3092\u60c5\u5831\u3092geojson\u306b\u5909\u63db\n    const shapes2geojson = (shapes, trips) =&gt; {\n        let lines = [];\n\n        \/\/shapes.txt(\u7d4c\u8def)\u306e\u5185\u5bb9\u3092\u3001shape_id\u3092\u5143\u306b\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0\n        const shapesGrouped = groupBy(shapes, (d) =&gt; d.shape_id);\n\n        \/\/trips.txt(\u4fbf\u60c5\u5831)\u306e\u5185\u5bb9\u3092\u3001shape_id\u3092\u5143\u306b\u30b0\u30eb\u30fc\u30d4\u30f3\u30b0\n        const tripsGrouped = groupBy(trips, (d) =&gt; d.shape_id);\n\n        \/\/shapes.txt\u306e\u5185\u5bb9\u3092\u30b0\u30eb\u30fc\u30d7\u3054\u3068\u306bgeojson(line)\u306b\u5909\u63db\n        for (let key in shapesGrouped) {\n            \/\/\u30dd\u30a4\u30f3\u30c8\u3092[lon, lat]\u5f62\u5f0f\u306e\u914d\u5217\u306b\u5909\u63db\n            const pointArry = shapesGrouped[key]\n                .sort((a, b) =&gt; a.shape_pt_sequence - b.shape_pt_sequence) \/\/\u5ff5\u306e\u70bashape_pt_sequence\u3092\u5143\u306b\u4e26\u3079\u66ff\u3048\u308b\n                .map((d) =&gt; [d.shape_pt_lon, d.shape_pt_lat]);\n\n            \/\/\u30dd\u30a4\u30f3\u30c8\u914d\u5217\u3092\u30e9\u30a4\u30f3\u306b\u3001\u5c5e\u6027\u5024\u306b\u4fbf\u60c5\u5831\u3092\u542b\u3081\u308b\n            const lineGeoJson = turf.lineString(pointArry, {\n                ...tripsGrouped[key]\n            });\n            lines.push(lineGeoJson);\n        }\n\n        \/\/\u30de\u30eb\u30c1\u30e9\u30a4\u30f3\u5316\u3059\u308b\n        const multiLine = turf.featureCollection(lines);\n        return multiLine;\n    };\n\n    \/\/\u505c\u7559\u6240\u30c7\u30fc\u30bf\u3092geojson\u306b\u5909\u63db\n    const stops2geojson = (stops) =&gt; {\n        let points = [];\n\n        stops.forEach((d) =&gt; {\n            const lnglat = [d.stop_lon, d.stop_lat];\n            const point = turf.point(lnglat, d);\n            points.push(point);\n        });\n\n        \/\/\u30de\u30eb\u30c1\u30dd\u30a4\u30f3\u30c8\u5316\u3059\u308b\n        const multiPoint = turf.featureCollection(points);\n        return multiPoint;\n    };\n\n    \/********************************************\n        * gtfs\u30c7\u30fc\u30bf\u306e\u8aad\u307f\u8fbc\u307f\n        ********************************************\/\n    const loadData = async (url) =&gt; {\n        const data = {};\n\n        \/\/zip\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\n        const zip = await load(url, ZipLoader);\n\n        \/\/zip\u5185\u30d5\u30a1\u30a4\u30eb\u3092csv\u3068\u3057\u3066\u30d1\u30fc\u30b9\n        for (let fileName in zip) {\n            const csv = await parse(zip[fileName], CSVLoader);\n            data[fileName] = csv;\n        }\n\n        \/\/geojson\u306b\u5909\u63db\u3057\u305fshapes\u3092\u4fdd\u7ba1\u3059\u308b\n        data[\"stops.geojson\"] = stops2geojson(data[\"stops.txt\"]);\n\n        \/\/geojson\u306b\u5909\u63db\u3057\u305fshapes\u3092\u4fdd\u7ba1\u3059\u308b\n        data[\"shapes.geojson\"] = shapes2geojson(\n            data[\"shapes.txt\"],\n            data[\"trips.txt\"]\n        );\n\n        console.log(\"loaded gtfs\", data);\n\n        setData(data);\n    };\n\n    loadData(\".\/data\/aoikotsu_GTFS.zip\");\n}, []);<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u3053\u306e\u8a18\u4e8b\u306fdeck.gl Advent Calen&hellip;<\/p>\n","protected":false},"author":1,"featured_media":7803,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[73],"tags":[],"class_list":["post-7753","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deck-gl","has-post-thumbnail-archive"],"_links":{"self":[{"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/posts\/7753","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/comments?post=7753"}],"version-history":[{"count":11,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/posts\/7753\/revisions"}],"predecessor-version":[{"id":7808,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/posts\/7753\/revisions\/7808"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/media\/7803"}],"wp:attachment":[{"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/media?parent=7753"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/categories?post=7753"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gunmagisgeek.com\/blog\/wp-json\/wp\/v2\/tags?post=7753"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}