経産省から7つ公開された「IMIコンポーネントツール」のESモジュール対応、全角半角変換に続き、4つ移植し、進捗5/7。

IMIコンポーネントツール ESモジュール版
ESモジュールなので、サーバーを使わず、ブラウザ上ですべて動作します。

IMIコンポーネントツール、ESモジュール対応状況
(1) 住所変換コンポーネント (対応予定)
(2) 法人種別名の抽出コンポーネント(対応予定)
(3) 全角-半角統一コンポーネント
(4) データバリデーションコンポーネント
(5) 電話番号正規化パッケージ一式
(6) 日付型正規化パッケージ一式
(7) 産業分類候補生成パッケージ一式

<script type="module"> import IMIMojiConverter from "https://code4sabae.github.io/imi-moji-converter/IMIMojiConverter.mjs"; import IMIEnrichmentContact from "https://code4sabae.github.io/imi-enrichment-contact/IMIEnrichmentContact.mjs"; import IMIEnrichmentDate from "https://code4sabae.github.io/imi-enrichment-date/IMIEnrichmentDate.mjs"; import IMIEnrichmentJSIC from "https://code4sabae.github.io/imi-enrichment-jsic/IMIEnrichmentJSIC.mjs"; import generateIMIDataValidator from "https://code4sabae.github.io/imi-data-validator/IMIDataValidator.mjs";

HTML内、type="module"と書くのがモジュール対応のJavaScriptの特徴。いろいろインポートして、サクッと使えます!

Denoをインスールし、JavaScript のファイル main.mjs を作って、

import IMIEnrichmentDate from "https://code4sabae.github.io/imi-enrichment-date/IMIEnrichmentDate.mjs"; console.log(IMIEnrichmentDate("令和二年12月1日"));

deno run で、すぐ実行できます。

$ deno run main.mjs { @context: "https://imi.go.jp/ns/core/context.jsonld", @type: "日付型", 表記: "令和二年12月1日", 標準型日付: "2020-12-01" }

各プロジェクト、コンソール上で使う cli.js を cli.mjs として、WebAPI提供する server.js を server.mjs として、それぞれ Deno 用に書き換えているので、参考にどうぞ。

同じコードが Deno を使うとコンソール上、サーバー上で動くので、クライアントとサーバーとの役割分担を柔軟に変更できるのがうれしい!

ESモジュール対応のバッジと、Denoでの動作バージョン確認のバッジ、作ってみました。


GitHub の README.md などで活用ください!

[![esmodules](https://taisukef.github.com/denolib/esmodulesbadge.svg)](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules) [![deno](https://taisukef.github.com/denolib/denobadge.svg)](https://deno.land/)

全角半角という言葉は、パソコン普及前夜のワープロ時代の遺物。「住所を全角で入れてください」など、優しくないお願いは排除し、必要な処理はプログラムでしましょう。

日本政府、経済産業省(経産省)から「IMI 情報共有基盤 コンポーネントツール」が発表されました。 5つ星オープンデータの語彙を定める「共通語彙基盤IMI」に関連したオープンソースなJavaScriptで作られたツール群です。

Node.js を前提に作られていますが、ブラウザや Deno で動かしたいので、ひとまず簡単そうな「全角-半角統一コンポーネント」を移植。インストール不要で下記のようにすっきり使えるものができました!(src on GitHub)

$ cat > main.mjs import IMIMojiConverter from "https://code4sabae.github.io/imi-moji-converter/IMIMojiConverter.mjs"; console.log(IMIMojiConverter.toHalfWidth("あかさたな0123456789")); console.log(IMIMojiConverter.toFullWidth("アカサタナ0123456789")); $ deno run main.mjs あかさたな0123456789 アカサタナ0123456789

ブラウザからはこの main.mjs をESモジュールとして読み込めば動きます

<script type="module" src="main.mjs"></script>

ブラウザ用の記述と、コンソール上のDeno、同じコードで動くのが、ESモジュールのいいところ!全角や半角という言葉をウェブサイト上で使っている方、利用者の登録率や利便性を向上させるためにご活用ください!

移植にあたって、imi-moji-converter が依存している Node.js 用の「半角全角変換ライブラリ」をDeno用に移植しました。(src on GitHub)

変換が必要ないので、srcフォルダが不要で、プログラムはすべてdocs内に置いています。ESモジュール以前の表記、require/export を import/export に変更し、testをDeno用に書き換えます。Node.jsっぽいテスト表記を実現する nodelikeassert.mjsdenolib に追加。
半角全角変換ライブラリ test
ライブラリはURLで読み込むだけなので、node_modules も不要、インストールも不要でとってもシンプルです!


オープンソース活用、加速しそうです!
→ 2020-05-30 対応拡大

links
- 経産省発の npm モジュール!住所や電話番号の正規化、ジオコーディングなどができる IMI コンポーネントツールを試した! - Geolonia developer's blog
- DenoとWebScoketで作る匿名チャットサーバー&webアプリ
- DenoとNodeとwebアプリの全対応、コンパクトに作り始めるLISPのS式風ライブラリ
- NodeからDenoへ、福井県で活用オープンソースのPUSHかんたんオープンデータ、バックエンド移行

お気に入りのDeno(ディーノ)、WebSocketを使ったチャットづくりなど、いい感じですが、Nodeで作った既存プロジェクトとの並行はしばらく続きます。 そこで、DenoとNodeに両対応する方法を整理するのを兼ねて、世界で2番目に古いというプログラミング言語、LISPのS式っぽいものを作ってみました。

const n = S.eval([ S.PLUS, 1, [ S.MUL, 2, 3 ] ])

LISPの特徴はツリー構造(LISPでは、S式という)をそのままプログラムとして使うところ。上記、JavaScriptの配列で表現したS式を評価をするサンプルです。


minimum S, src on GitHub
プログラムはとってもシンプル。四則演算とIF文しかありません。興味があれば,forkして、いろいろ機能を追加するなどして遊んでみてください。 (import.metaとglobalThis.processを使い、Deno/Nodeでの直接起動時に説明表示)

ブラウザ上や、Denoでは、import文を使って、簡単に組み込めます(Nodeの場合ダウンロードしてファイルとして使用)

import S from 'https://taisukef.github.io/minimum_S/S.mjs' console.log('S.eval([S.PLUS, 1, 2]) is ' + S.eval([S.PLUS, 1, 2]))

deno test で簡単テストな S_test.ts が走ります。TypeScriptが基本のDenoはテストコードの拡張子はtsとします。 TypeScriptはJavaScriptの上位互換のプログラミング言語なので、拡張子を変えるだけでOKです。

Denoに刺激されてか、Node.jsも試験的実装ながら関数外でも await が使えるように!

const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)) await sleep(3000)

指定された数分ミリ秒待つ関数 sleep を定義して、3000 = 3秒待つ。Nodeで普通に動かそうとすると「SyntaxError: Unexpected reserved word」とエラーになりますが、Node v14.3.0 で下記のように指定すれば、deno同様、動きます。 ただし、deno/node共に対話型アプリ上や、ブラウザ上では引き続き使えないので注意!(src on GitHub)

node --experimental-top-level-await sleep_test.mjs

Denoでの場合

deno run test.mjs

JavaScriptなど、最近のプログラミング言語には便利表記方法がいろいろ増えているので、少しずつ使える幅を増やすと楽できます。 例えば、=> は、関数定義のちょっと変わった書き方、reduceは配列の中身全部に対して処理して一つの値をだすものです。

sleepは、下記のようにも書けます。お好みでどうぞ!(thisを使う時は => と function で挙動が違うので注意)

async function sleep (msec) { return new Promise(function (resolve, reject) { setTimeout(resolve, msec) }) }

Deno情報を調べると気になる、汁なし担々麺「DENO」(こちらの読みは、での)

ファミマで販売中とのことで、こちらも試食!もっちりした麺が良い感じでした!札幌発、行くことがあればぜひ実店舗も巡礼したいところ。(東京、新潟、福島にもあった!)

links
- DenoとWebScoketで作る匿名チャットサーバー&webアプリ

Javaで作ったサーバーサイドのプログラム、少しずつリファインしつつDenoへ移行していきます。 まずはジェネラルにチャットサーバー。WebSocketベースで軽量実装!

AnonymousChat in deno」(src on GitHub)
部屋を作ってリアルタイムにやりとりできるだけのシンプルな匿名チャット。SSL化に別途調査が必要なので、テスト的に立ち上げています。

部屋があって、ユーザー管理する、オーソドックスなサーバー設計にしつつ、シンプルに匿名チャットとして作ったもの。「thecodeholic/deno-websocket-chat: Realtime Chat App with Deno and WebSockets」が参考になりました。Node.js風fsを使ったwebサーバーを除いて200行ほど。(server.mjs for Deno)

import { listenAndServe } from 'https://deno.land/std/http/server.ts' import { acceptWebSocket, acceptable, isWebSocketCloseEvent } from 'https://deno.land/std/ws/mod.ts' import { v4 } from 'https://deno.land/std/uuid/mod.ts' import { serveWeb } from 'https://taisukef.github.io/denolib/webserver.mjs' import dotenv from 'https://taisukef.github.io/denolib/dotenv.mjs' dotenv.config() const PORT = parseInt(Deno.env.get('PORT')) || 3000 class House { constructor () { this.rooms = new Map() this.users = new Map() } async serve (port) { listenAndServe({ port }, async req => { if (req.method === 'GET' && req.url === '/ws') { if (acceptable(req)) { const wsreq = { conn: req.conn, bufReader: req.r, bufWriter: req.w, headers: req.headers } const wsock = await acceptWebSocket(wsreq) await this.accept(wsock) } } else { serveWeb(req) } }) console.log(`started on port ${PORT}`) } async accept (ws) { let user = null for await (const data of ws) { if (isWebSocketCloseEvent(data)) { // code: 1001 break } const event = JSON.parse(data) const e = event.event if (e === 'login') { user = this.users.get(event.id) if (user) { if (user.passcode !== event.passcode) { await ws.send(JSON.stringify({ event: 'error', data: 'please login' })) } else { user.attachWebSocket(ws) } } if (!user) { user = new User(ws, this.getValidName(event.name)) this.users.set(user.id, user) } user.sendOne(ws, user.getData()) } else if (user === null) { await ws.send(JSON.stringify({ event: 'error', data: 'please login' })) } else if (e === 'create') { let room = this.rooms.get(event.room) if (room) { await user.sendOne(ws, { event: 'error', data: 'already exists' }) continue } room = new Room(event.room, user) this.rooms.set(event.room, room) user.send(room.getData()) } else if (e === 'enter') { const room = await this.getRoom(user, event.room) if (!room) { continue } if (room.users.indexOf(user) < 0) { room.users.push(user) const rep = { event: 'enter', data: { id: user.id, name: user.name } } room.users.forEach(u => u.send(rep)) } user.sendOne(ws, room.getData()) } else if (e === 'leave') { const room = await this.getRoom(user, event.room) if (!room) { continue } const n = room.users.indexOf(user) if (n < 0) { await user.sendOne(ws, { event: 'error', data: 'not member' }) continue } const rep = { event: 'leave', data: { id: user.id, name: user.name } } room.users.forEach(u => u.send(rep)) room.users.splice(n, 1) } else if (e === 'message') { const room = await this.getRoom(user, event.room) if (!room) { continue } if (room.users.indexOf(user) < 0) { await user.sendOne(ws, { event: 'error', data: 'not member' }) continue } // special command if (event.data.text === '/clearall') { room.messages = [] return } const message = { fromid: user.id, name: user.name, data: event.data } // console.log(room, room.messages) room.messages.push(message) const e = { event: 'message', data: { room: room.name, message } } room.users.forEach(u => u.send(e)) } else { await user.sendOne(ws, { event: 'error', data: 'unknown event' }) } } } async getRoom (user, rname) { const room = this.rooms.get(rname) if (!room) { await user.send({ event: 'error', data: 'not found ' + rname }) return null } return room } getValidName (name) { if (name == null) { name = '' } name = name.toString().trim() if (name.length === 0) { name = 'no name' } for (;;) { let flg = false this.users.forEach(v => { flg = flg || v.name === name }) if (!flg) { break } name += '\'' } return name } } class Room { constructor (name, owner) { this.id = v4.generate() this.name = name this.owner = owner this.users = [owner] this.messages = [] } getData () { return { event: 'room', data: { id: this.id, room: this.name, users: this.users.map(u => { return { id: u.id, name: u.name } }), messages: this.messages } } } } class User { constructor (ws, name) { this.ws = [ws] this.name = name this.id = v4.generate() this.passcode = v4.generate() this.rooms = [] } async sendOne (ws, json) { try { ws.send(JSON.stringify(json)) } catch (e) { } } async send (json) { let remflg = false for (let i = 0; i < this.ws.length; i++) { try { this.ws[i].send(JSON.stringify(json)) } catch (e) { this.ws[i] = null remflg = true // console.log('send', e) } } if (remflg) { this.ws = this.ws.filter(w => w) } } attachWebSocket (ws) { this.ws.push(ws) } getData () { return { event: 'user', data: { id: this.id, passcode: this.passcode, name: this.name, rooms: this.rooms.map(r => r.name) } } } } const house = new House() house.serve(PORT)

コミュニケーションツールも、ネットワークゲームもベースは一緒!

dotenv, fs, webserver など、Denoに使うちょっとしたライブラリは「taisukef/denolib」こちらでまとめて管理していきます。

JavaScriptでサーバーサイドもサクサク書けて便利な Node.js。 jsからmjsへ移行したもののブラウザ上のコードとの互換性が無いことにと、プロジェクトごとに増える node_modules が気になっていたところ、Node.jsの作者 Ryan Dahl 氏自身による Deno を発見。 いい感じだったので「PUSHかんたんオープンデータ」を移行してみました。


福井県施設 オープンデータ - PUSHかんたんオープンデータ」(src on GitHub)
バックエンドのエンジン変更なので、見た目は変わりません。 それでは寂しいので、複数の施設をひとまとめにする「集約オープンデータ」に対応! 複数の施設や店舗をひとまとめにしたオープンデータリンクを自由に作成できます。追加、削除、順番変更も思いのまま!


福井県、県有施設等の混雑状況をオープンデータとして公開:民間有志が同データを利用して混雑状況の一覧をスマートフォン等からも閲覧可能なダッシュボードを作成し公開 | カレントアウェアネス・ポータル
国立国会図書館さん、中日新聞さんにご紹介いただきました!オープンデータで切り拓く、ライトな官民連携の仕組み。ぜひ他の地域でもどうぞ!
県有施設の混雑状況をオープンデータに 出発前に確認、スマホでOK:福井:中日新聞(CHUNICHI Web)


Deno
恐竜アイコンがかわいい、Deno(ディーノ)は、Node.js作者が反省を踏まえて開発した、webフレンドリーなもうひとつのJavaScriptバックエンドエンジンです。ブラウザとの互換性の高さと、package.json不要で、URLでimportし、適宜キャッシュしてくれるパッケージ管理システムが特徴です。 (Denoの登場でNode.jsの時代は終わるのか? - Qiita

Node.js 用プログラムからの移行、ファイルアクセスの標準モジュール fs を必要なものだけ独自に用意。(node_fs.mjs on GitHub)

const fs = {} fs.readFileSync = function (fn) { const data = Deno.readFileSync(fn) const decoder = new TextDecoder('utf-8') const s = decoder.decode(data) return s } fs.writeFileSync = function (fn, s) { const d = new TextEncoder('utf-8').encode(s) Deno.writeFileSync(fn, d) } fs.appendFileSync = function (fn, s) { const d = new TextEncoder('utf-8').encode(s) Deno.writeFileSync(fn, d, { append: true }) } fs.readdirSync = function (dirn) { return Deno.readDirSync(dirn) } fs.mkdirSync = function (dirn) { return Deno.mkdirSync(dirn) } export default fs

あとは、expressからDenoのhttpに移植して、完了!(push_deno.mjs on GitHub)

セットアップに npm install はもういりません!下記の起動コマンドで即実行!(ファイルアクセスやネットワークアクセスを許可する -A オプションが必要です)

deno run -A push_deno.mjs

mjs x Deno の組み合わせで作ってみることにします!

混雑を避ける、コロナ禍では平時以上に重要です。福井県統計情報課とCode for Fukui / Sabae 連携で、福井県施設ダッシュボードに混雑状況表示が加わり、運用開始

福井県施設ダッシュボード」(src on GitHub)
定休日や、臨時休館日などを反映して、開いているかどうか、空いているかどうかがひと目で分かります。CSV/JSONオープンデータを使った応用例です。 デザインや、使い勝手、データを収集しての解析など、自由に二次利用が可能です。もちろんこのアプリもオープンソース、改造提案、改造版立ち上げも大歓迎!


福井県施設状況オープンデータ
CSVやJSONでダウンロードし、自由に活用可能です。


PUSH かんたんオープンデータ」(src on GitHub)
Code for Fukui / Sabae で開発した、データを更新する仕組みはこちら。Googleスプレッドシートによる更新をもっとシンプルにしたイメージです。スマホで簡単に、混雑状況を伝えられます。 急な休業日、営業時間変更も簡単!


教育博物館 オープンデータ
個別のページも自動的にでき、CSV/JSONオープンデータへのリンクがあって、人にも機械にも優しい。


教育博物館 オープンデータ
プログラマー大好き、JSON形式!


エイトセンス オープンデータ
ランチなどでお世話になっている鯖江のカフェ、EIGHT SENSE(エイトンセンス)さんにも使ってみてもらいました。行政施設のみならず、幅広く使っていきたいところ。


県有施設等の混雑情報をオープンデータ化します | 福井県ホームページ
行政と民間、オープンデータでどんどんつないでいきましょう!


日本政府による民間とすすめるコロナ対策「指標をCSVオープンデータに! by 福野泰介さん | 新型コロナウイルスと戦う意見募集サイト VS COVID-19アイディアボックス」が、人気です!

福井市のヒノキ花粉飛散量を予測するチャレンジ「カフモン2020」の期間が終了。 福井県による花粉オープンデータと照合して、結果発表です!

カフモン - 花粉オープンデータチャレンジ
優勝は、みずのさん!おめでとう!


2020年分のスギ花粉とヒノキ花粉の量も合わせてグラフ表示しています。(src on GitHub)
東京都版対策サイトでも活躍、グラフライブラリ Chart.js を、ES6モジュール対応の Chart.mjs へと調整、import 構文ですっきり使えるようにしています。

export default Chart

変更点はシンプル、末尾に Chartクラスを export と書くだけでした。

import Chart from './lib/Chart.mjs' import util from './lib/util.mjs' // 中略 const main = async () => { const url = 'data/r2_kahun_fukui.csv' const json = await util.fetchCSVtoJSON(url) console.log(json) showData(json) const urlp = 'data/kafumon2020.csv' const pdata = await util.fetchCSVtoJSON(urlp) console.log(pdata) showResult(json, pdata) }

Chart.mjs と util.mjs を使って、2つのグラフを作成しています。(src on GitHub)


今年のヒノキ花粉は、例と比べてずっと少なかったという結果でした。何が影響したのでしょうか?2007年からのデータがあるので、過去の気象データなどさまざまなデータを使った解析が可能です。

来年のカフモン2021もお楽しみに!

混雑を避ける重要性が増しています。リアルタイムに施設の混雑状況をオープンデータ化し、すばやくチェックするシステムを作りました。 ユーザー登録不要、スマホでもPCでも簡単に一次情報として更新できる、JavaScript/Node.jsで開発したシンプルなオープンソースです。


PUSH かんたんオープンデータ
施設名、休館日などを記載し、登録すると、JSONでアクセス可能なオープンデータが生成されます。

認証用コードが生成され、そのURLをブックマークなどしておけばいつでも更新できます。 もし、認証用コードを忘れてしまったり、誰かに間違って伝えてしまう可能性がでた場合は、新たにデータを生成してください。


COVID-19 Japan 新型コロナウイルス対策ダッシュボード
累積退院者数が1万人を超え、徳島県も再び現在患者数0人になるなど、全国的に感染は縮小方向にあります。

根本的な治療方法が確立されていない以上、注意は引き続き必要ですが、活動を通常に戻していきたいところです。 鯖江市でも飲食店の営業再開が少しずつ広まってきていますが、営業日や時間が通常通りではないので、こうしたリアルタイムのオープンデータが便利です。

ダッシュボードへの反映など、引き続き開発を続けます。Code for Fukui としてのオープンデータ、開発参加、デザイン参加も歓迎です!
福井県施設ダッシュボードと日本語で書かれた休館日や営業時間表記を解釈するJavaScriptのクラスライブラリ dtcheckjp.mjs

Web標準、W3C BROWSERS AND ROBOTICS コミュニティグループで議論され、仕様策定中のWebGPIOを使って、PCに接続したIchigoJamをコントロールしてみました。 まずは、JavaScriptで、エルチカ!(USBシリアル付き、IchigoKamuy with IchigoJam BASIC)

実装は、こちら (src on GitHub)

import navigator from './WebGPIO.mjs' const sleep = async msec => new Promise(resolve => setTimeout(resolve, msec)) const main = async () => { const gpio = await navigator.requestGPIOAccess() const port = gpio.ports.get('LED') await port.export('out') // export (activate) the port. for (;;) { port.write(1) await sleep(500) port.write(0) await sleep(500) } } main()

GitHubからソースをcloneするか、ダウンロードして

npm i

で、シリアル通信に必要なパッケージをセットアップ。USBシリアルポートを設定する '.env' ファイルを作成。

SERIAL_DRIVER=/dev/tty.SLAB_USBtoUART

あとは動かすだけ!

node led4.mjs

エルチカ部分、sleepメソッドやasync/awaitを使わず、このようにも記述可能です。

let v = 1 setInterval(function () { port.write(v) v = 1 - v }, 500)

まだ出力のみです。入力と、onchangeを使った入力監視を実装すれば、現時点でのWebGPIOとしては完了。

WebGPIOとして拡張したい3点
- アナログ出力(PWM) - サーボの制御、LEDの明るさ制御、簡易的なサウンド出力に使える
- アナログ入力(ANA) - アナログセンサーで距離や明るさ、温度などを計測できる
- プルアップ・プルダウンの設定 - 3値出力として使うなど

ちなみに、JavaScriptが使えるIchigoJam用OS、IchigoLatteでのエルチカはこちら

while (1) { led(1); sleep(500); led(0); sleep(500); }

何の宣言も必要なく、シリアルで転送したり、直接キーボードをつないで打ち込めばOK!
100円CPUには、Promiseの実装はちょっとヘビーだし過剰なので、WebGPIO light というのもあり?

links
- CHIRIMEN Open Hardware
- JavaScript Robotics Advent Calendar 2016 13日目 WebGPIOのPolyfillを実装してみて
- ハート基板 x IchigoKamuy で感染病床残数IoT by シリアルで制御するIchigoJam、IchigoSerial for Node.js

時間軸の高解像度化、1秒間あたりの画像枚数を増やす、高フレームレート化が熱い!

通常使いのMacbook Proに加え、家のWindows PCでは、240Hz駆動するモニターIO-DATAのGigaCrystaを愛用。 マウスの動きや、スクロール、3Dモデリング時など、ひとつひとつの動作がとっても滑らかで気持ち良い。

この滑らかさを伝えるには、直接見て触ってもらうしかない。なぜなら、今の世の中、スマホやPC、カメラの殆どが通常60fps(iPad Pro は120Hz)。 その場で比較すればひと目でわかる、動画再生アプリを作って、240fpsでスロー撮影できるiPhone動画。

240fps movie
Blenderで、240fps設定でレンダリングした動画をffmpegで12/24/30/60/120へとフレームレート変換。選択できるようにしてある。対応モニターで見れば、120fps/240fpsの滑らかさを体験できるでしょう!

4K/8Kとは別軸の高解像度化。まだまだコンピューターパワーが足りません!

links
- 映画の10倍、普通の4倍、秒間240回描画! 革新的ディスプレイに備える軽量ゲームフレームワーク sq-game.js
- 240Hz以上のモニター検索 on Amazon (現時点では240fpsが最高、2011年3D用に480Hz駆動も存在!

Tweet
クリエイティブ・コモンズ・ライセンス
この作品は「Creative Commons — CC BY 4.0」の下に提供されています。
CC BY / @taisukef / アイコン画像 / プロフィール画像 / RSS