2020/09,江端さんの技術メモ

以下、https://github.com/SINTEF-9012/PruneCluster の簡単な翻訳

PruneCluster is a fast and realtime marker clustering library.
PruneClusterは、高速でリアルタイムなマーカークラスタリングライブラリです。

Example 1: 150 000 randomly moving markers.
例1:150,000個のマーカーをランダムに移動させる。

 Example 2: Realtime clusters of tweets.
例2. リアルタイムでツイートのクラスターを作成します。

It's working with Leaflet as an alternative to Leaflet.markercluster.
Leaflet.markerclusterの代替としてLeafletと連携しています。

The library is designed for large datasets or live situations. The memory consumption is kept low and the library is fast on mobile devices, thanks to a new algorithm inspired by collision detection in physical engines.
このライブラリは、大規模なデータセットやライブの状況を想定して設計されています。物理エンジンの衝突検出にヒントを得た新しいアルゴリズムを採用しているため、メモリ消費量は低く抑えられており、ライブラリはモバイルデバイス上で高速に動作します。

Features
特徴

Realtime
リアルタイム

The clusters can be updated in realtime. It's perfect for live datasets or datasets you want to filter at runtime.
クラスターをリアルタイムで更新することができます。ライブデータセットや、実行時にフィルタリングしたいデータセットに最適です。

Fast
高速化

Number of markers
マーカーの数
First step
最初のステップ
Update (low zoom level)
更新(低倍率)
Update (high zoom level)
更新(高倍率)
100instantinstantinstant
1 000instantinstantinstant
10 00014ms3ms2ms
60 00070ms23ms9ms
150 000220ms60ms20ms
1 000 0001.9s400ms135ms

These values are tested with random positions, on a recent laptop, using Chrome 38. One half of markers is moving randomly and the other half is static. It is also fast enough for mobile devices.
これらの値は、最近のノートパソコンで、Chrome 38を使用して、ランダムな位置でテストされています。マーカーの半分はランダムに動き、残りの半分は静止しています。モバイル端末でも十分に高速です。

If you prefer real world data, the 50k Leaflet.markercluster example is computed in 60ms (original).
実世界のデータをお望みなら、50k Leaflet.markercluster の例は 60ms (オリジナル) で計算されています。

Weight
ウエイト

You can specify the weight of each marker.
各マーカーのウエイトを指定することができます。

For example, you may want to add more importance to a marker representing an incident, than a marker representing a tweet.
例えば、ツイートを表すマーカーよりも、事件を表すマーカーの方がウエイトが高い場合があります。

Categories
カテゴリ

You can specify a category for the markers. Then a small object representing the number of markers for each category is attached to the clusters. This way, you can create cluster icons adapted to their content.
マーカーのカテゴリを指定することができます。そして、各カテゴリのマーカーの数を表す小さなオブジェクトがクラスタに添付されます。このようにして、その内容に合わせたクラスタアイコンを作成することができます。

Dynamic cluster size
動的なクラスタサイズ

The size of a cluster can be adjusted on the fly (Example)
クラスターの大きさをその場で調整可能(例)

Filtering
フィルタリング

The markers can be filtered easily with no performance cost.
性能コストをかけずに、簡単にマーカーをろ過することができます。

Usage
使用方法

Classic Way
古典的な方法

	<!-- In <head> -->
	<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css"
  integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
  crossorigin=""/>

	<!-- In <head> or before </body> -->
	<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"
  integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
  crossorigin=""></script>
	<script src="PruneCluster/dist/PruneCluster.js"></script>

Webpack & NPM

npm install exports-loader prunecluster

import { PruneCluster, PruneClusterForLeaflet } from 'exports-loader?PruneCluster,PruneClusterForLeaflet!prunecluster/dist/PruneCluster.js'

Example

var pruneCluster = new PruneClusterForLeaflet();

...
var marker = new PruneCluster.Marker(59.8717, 11.1909);
pruneCluster.RegisterMarker(marker);
...

leafletMap.addLayer(pruneCluster);

PruneClusterForLeaflet constructor

PruneClusterForLeaflet([size](#set-the-clustering-size), margin);

You can specify the size and margin which affect when your clusters and markers will be merged.
クラスターとマーカーがマージされるときに影響するサイズとマージンを指定できます。

size defaults to 120 and margin to 20.
サイズはデフォルトで120、マージンは20に設定されています。

Update a position
ポジションの更新

marker.Move(lat, lng);

Deletions
削除

// Remove all the markers
pruneCluster.RemoveMarkers();

// Remove a list of markers
pruneCluster.RemoveMarkers([markerA,markerB,...]);

Set the category
カテゴリを設定する

The category can be a number or a string, but in order to minimize the performance cost, it is recommended to use numbers between 0 and 7.
カテゴリは数字でも文字列でも構いませんが、パフォーマンスコストを最小限に抑えるために、0~7の間の数字を使用することをお勧めします。

marker.category = 5;

Set the weight
ウエイトを設定する

marker.weight = 4;

Filtering
フィルタリング

marker.filtered = true|false;

Set the clustering size
クラスタリングサイズを設定する

You can specify a number indicating the area of the cluster. Higher number means more markers "merged". (Example)
クラスタの領域を示す数値を指定することができます。数値が大きいほど、より多くのマーカーが "マージ "されていることを意味します。(例)

pruneCluster.Cluster.Size = 87;

Apply the changes
変更を適用する

Must be called when ANY changes are made.
変更が行われたときに呼び出されなければなりません。

pruneCluster.ProcessView();

Add custom data to marker object
マーカーオブジェクトにカスタムデータを追加

Each marker has a data object where you can specify your data.
各マーカーには、データを指定できるデータオブジェクトがあります。

marker.data.name = 'Roger';
marker.data.ID = '76ez';

Setting up a Leaflet icon or a Leaflet popup
リーフレットアイコンやリーフレットのポップアップを設定する

You can attach to the markers an icon object and a popup content
マーカーにはアイコンオブジェクトとポップアップコンテンツを添付することができます。

marker.data.icon = L.icon(...);  // See http://leafletjs.com/reference.html#icon
marker.data.popup = 'Popup content';

Faster leaflet icons
より高速なリーフレットアイコン

If you have a lot of markers, you can create the icons and popups on the fly in order to improve their performance.
マーカーが多い場合は、その場でアイコンやポップアップを作成しておくとパフォーマンスが向上します。

function createIcon(data, category) {
    return L.icon(...);
}

...

marker.data.icon = createIcon;

You can also override the PreapareLeafletMarker method. You can apply listeners to the markers here.
PreapareLeafletMarkerメソッドをオーバーライドすることもできます。ここではマーカーにリスナーを適用することができます。

pruneCluster.PrepareLeafletMarker = function(leafletMarker, data) {
    leafletMarker.setIcon(/*... */); // See http://leafletjs.com/reference.html#icon
    //listeners can be applied to markers in this function
    leafletMarker.on('click', function(){
    //do click event logic here
    });
    // A popup can already be attached to the marker
    // bindPopup can override it, but it's faster to update the content instead
    if (leafletMarker.getPopup()) {
        leafletMarker.setPopupContent(data.name);
    } else {
        leafletMarker.bindPopup(data.name);
    }
};

Setting up a custom cluster icon
カスタム クラスター アイコンの設定

pruneCluster.BuildLeafletClusterIcon = function(cluster) {
    var population = cluster.population, // the number of markers inside the cluster
        stats = cluster.stats; // if you have categories on your markers

    // If you want list of markers inside the cluster
    // (you must enable the option using PruneCluster.Cluster.ENABLE_MARKERS_LIST = true)
    var markers = cluster.GetClusterMarkers() 
        
    ...
    
    return icon; // L.Icon object (See http://leafletjs.com/reference.html#icon);
};

Listening to events on a cluster
クラスタ上のイベントを聞く

To listen to events on the cluster, you will need to override the BuildLeafletCluster method. A click event is already specified on m, but you can add other events like mouseover, mouseout, etc. Any events that a Leaflet marker supports, the cluster also supports, since it is just a modified marker. A full list of events can be found here.
クラスタ上のイベントをリッスンするには、BuildLeafletClusterメソッドをオーバーライドする必要があります。クリックイベントはすでに m で指定されていますが、マウスオーバーやマウスアウトなどの他のイベントを追加することができます。リーフレットマーカーがサポートしているイベントはすべて、クラスタもサポートしています。イベントの完全なリストはこちらを参照してください。

Below is an example of how to implement mouseover and mousedown for the cluster, but any events can be used in place of those.
以下にクラスタのマウスオーバーとマウスダウンの実装例を示しますが、これらの代わりに任意のイベントを使用することができます。

pruneCluster.BuildLeafletCluster = function(cluster, position) {
      var m = new L.Marker(position, {
        icon: pruneCluster.BuildLeafletClusterIcon(cluster)
      });

      m.on('click', function() {
        // Compute the  cluster bounds (it's slow : O(n))
        var markersArea = pruneCluster.Cluster.FindMarkersInArea(cluster.bounds);
        var b = pruneCluster.Cluster.ComputeBounds(markersArea);

        if (b) {
          var bounds = new L.LatLngBounds(
            new L.LatLng(b.minLat, b.maxLng),
            new L.LatLng(b.maxLat, b.minLng));

          var zoomLevelBefore = pruneCluster._map.getZoom();
          var zoomLevelAfter = pruneCluster._map.getBoundsZoom(bounds, false, new L.Point(20, 20, null));

          // If the zoom level doesn't change
          if (zoomLevelAfter === zoomLevelBefore) {
            // Send an event for the LeafletSpiderfier
            pruneCluster._map.fire('overlappingmarkers', {
              cluster: pruneCluster,
              markers: markersArea,
              center: m.getLatLng(),
              marker: m
            });

            pruneCluster._map.setView(position, zoomLevelAfter);
          }
          else {
            pruneCluster._map.fitBounds(bounds);
          }
        }
      });
      m.on('mouseover', function() {
        //do mouseover stuff here
      });
      m.on('mouseout', function() {
        //do mouseout stuff here
      });

      return m;
    };
};

Redraw the icons
アイコンを再描画

Marker icon redrawing with a flag:
マーカーアイコンを旗で再描画。

marker.data.forceIconRedraw = true;

...

pruneCluster.ProcessView();

Redraw all the icons:

pruneCluster.RedrawIcons();

Acknowledgements
謝辞

This library was developed in context of the BRIDGE project. It is now supported by the community and we thank the contributors.
このライブラリはBRIDGEプロジェクトの文脈で開発されました。現在はコミュニティによってサポートされており、貢献者に感謝しています。

Licence
ライセンス

The source code of this library is licensed under the MIT License.
このライブラリのソースコードはMITライセンスの下でライセンスされています。

インストール方法

C:\Users\ebata>git clone https://github.com/SINTEF-9012/PruneCluster.git
Cloning into 'PruneCluster'…
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 1054 (delta 3), reused 2 (delta 1), pack-reused 1044
Receiving objects: 100% (1054/1054), 68.39 MiB | 9.96 MiB/s, done.
Resolving deltas: 100% (699/699), done.
Updating files: 100% (54/54), done.

C:\Users\ebata>cd PruneCluster

ここで初めてドキュメントに目を通す。

PruneClusterは、高速でリアルタイムなマーカークラスタリングライブラリです。

Leaflet.markerclusterの代替としてLeafletと連携しています。

このライブラリは、大規模なデータセットやライブ状況向けに設計されています。物理エンジンの衝突検出に着想を得た新しいアルゴリズムのおかげで、メモリ消費量は低く抑えられ、ライブラリはモバイルデバイス上で高速に動作します。
クラスターをリアルタイムで更新することができます。ライブデータセットや、実行時にフィルタリングしたいデータセットに最適です。
各マーカーの重みを指定できます
たとえば、ツイートを表すマーカーよりも、事件を表すマーカーの方が重要度が高い場合があります。
マーカーのカテゴリを指定することができます。そして、各カテゴリのマーカーの数を表す小さなオブジェクトがクラスタに添付されます。このようにして、その内容に合わせたクラスタアイコンを作成することができます。

C:\Users\ebata\PruneCluster\examples>の中にあるサンプルのhtmlの中の座標をいじるだけで、こんなことができました。

https://kobore.net/soft/PruneCluster/examples/toyosu.100.html

このクラスタを無くすには、例えば

var leafletView = new PruneClusterForLeaflet(1,1);  // (120,20)がデフォルト

とかすればいいみたい。

その上で、オブジェクトを1000個に増やしたものが以下になります。

https://kobore.net/soft/PruneCluster/examples/toyosu2.1000.html

以上

2020/09,2020/09,江端さんの忘備録

私は、自分の娘たちに、若いころの恋愛話を―― 客観的に、他人事のように ―― ペラペラとしゃべっています。

I talk to my daughters about my youthful romance objectively, like other's love affairs.

それを受けてか、我が家の娘たちも、現在進行形の話を、私に話します(当然、開示内容には制限はかかっているでしょうが)

After that, my daughters also tell me the ongoing story (although the disclosure may be limited, of course).

-----

最近、娘にウケたネタとしては、

Recently, as a fummy story that my daughter got

―― 運命の赤い糸は、自分を中心として1024本ほど同時に繋っていて、そのいずれも確定状態にはない

"The 1024 red threads of destiny are connected at the same time, centering on you, and none of them are in a confirmed state"

という、最近の私の『量子論』の勉強に基づく、いわゆる「量子恋愛論」でした。

That was the so-called "quantum love theory" based on my recent study of "quantum theory".

-----

その話を聞いた娘は、大爆笑していました。

After hearing the story, she was laughing loudly.

彼女に何があったのかは知りませんが、『まあ、がんばれ』とだけ、言っておきました。

Though I don't know what happened to her, I told my daughter, "Well, do your best."

2020/08,江端さんの技術メモ

  • 概要
    • Webシステムを外部から利用するためのプログラムの呼び出し規約(API)
    • リソースの操作はHTTPメソッドによって指定(取得ならGETメソッド、書き込みならPOSTメソッド)される
    • 結果はXMLやHTML、JSONなどで返される
    • 処理結果はHTTPステータスコードで通知する
  • RESTの基本仕様
    • セッション管理を行わない
    • 情報を操作する命令の体系が予め定義(HTTPのGETやPOSTメソッドなどで)されている
    • 汎用的な構文で識別される(URLやURIなど)
    • 情報の内部に、別の情報を含めることができる
    • リソースに対してURLが対応づけられる
    • 「GET」なら取得、「POST」なら作成、「PUT」なら更新、「DELETE」なら削除のように処理する
  • ここから先は、Golangの開発プロセスになる
    • go get -u github.com/gorilla/mux を行う

golangでシンプルなRESTful APIを作ってみよう!を、そのまま書き下してトレースで動きを追ってみました。

今回から、emacs + gdb を断念して、Visual Studio Code を使ってみました。うん、死ぬほど便利。

//C:\Users\ebata\go_rest\restful\main.go
 
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
)

// Item representation
type Item struct {
	Title       string `json:"title"`
	Description string `json:"description"`
}

// Global, static list of items
var itemList = []Item{
	Item{Title: "Item A", Description: "The first item"},
	Item{Title: "Item B", Description: "The second item"},
	Item{Title: "Item C", Description: "The third item"},
}

// Controller for the / route (home)
func homePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "This is the home page. Welcome!")
}

// Contoller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
	respondWithJson(w, http.StatusOK, itemList)
}

// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
	// Get query parameters using Mux
	vars := mux.Vars(r)

	// Convert {id} parameter from string to int
	key, err := strconv.Atoi(vars["id"])

	// If {id} parameter is not valid in
	if err != nil {
		respondWithError(w, http.StatusBadRequest, "Invalid reqest payload")
		return
	}

	// If item with ID of {id} does not exist
	if key >= len(itemList) {
		respondWithError(w, http.StatusNotFound, "Item does not exist")
		return
	}

	respondWithJson(w, http.StatusOK, itemList[key])
}

func respondWithError(w http.ResponseWriter, code int, msg string) {
	respondWithJson(w, code, map[string]string{"error": msg})
}

func respondWithJson(w http.ResponseWriter, code int, payload interface{}) {
	response, _ := json.Marshal(payload)
	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(code)
	w.Write(response)
}

func handleRequests() {
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/items", returnAllItems)
	myRouter.HandleFunc("/items/{id}", returnSingleItem)
	log.Fatal(http.ListenAndServe(":8000", myRouter))
}

func main() {
	handleRequests()
}

http://localhost:8000/ → ホームページ、テキスト("This is the home page. Welcome!")を見せる

http://localhost:8000/items → "[{"title":"Item A","description":"The first item"},{"title":"Item B","description":"The second item"},{"title":"Item C","description":"The third item"}]"

http://localhost:8000/items/1 → {"title":"Item B","description":"The second item"}

なるほど、こんな感じで使うのね。

ーーーーー

jsonの使い方についても、Go言語でJSONを扱う を写経させて頂いた。

vro.json

[
    {"id":1, "name":"akane","birthday":"08-16","vivid_info":{"color":"red","weapon":"Rang"}},
    {"id":2, "name":"aoi","birthday":"06-17","vivid_info":{"color":"blue","weapon":"Impact"}},
    {"id":3, "name":"wakaba","birthday":"05-22","vivid_info":{"color":"green","weapon":"Blade"}},
    {"id":4, "name":"himawari","birthday":"07-23","vivid_info":{"color":"yellow","weapon":"Collider"}}, 
    {"id":0, "name":"rei"}    
]

vro.go

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
)

/** JSONデコード用の構造体定義 */

type Person struct {
	Id       int      `json:"id"`
	Name     string   `json:"name"`
	Birthday string   `json:"birthday"`
	Vivid    struct { // 構造体の中にネストさせて構造体を定義
		Color  string `json:color`
		Weapon string `json:weapon`
	} `json:"vivid_info"`
}

func main() {
	// JSONファイル読み込み
	bytes, err := ioutil.ReadFile("vro.json")
	if err != nil {
		log.Fatal(err)
	}
	// JSONデコード
	var persons []Person
	if err := json.Unmarshal(bytes, &persons); err != nil {
		log.Fatal(err)
	}
	// デコードしたデータを表示
	for _, p := range persons {
		fmt.Printf("%d : %s : (%s) [%s]\n", p.Id, p.Name, p.Vivid.Color, p.Birthday)
	}

	// JSONデコード
	var decode_data interface{}
	if err := json.Unmarshal(bytes, &decode_data); err != nil {
		log.Fatal(err)
	}
	// 表示
	for _, data := range decode_data.([]interface{}) {
		var d = data.(map[string]interface{})
		fmt.Printf("%d : %s\n", int(d["id"].(float64)), d["name"])
	}
}

2020/08,江端さんの技術メモ

  1. まずは、MSYS2をメンテナンス(2年近く放置していたから)
$ pacman -Syu

を、ターミナルを何度も立ち直し続けてパッケージの更新を繰返す。

$ pacman -S base-devel
$ pacman -S msys2-devel
$ pacman -S mingw-w64-x86_64-toolchain
$ pacman -S mingw-w64-x86_64-gnutls
$ pacman -S mingw-w64-x86_64-ruby
$ pacman -S nano      #(簡易エディター)
$ pacman -S make      #(make をインストール ※重要※)
$ pacman -S openssh   #(openssh をインストール)
$ pacman -S git       #(Git をインストール)
$ pacman -S ruby      #(Ruby をインストール)
$ pacman -S ruby-docs #(Rubyドキュメント をインストール)
$ pacman -S p7zip     #(7z をインストール)
$ pacman -S mingw-w64-x86_64-ag  #(ag 高速検索コマンドをインストール)

2. パッケージのインストールとアンインストール

sudo pacman -S [パッケージ名]
sudo pacman -R [パッケージ名]

3. Windows10の環境をMSYS2に引きつぐ方法

2020/08,江端さんの技術メモ



go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } log.Printf("recv: %s", message) } }() ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-done: return case t := <-ticker.C: err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) if err != nil {

go func()で、defer close(done)が効いてくるまで、 case <-done:はロックされて、デッドロックになるじゃないか? と、ずっと考えて訳が分からなくなってきたところで、Go言語でチャネルとselect というページに、

チャネルに値が入っていない場合、受信はブロックする。ブロックせずに処理を行いたい場合は select を使う。

そんなSwitchの使いかた、あるかーーーー! と、叫びそうになりました(私の2時間を返せ)

2020/08,江端さんの技術メモ

https://github.com/gorilla/websocket/tree/master/examples/echo

この"server.go"は、client.goからの通信だけではなく、ブラウザの画面も提供します(スゴい!)。

というか、Goプログラムの中に、html書けるなんて知らんかった。

 
// server.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore
// +build ignore

package main

import (
	"flag"
	"html/template"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var addr = flag.String("addr", "localhost:8080", "http service address")

var upgrader = websocket.Upgrader{} // use default options

func echo(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()
	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			break
		}
		log.Printf("recv: %s", message)
		err = c.WriteMessage(mt, message)
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}

func home(w http.ResponseWriter, r *http.Request) {
	homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}

func main() {
	flag.Parse()
	log.SetFlags(0)
	http.HandleFunc("/echo", echo)
	http.HandleFunc("/", home)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>  
window.addEventListener("load", function(evt) {

    var output = document.getElementById("output");
    var input = document.getElementById("input");
    var ws;

    var print = function(message) {
        var d = document.createElement("div");
        d.textContent = message;
        output.appendChild(d);
        output.scroll(0, output.scrollHeight);
    };

    document.getElementById("open").onclick = function(evt) {
        if (ws) {
            return false;
        }
        ws = new WebSocket("{{.}}");
        ws.onopen = function(evt) {
            print("OPEN");
        }
        ws.onclose = function(evt) {
            print("CLOSE");
            ws = null;
        }
        ws.onmessage = function(evt) {
            print("RESPONSE: " + evt.data);
        }
        ws.onerror = function(evt) {
            print("ERROR: " + evt.data);
        }
        return false;
    };

    document.getElementById("send").onclick = function(evt) {
        if (!ws) {
            return false;
        }
        print("SEND: " + input.value);
        ws.send(input.value);
        return false;
    };

    document.getElementById("close").onclick = function(evt) {
        if (!ws) {
            return false;
        }
        ws.close();
        return false;
    };

});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server, 
"Send" to send a message to the server and "Close" to close the connection. 
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output" style="max-height: 70vh;overflow-y: scroll;"></div>
</td></tr></table>
</body>
</html>
`))
// client.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore
// +build ignore

package main

import (
	"flag"
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/websocket"
)

var addr = flag.String("addr", "localhost:8080", "http service address")

func main() {
	flag.Parse()
	log.SetFlags(0)

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
	log.Printf("connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
			log.Printf("recv: %s", message)
		}
	}()

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-done:
			return
		case t := <-ticker.C:
			err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
			if err != nil {
				log.Println("write:", err)
				return
			}
		case <-interrupt:
			log.Println("interrupt")

			// Cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}
}

2020/08,江端さんの技術メモ

「特許明細書のロジック」の説明の仕方

「特許明細書のロジック」の説明の仕方

2010/09/17

1. 背景と目的

(1)後僚より、『知財担当者から、「特許明細書作成のロジックを説明しろ」と、言われたけど、なんのことか分からない』と相談された。

(2)そこで、江端が独断と偏見に基いて確立した「特許明細書のロジック」を開示する。

(3)なお、本内容は、知財担当者(弁理士を含む)にも開示したが、少なくともクレームは受けなかった(ように思う)。

従って、内容的には大きく外れてはいないのだろう、と考えている。

2. 知財担当者からの要請

知財担当者殿より、『「特許明細書のロジック」を説明する場合には、以下のように説明して欲しい』との要請を受けている。

====================================================================== 
先ほど、「背景技術→背景技術の問題点→本発明」と書きましたが、
詳細には、下記の点についてご説明頂きたくお願い致します。

(Step.1)発明のバックグラウンドとなる分野の説明
   	↓
(Step.2)その分野における一般的な課題の説明
    	↓
(Step.3)公知例がどのように上記課題を解決するかの説明
    	↓
(Step.4)その公知例でも解決できない課題の説明
    	↓
(Step.5)それをどう解決するかの説明(*本発明のロジック)

*本発明について、代表となる図を記載して頂けると助かります。
====================================================================== 

3. 江端の付帯説明

(面倒なので、以下メールより抜粋)

知財担当者殿のフローチャートの内容は完璧なので、そこに、私が具体例を書きます。

私はこういう風に書いてきて、発明検討会を何十回も突破して、(分割と共同を含めて)100本以上の明細書に関わってきたのですから、

先ずは、黙って、私を信じろ

(1)発明のバックグラウンドとなる分野の説明

 
  (ポイント:ここは「技術」を書くな)
 
   ○有線でやっていたメータリングを無線でやろうとする試みはあった。
   ○が、「メータおばさん」のコストの方が圧倒的に安かった。
   ○近年、無線のリソースが滅茶苦茶安くなってきた。
   ○「メータおばさん」の産業構造が崩れる可能性がでてきた。
   ○アドホックに関する技術も、かなり溜ってきた。
   ○実験ネットワークで、華々しい報告もある。
   ○太陽光発電とか、未知のシステム構成要素が入ってきている。

(2)その分野における一般的な課題の説明

 
   (ポイント:「技術」を書いても良いが、基本的には「金」「不安」で良い)

   ○事業的困難性
        インフラコストが高い、設置面倒、メンテ面倒、保守コスト試算困難、
        事業主体が不明瞭、金主が不明、ビジネスモデルが作れん

   ○技術的困難性
        無線に対する信頼性がない(本当にない)。実績がない。

   ○失敗時のインパクト
       課金不公平→暴動→政権倒れる→革命(というのは冗談だが)              
       深刻な社会不安、インフラに対する信頼性の下落

(3)公知例がどのように上記課題を解決するかの説明

 
   (ポイント:他人の文献公知発明を褒め称える)

    ○A社の特許文献1は、アドホックを自由に構築できる。
      上記の技術的困難性を見事に解決できる。素晴しい(と褒め称える)。

    ○B社の特許文献2は、無線をこんなに高信頼にして、社会インフラレベル
      にもっていける。
      上記の失敗時のインパクトを見事に回避できる。
      実に素晴しい(と褒め称える)。

    ○C社の非特許文献1は、これらを低コストで製品化している。誠に素晴
      しい(と褒めちぎる)
      上記の事業的困難性を解決できるではないか。
      涙が出そうな程、見事である(と褒め称える)。

【特許文献1】特開2007-278XXX号公報
【特許文献2】特開2002-132XXX号公報
【非特許文献1】「XXXXX」、Ebata Inc..[online][平成20年4月7日検索]、
                インターネット

(4)その公知例でも解決できない課題の説明

 
   (ポイント:褒め称えた発明を、掌を返して、鬼のように非難する)

    ○しかし、よくよく見るとA社の特許文献1は、×××はできないし、△△
      △はできない。カスな発明である(と、罵しる)

    ○また、B社の特許文献2は、★★★★★はできないし、■■■■■はでき
      ない。こんなものが現実世界で使える訳がない(と、嘲笑う)

    ○それに、C社の非特許文献1は、安いだけで、絶対必要となる○○○と
      いう機能がなく、お話になりゃしない(と、コケにする)

(5)それをどう解決するかの説明(*本発明のロジック)

 
   (ポイント:自分の発明を絶賛する(弱点については黙っている))

   ○ {俺の/あたしの}発明は、こういう内容だ(*本発明のロジック)。
      (ここで技術の話が始めて登場する)

   ○A社の特許文献1の問題点である、×××を解決し、△△△を可能とする。
      この発明は完璧だ(と、自己陶酔する)

   ○またB社の特許文献2の弱点である、★★★★★もできるし、■■■■■
     もできる。この発明は凄すぎる(と、自画自賛する)

   ○加えて、C社の非特許文献1が具備していない機能を、{俺の/あたしの}
     発明は持っていて、産業上の利用性も完璧!(と、誇大妄想する)

   ○以下、その理屈をお前達に、説明してやるから、実施例を見ろ!!!
     (以下実施例に続く)
とりあえず、こんな風に乱暴に理解して下さい。 ロジックとは、ようするに「物語」です。

(6)その他

江端が発明を評価する場合の評価方法は3つです。

(1)コスト(製造、メンテ、人材)が 1/10になる。

(2)性能(速度、メモリ、ハードディスク等)が10倍になる。

(3)上記(1)(2)に該当しなくても、(こじつけでも)「億円」の単位で儲かる。

これを、私は『特許発明 10分の1、10倍の法則』と名づけています。

『5%向上』程度なら出願やめとけ、と言っています。学生の研究ではないのだから。

また、ソフトウェアのアルゴリズムで、到底外部から発見できないようなものならやめとけ、とも。

書くだけ無駄です。侵害を立証できませんから。

# 山程書いてきた私が言っているのだから間違いない。

では、頑張って下さい。

以上