위치 정보는 어색하지 않다 (GAE/Go 실천 편)
지난번 에 이어 위치정보를 GAE/Go로 해보고 싶습니다.
Why GAE/Go?
내가 멋지기 때문입니다.
라는 것은 절반 농담으로. Google 영업 중 한 명
GAE/Go를 사용하고 싶어서 GCP나 Golang을 사용하자는 안건이 실제로 늘고 있습니다.
싸고 빠르고 좋기 때문에 실제로.
그렇기 때문에, Golang 사용의 냄새로서는, GAE/Go 사용할 수 없으면 안 된다고 하는 것입니다.
정책
아티팩트
여기 입니다.
"폐하가 발견한 너구리 을 볼 수 있는 모바일 앱"같은 녀석(누득)을 상정해 백엔드 API를 썼습니다.
아래에서 시작합니다.
# ローカルサーバー起動
dev_appserver.py --port=8081 --admin_port=8001 server/app.yaml
# テストデータ投入(初回のみ)
go run tool/post.go 20
가정하는 사용법
# 結果を見る
curl http://localhost:8081/?lt=35.6949464269382,139.7493553161621&rt=35.688299,139.759852&rb=35.676834,139.758911&lb=35.682482,139.745092&lv=16
결과는 GeoJson을 반환합니다. 이것을 geojson.io에 복사 한 결과는 다음과 같습니다.
입니다. 좋은 감기에 검색할 수 있네요.
포인트
모바일 앱을 가정하면 지정된 범위가 직사각형이 아닙니다.
GoogleMap등이라면, 화면상이 북과는 한하지 않습니다.
그래, 자신이 향하고 있는 분을 화면상에 하는 모드라든지, Google Earth 같은 대각선 위에서 본 화면이군요.
이를 예측하여 네 모퉁이의 정보를 GET 매개 변수에 포함시키려고합니다.
화면상이 북이라고 결정할 수 있는 경우는, 북동과 남서의 위도 경도만으로도 괜찮을 것입니다.
지정된 범위에서 검색에 사용할 Quadkey를 추정해 봅시다.
func EstimateTiles(r orb.Ring, ilv int) []QuadkeyTile {
b := r.Bound()
lv := maptile.Zoom(ilv)
minTile := maptile.At(b.Min, lv)
maxTile := maptile.At(b.Max, lv)
// 指定レベル内でtileがひとつだけ(指定レベルが範囲に対して比較的小さい)場合は
// 範囲内のタイルを総なめする価値がないのですぐ返す
if reflect.DeepEqual(minTile, maxTile) {
return []QuadkeyTile{{Tile: minTile}}
}
res := []QuadkeyTile{}
minX := float64(minTile.X)
minY := float64(minTile.Y)
maxX := float64(maxTile.X)
maxY := float64(maxTile.Y)
// 範囲内のタイルを総なめしてQuadkey文字列を取り出す
for x := math.Min(minX, maxX); x <= math.Max(minX, maxX); x++ {
for y := math.Min(minY, maxY); y <= math.Max(minY, maxY); y++ {
tile := QuadkeyTile{Tile: maptile.New(uint32(x), uint32(y), lv)}
// 四隅が指定範囲に含まれているかチェック
if !tile.IsContained(r) {
continue
}
res = append(res, tile)
}
}
return res
}
github.com/paulmach/orb
에서 Quadkey를 출력하기 위해 필요한 maptile.Tile
는, 소지도를 구분해 X,Y 좌표로 표현한 대물입니다.지정 범위를 알면
foreach
에서 빙빙 돌리는 것도 간단!예상 Quadkey로 Datastore 검색
func fetchTanukis(ctx context.Context, tiles []QuadkeyTile, r orb.Ring) ([]*entity.Tanuki, error) {
g := goon.FromContext(ctx)
eg := errgroup.Group{}
mu := new(sync.Mutex)
res := []*entity.Tanuki{}
resMap := map[string]bool{}
f := func(t QuadkeyTile) func() error {
return func() error {
qk1, qk2 := t.FixQuadkey()
q := datastore.NewQuery(g.Kind(entity.Tanuki{})).
Filter("Quadkey20 >=", qk1).
Filter("Quadkey20 <", qk2)
entities := []*entity.Tanuki{}
if _, err := g.GetAll(q, &entities); err != nil {
return err
}
mu.Lock()
defer mu.Unlock()
for _, e := range entities {
if resMap[e.ID] {
continue
}
//指定範囲外のたぬきは結果に含めない
if !planar.RingContains(r, orb.Point{e.Geo.Lng, e.Geo.Lat}) {
continue
}
res = append(res, e)
}
return nil
}
}
for _, t := range tiles {
eg.Go(f(t))
}
err := eg.Wait()
return res, err
}
func (t QuadkeyTile) FixQuadkey() (string, string) {
org := t.QuadkeyString()
if 20 < len(org) {
return "", ""
}
lv := len(org)
// Quadkey Query Filter のMax値に利用するため、最後の数字に+1する。
lastString := org[lv-1 : lv]
lastInt, _ := strconv.Atoi(lastString)
lastInt++
res2 := org[0:lv-1] + strconv.Itoa(lastInt)
diff := 20 - lv
for i := 0; i < diff; i++ {
org += "0"
res2 += "0"
}
return org, res2
}
포인트는
datastore.NewQuery(g.Kind(entity.Tanuki{})).Filter("Quadkey20 >=", qk1).Filter("Quadkey20 <", qk2)
.Datastore는 Like 검색은 할 수 없다. 하지만 정방향 일치 검색만 가능 이다.
이것이 Quadkey 검색과 궁합 최고.
미리 필요한 한 긴 Quadkey를 DB에 저장해 두고(이 예에서는 lv20), 검색하고 싶은 Quadkey의 엉덩이에
0
를 더한 후 전방 일치 검색하는 것이다!요약
헤이세이 타이전 폰포코 는 지브리 최고 걸작. 이론은 인정한다.
어쩌면 나중에 개정할 것입니다.
Happy GeoCoding!
Reference
이 문제에 관하여(위치 정보는 어색하지 않다 (GAE/Go 실천 편)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/tarokamikaze/items/4f5327bccc7b166397ec텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)