위치 정보는 어색하지 않다 (GAE/Go 실천 편)

18916 단어 5GAEgeometrydatastore
안녕하세요. 만나서 반갑습니다. tarokamikaze입니다.
지난번 에 이어 위치정보를 GAE/Go로 해보고 싶습니다.

Why GAE/Go?



내가 멋지기 때문입니다.
라는 것은 절반 농담으로. Google 영업 중 한 명

GAE/Go를 사용하고 싶어서 GCP나 Golang을 사용하자는 안건이 실제로 늘고 있습니다.
싸고 빠르고 좋기 때문에 실제로.

그렇기 때문에, Golang 사용의 냄새로서는, GAE/Go 사용할 수 없으면 안 된다고 하는 것입니다.

정책


  • Server: GAE/Go1.11(standard)
  • DB: Datastore
  • Search API 등이라고 하는 그그라빌리티가 최악인 메인 DB와 이중 등록할 수 없는 것은 사용하지 않는다
  • Quadkey를 사용하여 쿼리가 빈약한 Datastore에서도 위치 정보 검색할 수 있는 곳을 보여준다!

  • 아티팩트



    여기 입니다.
    "폐하가 발견한 너구리 을 볼 수 있는 모바일 앱"같은 녀석(누득)을 상정해 백엔드 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
    
  • lt (left-top), rt, rb, lb에 쉼표로 구분 된 모바일 화면의 네 모서리 위도 경도.
  • lv는 검색하는 quadkey의 레벨. 모바일의 경우지도의 zoom level +2가 경험적으로 좋은 느낌

  • 결과는 GeoJson을 반환합니다. 이것을 geojson.io에 복사 한 결과는 다음과 같습니다.


  • 거친 사각형 : 요청 데이터 범위
  • 격자 모양의 사각형 : 검색에 사용 된 Quadkey 범위
  • 핀: 히트된 데이터의 위치 정보

  • 입니다. 좋은 감기에 검색할 수 있네요.

    포인트



    모바일 앱을 가정하면 지정된 범위가 직사각형이 아닙니다.



    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를 더한 후 전방 일치 검색하는 것이다!

    요약



  • 헤이세이 타이전 폰포코 는 지브리 최고 걸작. 이론은 인정한다.
  • GAE/Go + Datastore만으로도 위치 정보 검색은 낙승에서 구현할 수 있어요!
  • Quadkey 구현이나 무리하지 않고 거인에게 타자!
  • 음색을 타고 어드벤트 캘린더 한다든가 말하지 않으면 좋았다

  • 어쩌면 나중에 개정할 것입니다.
    Happy GeoCoding!

    좋은 웹페이지 즐겨찾기