2021/01,江端さんの技術メモ

QGIS3でosmファイルを地図表示する方法

いつも、osmファイルをドラッグする場所を忘れるので、メモ

すると、こうなる。

以上

2021/01,江端さんの技術メモ

0.追記(江端が、今、必要な情報をトップに)

### STLって面倒(strcpyでええやん)

// added by Ebata 2021/01/04
#include "simple_udp.h"

// Ebata added simple_udp 2021/01/05                                                                                    
simple_udp udp0("192.168.0.8",12345);

// added by Ebata 2021/01/04
std::cout << "Ebata:User:  " << user.id() << "," << user.current_xy().x() << "," << user.current_xy().y() << std::endl;
std::stringstream ss;
ss << "Ebata:User:  " << user.id() << "," << user.current_xy().x() << "," << user.current_xy().y() << std::endl;

// simple_udp udp0("192.168.0.8",12345);
udp0.udp_send("hello!\n");

//udp0.udp_send(ss);  エラー
//udp0.udp_send(ss.c_str()); エラー
udp0.udp_send(ss.str());

1.背景

LinuxのDockerコンテナの中から、一方的に、ホストOSにデータを放り投げる為のUDPプログラムです。

1行程度のデータをコンテナの外に出したいだけなのに、なかなか、良い方法が見つからず、結局UDP socketを使ってしまいました(websocket用にサーバを作るのも大袈裟で面倒でした(3~4行程度で記述したかった))。

ちなみに、UDPは、相手がいようがいまいが、一方的に送信し、一方的に受信待ちができる、とても便利な通信方式です。当然、送達保証はありません。

2. 環境

私のホストOSのIPアドレスは、192.168.0.8、ポートは、12345 と適当に選びました。

普通DockerにはUDPのポートオープンの設定が必要だと思いますが、今回は、テストプログラムでたまたまpingとUDPが通ってしまったので、何もやっていません(運が良かっただけかもしれません)。

テスト用のUDPプログラムは、こちら「UDP送受信プログラム」を使いました(何でも残しておくものです)

3.ソースコード

まるまる、こちらをコピペさせて頂きました → 「メカトロ講座03 c++でudp通信をする方法」

一行でUDP送信ができるようにしたかったもので。

3.1 共通のヘッダファイル(simple_udp.h)

// g++ -std=c++11 としないと動かない(ことがある)

// simple_udp.h

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>  // "string.h"だと通らないことがあるので、注意のこと(私は、ここで2時間ほど嵌った)

class simple_udp{
  int sock;
  struct sockaddr_in addr;
public:
  simple_udp(std::string address, int port){
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(address.c_str());
    addr.sin_port = htons(port);
  }
  void udp_send(std::string word){
    sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
  }

  void udp_bind(){
    bind(sock, (const struct sockaddr *)&addr, sizeof(addr));

  }
  std::string udp_recv(){
            #define BUFFER_MAX 400
    char buf[BUFFER_MAX];
    memset(buf, 0, sizeof(buf));
    recv(sock, buf, sizeof(buf), 0);
    return std::string(buf);
  }
  void udp_recv(char *buf, int size){
    memset(buf, 0, size);
    recv(sock, buf, size, 0);
  }

  ~simple_udp(){
    close(sock);
  }
};

3.2 送信プログラム(udp_sendto.cpp)

// g++ udp_sendto.cpp -o udp_sendto で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)

#include <stdio.h>
#include <string.h>
#include "simple_udp.h"

simple_udp udp0("192.168.0.8",12345); // ホストOSのUDPの受信側

int main(int argc, char **argv){
  udp0.udp_send("hello!");
  return 0;
}

3.3 受信プログラム(udp_recvfrom.cpp)

// g++ udp_recvfrom.cpp -o udp_recvfrom で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
#include <stdio.h>
#include <string.h>
#include "simple_udp.h"
simple_udp udp0("0.0.0.0",12345);

int main(int argc, char **argv){
  udp0.udp_bind();
  while (1){
    std::string rdata=udp0.udp_recv();
    printf("recv:%s\n", rdata.c_str());
  }
  return 0;
}

4.Windows10でMinGWのgccを使ってる私の場合のケース

以下のように変更した

4.1 共通のヘッダファイル(simple_udp_win.h)

// simple_udp_win.h

/* Windows版 */
#include <stdio.h>
#include <sys/types.h>
#include <winsock2.h>
#include <ws2tcpip.h>
//#include <sys/socket.h>
//#include <netinet/in.h>
//#include <arpa/inet.h>
//#include <netdb.h>
#include <string>  // "string.h"だと通らないことがあるので、注意のこと(私は、ここで2時間ほど嵌った)
#include <unistd.h> // error: 'close' was not declared in this scope; did you mean 'fclose'?


class simple_udp{
  int sock;
  struct sockaddr_in addr;

  // Windows専用おまじない(ここから)
  WSADATA wsaData;
  // Windows専用おまじない(ここまで)	  

 public:
  simple_udp(std::string address, int port){

	// Windows専用おまじない(ここから)
	WSAStartup(MAKEWORD(2,0), &wsaData);
	// Windows専用おまじない(ここまで)	  
	  
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(address.c_str());
	addr.sin_port = htons(port);
  }
  void udp_send(std::string word){
	sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
  }
  
  void udp_bind(){
	bind(sock, (const struct sockaddr *)&addr, sizeof(addr));
  }
  std::string udp_recv(){
#define BUFFER_MAX 400
	char buf[BUFFER_MAX];
	memset(buf, 0, sizeof(buf));
	recv(sock, buf, sizeof(buf), 0);
	return std::string(buf);
  }
  
  void udp_recv(char *buf, int size){
	memset(buf, 0, size);
	recv(sock, buf, size, 0);
  }
  
  ~simple_udp(){
	close(sock);
  }
};

4.2 送信プログラム(udp_sendto_win.cpp)

// g++ udp_sendto_win.cpp -o udp_sendto_win で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
// g++ -g udp_sendto_win.cpp -o udp_sendto_win -lwsock32 -lws2_32
#include <stdio.h>
#include <string.h>
//#include "simple_udp.h"
#include "simple_udp_win.h"

//simple_udp udp0("192.168.0.8",12345); // ホストOSのUDPの受信側
//simple_udp udp0("0.0.0.0",12345); // ホストOSのUDPの受信側
simple_udp udp0("127.0.0.1",12345); // ホストOSのUDPの受信側

int main(int argc, char **argv){
  udp0.udp_send("hello!");
  return 0;  
}

4.3 受信プログラム(udp_recvfrom_win.cpp)

// g++ udp_recvfrom_win.cpp -o udp_recvfrom_win で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
// Windows の場合は、g++ -g udp_recvfrom_win.cpp -o udp_recvfrom_win -lwsock32 -lws2_32

#include <stdio.h>
#include <string.h>
#include "simple_udp.h"

//simple_udp udp0("0.0.0.0",12345); // ホストOSのUDPの受信側 simple_udp udp0("127.0.0.1",12345); int main(int argc, char **argv){ udp0.udp_bind(); while (1){ std::string rdata=udp0.udp_recv(); printf("recv:%s\n", rdata.c_str()); } return 0; }

2021/01,江端さんの技術メモ

1.前書き
TILE38のクライアントプログラムのサンプルを見たくて、色々探していたら、「TILE38紹介」を紹介するという、非常に良いページを見つけました。
私がプログラムを良く忘れるので、このページのプログラムをそのままコピペさせて頂いて、私の備忘録とさせて頂きます(私の環境では、コメントアウト等がちょっと違ったようです)

2. 前提
Tile38のサーバを立ち上げておきます。Dockerを使って立ち上げておくと簡単です。dockerを使わない方法は、https://github.com/tidwall/tile38 に記載があります。

docker pull tile38/tile38
docker run -p 9851:9851 tile38/tile38

3. サンプルプログラム

(1つ目)

package main

import (
        //"encoding/json"
        "fmt"
        "log"

        //"reflect"

        "github.com/garyburd/redigo/redis"
)

func main() {
        // db connect
        c, err := redis.Dial("tcp", ":9851")
        if err != nil {
                log.Fatalf("Could not connect: %v\n", err)
        }
        defer c.Close()

        // SET location
        ret, err := c.Do("SET", "location", "me", "POINT", 35.6581, 139.6975)
        if err != nil {
                log.Fatalf("Could not SET: %v\n", err)
        }
        fmt.Printf("SET ret:%s\n", ret)

        // GET location
        ret, err = c.Do("GET", "location", "me")
        if err != nil {
                log.Fatalf("Could not GET: %v\n", err)
        }
        fmt.Printf("GET ret:%s\n", ret)
}

(2つめ)

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/garyburd/redigo/redis"
)

type GeoJsonMember struct {
	Type           string          `json:"type"`
	CoordinatesRaw json.RawMessage `json:"coordinates,omitempty"`
	CoordinatesObj interface{}     `json:"-"`
}

type Point [2]float64

type LineString []Point

type Polygon []LineString

func (member *GeoJsonMember) String() string {
	return fmt.Sprintf("%s %v", member.Type, member.CoordinatesObj)
}

func (member *GeoJsonMember) setCoordinates() error {
	var coordinates interface{}
	switch member.Type {
	case "Point":
		coordinates = new(Point)
	case "LineString":
		coordinates = new(LineString)
	case "Polygon":
		coordinates = new(Polygon)
	default:
		return fmt.Errorf("Unknown type: %v", member.Type)
	}
	err := json.Unmarshal(member.CoordinatesRaw, &coordinates)
	if err != nil {
		return fmt.Errorf("json.Unmarshal error: %v", err)
	}
	member.CoordinatesObj = coordinates
	return nil
}

func unmarshalMultiResults(shapes []byte) ([]*GeoJsonMember, error) {
	var members []*GeoJsonMember
	err := json.Unmarshal(shapes, &members)
	if err != nil {
		return nil, fmt.Errorf("Unmarshal error: %v", err)
	}

	for i, member := range members {
		err := member.setCoordinates()
		if err != nil {
			return nil, fmt.Errorf("member[%v]:type:%v coordinates:%v err:%v\n", i, member.Type, member.CoordinatesRaw, err)
		}
	}
	return members, nil
}

func unmarshalSingleResult(shapes []byte) (*GeoJsonMember, error) {
	var member GeoJsonMember
	err := json.Unmarshal(shapes, &member)
	if err != nil {
		return nil, fmt.Errorf("Unmarshal error: %v", err)
	}

	err = member.setCoordinates()
	if err != nil {
		return nil, fmt.Errorf("type:%v coordinates:%v err:%v\n", member.Type, member.CoordinatesRaw, err)
	}
	return &member, nil
}

func main() {
	// db connect
	c, err := redis.Dial("tcp", ":9851")
	if err != nil {
		log.Fatalf("Could not connect: %v\n", err)
	}
	defer c.Close()

	// SET fleet
	for _, data := range [][]interface{}{
		{"fleet", "id1", "FIELD", "start", "123456", "FIELD", "end", "789012", "POINT", 35.6581, 139.6975},
		{"fleet", "id2", "OBJECT", `{"type":"Point","coordinates":[139.6975,35.6581]}`},
		{"fleet", "id3", "OBJECT", `{"type":"LineString","coordinates":[[139.6975,35.6581],[1,1],[2,2]]}`},
		{"fleet", "id4", "POINT", 35.6581, 139.6975},
	} {
		ret, err := c.Do("SET", data...)
		if err != nil {
			log.Fatalf("Could not SET: %v\n", err)
		}
		fmt.Printf("SET ret:%#s\n", ret)
	}

	// SCAN fleet
	results, err := redis.Values(c.Do("SCAN", "fleet"))
	if err != nil {
		log.Fatalf("Could not SCAN: %v\n", err)
	}

	var cursor int
	var members []interface{}
	_, err = redis.Scan(results, &cursor, &members)
	if err != nil {
		fmt.Printf("scan result error: %v", err)
		return
	}

	for len(members) > 0 {
		// pick up one record from results as []interface{}
		var object []interface{}
		members, err = redis.Scan(members, &object)
		if err != nil {
			fmt.Printf("scan record error: %v", err)
			return
		}
		// scan columns from one record -> [id,json],fields
		var id []byte
		var json []byte
		others, err := redis.Scan(object, &id, &json)
		if err != nil {
			fmt.Printf("scan columns error: %v", err)
			return
		}

		// unmarshal geojson string to struct
		gjm, err := unmarshalSingleResult(json)
		if err != nil {
			fmt.Printf("unmarshal json error: %v", err)
			return
		}
		fmt.Printf("id:%s  json:%s  others:%s\n", id, gjm, others)
	}
}

4. PruneMobileと組み合わせてみる
ランダムウォークさせている歩行者が、一定のエリアに入った時に、メッセージを送付する。

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

 

 

2021/01,江端さんの技術メモ

  1. 参考文献
    https://r9y9.github.io/blog/2014/03/22/cgo-tips/
    https://hawksnowlog.blogspot.com/2018/12/getting-started-with-cgo.html
  2. 取り敢えず、Goのプログラムの中からCの関数を使う方法
    // go run main.go
    
    package main
    
    /*
    #include <stdio.h>
    #include <stdlib.h>
    
    void myprint(char* s) {
    	printf("%s\n", s);
    }
    */
    import "C"
    
    import "unsafe"
    
    func main() {
    	cs := C.CString("Hello from stdio\n")
    	C.myprint(cs)
    	C.free(unsafe.Pointer(cs))
    }
    
    // output
    // Hello from stdio

    とか

    package main
    
    /*
    #include <math.h>
    double MyPow(double x, double y) {
      double ret = pow(x, y);
      return ret;
    }
    */
    import "C"
    import "fmt"
    
    func main() {
    	ret := C.MyPow(10, 2)
    	fmt.Println(ret)
    }
  3. 結構面倒くさい決まりごと
    (1)import "C" の前には空行を入れない

    // #include <stdio.h>
    // #include <errno.h>
    import "C"

    (2)C / C++ のコンパイラに渡すオプション(flag)を記述する

    // #cgo CFLAGS: -I/usr/local/lib
    import C

 

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

Tile38

Slack Channel Docker Ready

Tile38 is an open source (MIT licensed), in-memory geolocation data store, spatial index, and realtime geofence. It supports a variety of object types including lat/lon points, bounding boxes, XYZ tiles, Geohashes, and GeoJSON.

Tile38は、オープンソース(MITライセンス)で、インメモリのジオロケーションデータストア、空間インデックス、リアルタイムジオフェンスです。緯度/経度ポイント、バウンディングボックス、XYZタイル、Geohash、GeoJSONを含む様々なオブジェクトタイプをサポートしています。

This README is quick start document. You can find detailed documentation at https://tile38.com.

このREADMEはクイックスタートのドキュメントです。詳細なドキュメントはhttps://tile38.com.

Nearby Within Intersects Geofencing Roaming Geofences

Features

特徴

Components

  • tile38-server - The server → サーバ
  • tile38-cli - Command line interface tool → コマンドインターフェースツール
  • tile38-benchmark - Server benchmark tool → サーバベンチマークツール

Getting Started

はじめに

Getting Tile38

タイル38の取得

Perhaps the easiest way to get the latest Tile38 is to use one of the pre-built release binaries which are available for OSX, Linux, FreeBSD, and Windows. Instructions for using these binaries are on the GitHub releases page. → 最新のTile38を入手する最も簡単な方法は、OSX、Linux、FreeBSD、Windows用に提供されているビルド済みのリリースバイナリを使用することでしょう。これらのバイナリの使い方は、GitHub release pageにあります。

Docker

To run the latest stable version of Tile38:→Tile38の最新の安定版を実行する。

docker pull tile38/tile38
docker run -p 9851:9851 tile38/tile38

Visit the Tile38 hub page for more information. → 詳しくはTile38ハブページをご覧ください。

Homebrew (macOS)

Install Tile38 using Homebrew →Homebrew](https://brew.sh/)を使ってTile38をインストールします。

brew install tile38
tile38-server

Building Tile38

タイル38の構築

Tile38 can be compiled and used on Linux, OSX, Windows, FreeBSD, and probably others since the codebase is 100% Go. We support both 32 bit and 64 bit systems. Go must be installed on the build machine.

Tile38は、コードベースが100%Goなので、Linux、OSX、Windows、FreeBSD、そしておそらく他のOSでもコンパイルして使うことができます。32ビットと64ビットの両方をサポートしています。ビルドマシンにはGoがインストールされている必要があります。

To build everything simply:

すべてをシンプルに構築する為に

$ make

To test:

$ make test

Running

実行

For command line options invoke: コマンドラインオプションの場合は、次を呼び出します。

$ ./tile38-server -h

To run a single server:

$ ./tile38-server

# The tile38 shell connects to localhost:9851
$ ./tile38-cli
> help

Playing with Tile38

Basic operations: 基本操作:

$ ./tile38-cli

# add a couple of points named 'truck1' and 'truck2' to a collection named 'fleet'.
#「fleet」という名前のコレクションに「truck1」および「truck2」という名前のポイントをいくつか追加します。
> set fleet truck1 point 33.5123 -112.2693   # on the Loop 101 in Phoenix
> set fleet truck2 point 33.4626 -112.1695   # on the I-10 in Phoenix

# search the 'fleet' collection.
#「フリート」コレクションを検索します。
> scan fleet                                 # returns both trucks in 'fleet'
> nearby fleet point 33.462 -112.268 6000    # search 6 kilometers around a point. returns one truck.

# key value operations
# キー値操作
> get fleet truck1                           # returns 'truck1'
> del fleet truck2                           # deletes 'truck2'
> drop fleet                                 # removes all 

Tile38 has a ton of great commands. Tile38にはたくさんの[すばらしいコマンド](https://tile38.com/commands)があります。

Fields

フィールド

Fields are extra data that belongs to an object. A field is always a double precision floating point. There is no limit to the number of fields that an object can have.

フィールドは、オブジェクトに属する追加のデータです。 フィールドは常に倍精度浮動小数点です。 オブジェクトが持つことができるフィールドの数に制限はありません。

To set a field when setting an object: オブジェクトを設定するときにフィールドを設定するには:

> set fleet truck1 field speed 90 point 33.5123 -112.2693             
> set fleet truck1 field speed 90 field age 21 point 33.5123 -112.2693

To set a field when an object already exists: オブジェクトがすでに存在するときにフィールドを設定するには:

> fset fleet truck1 speed 90

Searching

検索

Tile38 has support to search for objects and points that are within or intersects other objects. All object types can be searched including Polygons, MultiPolygons, GeometryCollections, etc.

Tile38は、他のオブジェクト内または他のオブジェクトと交差するオブジェクトおよびポイントの検索をサポートしています。 Polygons、MultiPolygons、GeometryCollectionsなどを含むすべてのオブジェクトタイプを検索できます。

Search Within

Within

WITHIN searches a collection for objects that are fully contained inside a specified bounding area. WITHINは、指定された境界領域内に完全に含まれているオブジェクトをコレクションで検索します。

Search Intersects

Intersects

INTERSECTS searches a collection for objects that intersect a specified bounding area.

Search Nearby

Nearby

NEARBY searches a collection for objects that intersect a specified radius. INTERSECTSは、指定された境界領域と交差するオブジェクトをコレクションで検索します。

Search options

検索オプション

SPARSE - This option will distribute the results of a search evenly across the requested area.

** SPARSE **-このオプションは、検索結果を要求された領域全体に均等に分散します。

This is very helpful for example; when you have many (perhaps millions) of objects and do not want them all clustered together on a map. Sparse will limit the number of objects returned and provide them evenly distributed so that your map looks clean.

これは、非常に役立ちます。 多数(おそらく数百万)のオブジェクトがあり、それらすべてをマップ上にクラスター化したくない場合。 スパースは、返されるオブジェクトの数を制限し、マップがきれいに見えるようにそれらを均等に分散させます。

You can choose a value between 1 and 8. The value 1 will result in no more than 4 items. The value 8 will result in no more than 65536. 1=4, 2=16, 3=64, 4=256, 5=1024, 6=4098, 7=16384, 8=65536.

1から8までの値を選択できます。値1は、4つ以下のアイテムになります。 値8は、65536以下になります。* 1 = 4、2 = 16、3 = 64、4 = 256、5 = 1024、6 = 4098、7 = 16384、8 =65536。*

>

No SparsingSearch WithinSparse 1Search WithinSparse 2Search WithinSparse 3Search WithinSparse 4Search WithinSparse 5Search Within

Please note that the higher the sparse value, the slower the performance. Also, LIMIT and CURSOR are not available when using SPARSE.

スパース値が高いほど、パフォーマンスが低下することに注意してください。 また、SPARSEを使用している場合、LIMITとCURSORは使用できません。

WHERE - This option allows for filtering out results based on field values. For example
nearby fleet where speed 70 +inf point 33.462 -112.268 6000 will return only the objects in the 懼fleet懼 collection that are within the 6 km radius and have a field named speed that is greater than 70.

Multiple WHEREs are concatenated as and clauses. WHERE speed 70 +inf WHERE age -inf 24 would be interpreted as speed is over 70 and age is less than 24.

The default value for a field is always 0. Thus if you do a WHERE on the field speed and an object does not have that field set, the server will pretend that the object does and that the value is Zero.

WHERE - このオプションは、fieldの値に基づいて結果をフィルタリングすることができます。例えば
`nearby fleet where speed 70 +inf point 33.462 -112.268 6000````は、半径6km以内にあるオブジェクトのみを返します。<br><br>複数のWHEREは**and**句として連結されます。<br><br>フィールドのデフォルト値は常に0です。したがって、フィールドspeed` の WHERE を実行したときに、オブジェクトにそのフィールドが設定されていない場合、サーバーはオブジェクトが設定されていて、その値がゼロであるかのように振舞うことになります。

MATCH - MATCH is similar to WHERE except that it works on the object id instead of fields.
nearby fleet match truck* point 33.462 -112.268 6000 will return only the objects in the 懼fleet懼 collection that are within the 6 km radius and have an object id that starts with truck. There can be multiple MATCH options in a single search. The MATCH value is a simple glob pattern.

nearby fleet match truck* point 33.462 -112.268 6000は、半径6km以内にある懼fリート懼コレクションのオブジェクトのうち、かつtruckで始まるオブジェクトIDを持つものだけを返します。1つの検索で複数のMATCHオプションを指定することができます。MATCHの値は単純なglob patternです。

CURSOR - CURSOR is used to iterate though many objects from the search results. An iteration begins when the CURSOR is set to Zero or not included with the request, and completes when the cursor returned by the server is Zero.

CURSOR - CURSORは、検索結果から多くのオブジェクトを繰り返し処理するために使用されます。CURSORが0に設定されているか、リクエストに含まれていない場合には反復処理が開始され、サーバから返されたカーソルが0になった時点で終了します。

NOFIELDS - NOFIELDS tells the server that you do not want field values returned with the search results.

NOFIELDS - NOFIELDSは、検索結果で返されるフィールド値を望まないことをサーバーに伝えます。

LIMIT - LIMIT can be used to limit the number of objects returned for a single search request.

LIMIT - LIMITは、1つの検索リクエストに対して返されるオブジェクトの数を制限するために使用することができます。

Geofencing

ジオフェンシング

Geofence animation A geofence is a virtual boundary that can detect when an object enters or exits the area. This boundary can be a radius, bounding box, or a polygon. Tile38 can turn any standard search into a geofence monitor by adding the FENCE keyword to the search.

ジオフェンスは、物体がその領域に入ったり出たりしたときに検出できる仮想的な境界です。この境界は、半径、境界ボックス、または多角形にすることができます。Tile38は、検索にFENCEキーワードを追加することで、標準的な検索をジオフェンスモニタに変えることができます。

Tile38 also allows for Webhooks to be assigned to Geofences.

*Tile38では、ジオフェンスにWebhooksを割り当てることもできます。

 

A simple example:

簡単な例です。

> nearby fleet fence point 33.462 -112.268 6000

This command opens a geofence that monitors the 懼fleet懼 collection. The server will respond with:

このコマンドは 懼fリート懼 コレクションを監視するジオフェンスを開きます。サーバは以下のように応答します。

{"ok":true,"live":true}

And the connection will be kept open. If any object enters or exits the 6 km radius around 33.462,-112.268 the server will respond in realtime with a message such as:

また、接続はオープンに保たれます。33.462,-112.268` 付近の半径6kmの範囲に何か物体が出入りすると、サーバはリアルタイムで次のようなメッセージを返します。

{"command":"set","detect":"enter","id":"truck02","object":{"type":"Point","coordinates":[-112.2695,33.4626]}}

The server will notify the client if the command is del | set | drop.

サーバは commanddel | set | drop の場合にクライアントに通知する。

  • del notifies the client that an object has been deleted from the collection that is being fenced.
  • del` は、フェンシングされているコレクションからオブジェクトが削除されたことをクライアントに通知します。
  • drop notifies the client that the entire collection is dropped.
  • drop` はコレクション全体が削除されたことをクライアントに通知する。
  • set notifies the client that an object has been added or updated, and when it懼s position is detected by the fence.
  • set` はオブジェクトが追加または更新されたことをクライアントに通知し、フェンスによってその位置が検出されたときにそのことを通知します。

The detect may be one of the following values.

detect` は以下の値のいずれかである。

  • inside is when an object is inside the specified area.
  • inside` はオブジェクトが指定された領域の内側にあるときのことである。
  • outside is when an object is outside the specified area.
  • outside` は、オブジェクトが指定された領域の外にあるときのことである。
  • enter is when an object that was not previously in the fence has entered the area.
  • enter`は、それまで柵の中に**なかったオブジェクトがエリア内に入ってきたときのことである。
  • exit is when an object that was previously in the fence has exited the area.
  • exit`は、以前にフェンスの中にあった***オブジェクトがエリア外に出たときのことである。
  • cross is when an object that was not previously in the fence has entered and exited the area.
  • クロス`とは、それまで柵の中になかったオブジェクトがエリアに入ってきて**エリアから出てきたときのことである。

These can be used when establishing a geofence, to pre-filter responses. For instance, to limit responses to enter and exit detections:

これらは、ジオフェンスを確立する際に、応答を事前にフィルタリングするために使用することができます。例えば、enterexit の検出に対して応答を制限することができます。

> nearby fleet fence detect enter,exit point 33.462 -112.268 6000

Pub/sub channels

パブ/サブチャンネル

Tile38 supports delivering geofence notications over pub/sub channels.

Tile38は、パブ/サブチャンネル上のジオフェンス通知の配信をサポートしています。

To create a static geofence that sends notifications when a bus is within 200 meters of a point and sends to the busstop channel:

バスがポイントから200メートル以内にいるときに通知を送信し、busstopチャンネルに送信する静的ジオフェンスを作成します。

> setchan busstop nearby buses fence point 33.5123 -112.2693 200

Subscribe on the busstop channel:

バストップチャンネルを購読してください。

> subscribe busstop

Object types

オブジェクトの種類

All object types except for XYZ Tiles and QuadKeys can be stored in a collection. XYZ Tiles and QuadKeys are reserved for the SEARCH keyword only.

XYZ TilesとQuadKeyを除くすべてのオブジェクトタイプは、コレクションに格納することができます。XYZ TilesとQuadKeyは、SEARCHキーワードのみに予約されています。

Lat/lon point

ラット/ロングポイント

The most basic object type is a point that is composed of a latitude and a longitude. There is an optional z member that may be used for auxiliary data such as elevation or a timestamp.

最も基本的なオブジェクト型は緯度と経度からなる点である。オプションの z メンバがあり、標高やタイムスタンプなどの補助データに使用することができます。

set fleet truck1 point 33.5123 -112.2693     # plain lat/lon
set fleet truck1 point 33.5123 -112.2693 225 # lat/lon with z member

Bounding box

バウンディングボックス

A bounding box consists of two points. The first being the southwestern most point and the second is the northeastern most point.

バウンディングボックスは2つの点で構成されています。1つ目は最も南西の点で、2つ目は最も北東の点です。

set fleet truck1 bounds 30 -110 40 -100

Geohash

ジオハッシュ

A geohash is a string representation of a point. With the length of the string indicating the precision of the point.

geohash は、点を文字列で表現したものです。文字列の長さは点の精度を表します。

set fleet truck1 hash 9tbnthxzr # this would be equivalent to 'point 33.5123 -112.2693'

GeoJSON

GeoJSON is an industry standard format for representing a variety of object types including a point, multipoint, linestring, multilinestring, polygon, multipolygon, geometrycollection, feature, and featurecollection.

GeoJSON](https://tools.ietf.org/html/rfc7946)は、点、多点、線条、多線条、多角形、マルチポリゴン、ジオメトリコレクション、フィーチャ、フィーチャコレクションなど、様々なオブジェクトタイプを表現するための業界標準フォーマットです。

* All ignored members will not persist.

* すべての無視されたメンバーは持続しません。

Important to note that all coordinates are in Longitude, Latitude order.

すべての座標は経度、緯度の順になっていることに注意してください。

set city tempe object {"type":"Polygon","coordinates":[[[0,0],[10,10],[10,0],[0,0]]]}

XYZ Tile

XYZタイル

An XYZ tile is rectangle bounding area on earth that is represented by an X, Y coordinate and a Z (zoom) level.

XYZタイルとは、X,Y座標とZ(ズーム)レベルで表現された地球上の矩形領域のことです。

Check out maptiler.org for an interactive example.

インタラクティブな例は maptiler.org をご覧ください。

QuadKey

A QuadKey used the same coordinate system as an XYZ tile except that the string representation is a string characters composed of 0, 1, 2, or 3. For a detailed explanation checkout The Bing Maps Tile System.

QuadKey は XYZ タイルと同じ座標系を使用しますが、文字列表現は 0, 1, 2, 3 で構成される文字列です。詳しい説明は Bing Maps Tile System をご覧ください。

Network protocols

ネットワークプロトコル

It懼s recommended to use a client library or the Tile38 CLI, but there are times when only HTTP is available or when you need to test from a remote terminal. In those cases we provide an HTTP and telnet options.

クライアントライブラリ](#client-libraries)またはTile38 CLIを使用することをお勧めしますが、HTTPしか利用できない場合や、リモート端末からテストする必要がある場合があります。そのような場合には、HTTPとtelnetオプションを用意しています。

HTTP

One of the simplest ways to call a tile38 command is to use HTTP. From the command line you can use curl. For example:

tile38コマンドを呼び出す最も簡単な方法の1つは、HTTPを使用することです。コマンドラインからcurlを使用することができます。例えば、以下のようになります。

# call with request in the body
curl --data "set fleet truck3 point 33.4762 -112.10923" localhost:9851

# call with request in the url path
curl localhost:9851/set+fleet+truck3+point+33.4762+-112.10923

Websockets

ウェブソケット

Websockets can be used when you need to Geofence and keep the connection alive. It works just like the HTTP example above, with the exception that the connection stays alive and the data is sent from the server as text websocket messages.

Websocket は、ジオフェンスして接続を維持する必要がある場合に使用することができます。これは上記の HTTP の例と同じように動作しますが、接続が生きていることと、データがテキストの Websocket メッセージとしてサーバーから送信されることを除いては、動作しません。

Telnet

There is the option to use a plain telnet connection. The default output through telnet is RESP.

プレーンなtelnet接続を使用するオプションがあります。telnetによるデフォルトの出力はRESPです。

telnet localhost 9851
set fleet truck3 point 33.4762 -112.10923
+OK

The server will respond in JSON or RESP depending on which protocol is used when initiating the first command.

サーバは、最初のコマンドを開始するときに使用されるプロトコルに応じて、JSONまたはRESPで応答します。

  • HTTP and Websockets use JSON.
  • HTTPとWebsocketsはJSONを使用しています。
  • Telnet and RESP clients use RESP.
  • Telnet と RESP クライアントは RESP を使用します。

Client Libraries

クライアントライブラリ

Tile38 uses the Redis RESP protocol natively. Therefore most clients that support basic Redis commands will in turn support Tile38. Below are a few of the popular clients.

Tile38はRedis RESPプロトコルをネイティブに使用します。そのため、基本的なRedisコマンドをサポートしているほとんどのクライアントがTile38をサポートしています。以下に人気のあるクライアントをいくつか紹介します。

Contact

連絡先

Josh Baker [@tidwall](https://twitter.com/tidwall)

License

ライセンス

Tile38 source code is available under the MIT License.

Tile38のソースコードはMIT Licenseで公開されています。

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

キッチンの蛍光管を交換しても点灯しなくなったことから、どうやら安定器(スターター)が不調になってきているとアタリを付けました。

そもそも、この家を作った時には、LED電球という考え方すらなく、今のように蛍光管の代替となるLED直管ライトなどは、影も形もありませんでした。

で、この機会に、スターターをバイパス(無視)して、LED直管ライトを接続できるように、回路を変更してやろうと思ったのですが、そもそも、蛍光管も、LED直管ライトも、その仕組みがよく分っていません(ネットの情報も、これといったものが見つかりませんでした)。

で、今回、「オーム電機 LED蛍光灯 直管形LEDランプ G13 40形 昼光色 グロー専用 LDF40SS・D/17/23」を使って調べてみました。

https://www.youtube.com/watch?v=ne2NLdvOvSI
 

これで、ざっくりと結線のやり方が分かりました。

 

赤丸のところにマイナスドライバーを差し込むと、配線が外れました。

配線の番号はこのようになっています(ネットで仕様書を調べて、アタリを付けています)

これを、以下のように変更します。電子安定器に接続された結線は、すべて外して(あるいは切断して)下さい。

結線を外す前に配線の番号をタグ付けしておきます。

今回は、半田付けが難しかったので、こういう接合具を使って結線しました。外被を向いた動線を差し込んで、ペンチで金属部を押し潰します。

結線して、点灯を確認しました。

これで、安定器(スターター)は不要となりました。待機電力の消費もなくなるので、少しは節電になるでしょう。

注意事項:LED直管ライトの方向を逆にするとショートします。映像の中で見たように、逆方向は短絡しているからです。結線の中にヒューズを入れておくべきです(私も今、気がつきましたので、後日対応しておきます)。

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

「無理せんと、リバースプロキシ(nginx)使えば?」

で、nginxで、外向きにはhttps, 内側はhttpという nginx.confを作りました。

今回は、nginxがdockerのコンテナですでに稼動中のプログラムで、これを試してみました。

まず、docker-compose.ymlですが、まず、docker-compose.ymlと同じディレクトリに、algo.crtとalgo.keyを放り込んでおいて、portsとvolumesを以下のように変更します。

(前略)
nginx:
    image: nginx:1.15-alpine
    ports:
      - "80:80"    ←ここ      
      - "443:443"   ←ここ      
    links:
      - app:app
    volumes:
      - "./nginx.conf:/etc/nginx/conf.d/default.conf"
      - "./algo.crt:/etc/nginx/conf.d/algo.crt"  ←ここ  
      - "./algo.key:/etc/nginx/conf.d/algo.key"  ←ここ  

portsは、"443:80"と記載するのが正解じゃないかなーと思うのですが、上手く動かなかったので、理由は不明ですが上記のように記載しました。

最後の2行ですが、これはnginxのdockerのコンポーネントの中に強制的にリンクするものらしいのです。少なくとも、dockerのコンポーネントの中に、/etc/nginx/conf.d/ は存在しているようなので、もう適当に、そこにリンクを張ることにしました。(いや、"docker container exec -it nginx_1 sh "等で、コンテナに入って確認すれば良いですが、なんか上手く入れないことがありましてですね)

また、docker-compose.ymlと同じディレクトリに(最初から)入っている、nginx.confを以下のよう改造します。

server { 
 listen 80 default_server;  ←ここ   listen [::]:80 default_server;  ←ここ   return 301 https://$host$request_uri;  ←ここ   } server {  # listen 80 default_server; # listen [::]:80 default_server; listen 443; ←ここ server_name localhost; ←ここ ssl on; ←ここ ssl_certificate /etc/nginx/conf.d/algo.crt; ←ここ # https://wp.kobore.net/2020/09/post-1124/で作ったもの ssl_certificate_key /etc/nginx/conf.d/algo.key; ←ここ # 同上  client_max_body_size 1M; root /go/hitachi_ride_hailing_go/static; # これが良く分からんのだが (app_1の方のディレクトリみたいなんだよなぁ)  location / { index index.html index.htm; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; if (!-f $request_filename) { proxy_pass http://app:8000; } } location /ws { proxy_pass http://app:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Frame-Options SAMEORIGIN; # リバースプロキシするサーバーから応答が無くとも、接続を切断しない上限の秒数 # http://zaiste.net/2014/12/nginx_websockets_proxy_for_nodejs/ proxy_read_timeout 86400; } }

なんだか分からんけど、太線(←ここ)の所を直したら、https://で通信できるようになったみたいです。

(多分、静的なhtmlファイルの表示だけなら問題はないでしょう)。

あ、忘れていましたが、

JSONのデータを直接受けるhtmlファイルを作成している途中の件

の問題も発生するようなので、面倒なら、"Cross Domain - CORS"を起動しておくと良いでしょう(終了したら取り外しておかないと、Twitterで通信障害「"問題が発生しました" “やりなおす"」が発生するので注意のこと)

ただ、私が現状、https化を試みているプログラムは、まだ問題が残っていて、全部は動いていません。

 

build.9a271f4.js:23 WebSocket connection to 'ws://localhost/ws' failed: Error during WebSocket handshake: Unexpected response code: 301
value @ build.9a271f4.js:23

の方は直接、ws:// → wss://に 書き換えたりとかやっていんだけど、上手く動かないみたいです。

=======

翌日、動いていました。

こういう原因が分からない起動も、厄介です。

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

Dockerを使っていて、このメッセージにどれだけ苦しめられたことか分からない。

で、世界中の人も困っているようである。

コードにトレースログを仕込んでも、どうもコードではない部分で発生しているらしくて、手がつけられなかったが、調べている内に、こんなことが分かってきた。

  1. Dockerの中にデフォルトに入っているコンテナ間名前解決に関するアドレスらしい
  2. PostgreSQLに関する接続に関するところで発生るうことが多いらしい
  3. で、試しに、docker-compose.yml の"db:"に関わるところに、3行ほど追加してみた(最近濫用している、セキュリティ無視の接続の呪文)
    db:
    image: postgis-pgrouting:latest
    build:
    context: docker/postgis-pgrouting
    expose:
    - "5432"
    ports:
    - "15432:5432"
    volumes:
    - data
    - ./:/hitachi_ride_hailing_go
    environment:
    POSTGRES_HOST_AUTH_METHOD: 'trust'
    POSTGRES_PASSWORD: 'postgres'

そうしたら、いきなり、"dial tcp: lookup db on 127.0.0.11:53: no such host exit" が出てこなくなった。

理由は不明ですし、たまたまかもしれないけど、直ったので、それで良しとします。

 

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

2年前のコードをAWS Lightsailに乗せられないか、docker compose up -d  を試してみたところ、変なところでエラーが出てきた。

go build cmd/server/main.go

と打ち込んでみたら

というところで、引掛っていることが分かった。

原因をググってみた「メモリ不足」。

最低でも1G用意しろ、って書いてあった。

Amazon Lightsailの一番安いやつは、512MBだった。

まあ、今時、ノートPCだって、4G搭載しているからなー、これはないかなー 。

しかし、搭載を断念するのも残念だしなー、安全を考えると2GBは欲しいしなー ・・・でも、10ドル/月かぁ。

ちょっと考えよう。

Lightsail のインスタンスのスケールアップ

(後日談) 1MBのメモリを持つ、5$/月 の インスタンスにスケールアップして、docker compose up -d を通しました。

# いや、実装に成功していたラズパイのメモリが1GBだったので、多分大丈夫だろうと踏みました。

―― が、コンテナが起動していないので、ここからが勝負です。

 

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

地図データの入った PostgreSQL + postGIS サーバ を関係者に配るために、Docker containerのイメージを公開する(サーバ構築手順書を作成するのが面倒だから)

1.コンテナイメージの確認

C:\Users\ebata>docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6432e639f678 postgis-pgrouting:latest "docker-entrypoint.s…" 3 months ago Up 6 hours 0.0.0.0:15432->5432/tcp toyosu_db_1

2.コンテナイメージのパッキング(commit)

C:\Users\ebata>docker commit 6432e639f678 tommurphy/toyosu_db_1
sha256:c7a1a0537fbe24d0c1371264cbd83e620a09e2ec5c328b5fbb42efb0e9454f8e

3. dockerへのログイン

C:\Users\ebata>docker login -u tommurphy
Password:
Login Succeeded

4. 外部への公開

C:\Users\ebata>docker push tommurphy/toyosu_db_1
The push refers to repository [docker.io/tommurphy/toyosu_db_1]
fcc5c725337c: Layer already exists             

fcc5c725337c: Layer already exists
a3994f456ccb: Layer already exists
.....
latest: digest: sha256:27a46f90aa1c7c9f9b5fa92877d22156144b882ba15ba4e04fe0e4fe2cb4af24 size: 4497

5. 確認

https://hub.docker.com/にアクセス

5. 相手側への連絡

「dockerをインストールしたマシンで、"docker pull tommurphy/toyosu_db_1" とコマンド投入しろ」

「で、そのあと、$ docker run -it tommurphy/toyosu_db_1 bash とコマンド投入しろ」

 

と連絡する。

(とりあえず、Windows10で作ったイメージを、Amazon Lightsailの ubuntu20.04に入れることまでは成功した。動くかどうかは不明)

→ ダメ。データベースすっからかんのコンテナができた。もうちょっと嵌る必要があるなぁ

というか、そもそもDockerコンテナとはそういうものらしい("docker" "postgresql" "永続化"、あたりでググってみる)

現在、PostgreSQLをデータごとDockerコンテナ化するを読んでいる。