ホームページ >バックエンド開発 >Golang >Go言語を使ってオーダーシステムのテイクアウト配達範囲機能を開発する方法

Go言語を使ってオーダーシステムのテイクアウト配達範囲機能を開発する方法

WBOY
WBOYオリジナル
2023-11-01 10:33:171039ブラウズ

Go言語を使ってオーダーシステムのテイクアウト配達範囲機能を開発する方法

テイクアウト ビジネスの発展に伴い、テイクアウトの配達範囲機能はテイクアウト注文システムの非常に重要な機能ポイントになりました。ユーザーのニーズを満たすために、多くのフードデリバリープラットフォームがそのような機能を提供するでしょう。では、Go 言語を使用してこの配信範囲関数を開発するにはどうすればよいでしょうか?この記事では、読者がこの関数の実装をよりよく理解し、習得できるように、このプロセスを詳細に紹介し、具体的なコード例を示します。

  1. 前提条件

開発を開始する前に、まずこの関数の要件と実装を理解する必要があります。具体的には:

  • 多角形のエリア、つまりテイクアウト配達のサービス範囲を指定する必要があります;
  • ユーザーが注文ページで住所を入力する際に​​、ユーザーの所在地に基づいて判断され、サービスの範囲内であるかどうかが注文を受けるかどうかを決定します。

この機能を実現するには、いくつかのツールとテクノロジーを使用する必要があります。

  • まず、マップ API サービスを使用してサービス スコープ データを取得する必要があります。ユーザーの位置に関する地理情報が必要です。
  • 2 番目に、ポリゴン アルゴリズム、つまりポイント イン ポリゴン アルゴリズムを使用して、測位ポイントがサービス範囲内にあるかどうかを判断する必要があります。
  • 最後に、注文システムで使用できるように、これらのツールをコード ライブラリにカプセル化する必要があります。
  1. 設計アイデア

この関数を実装する前に、いくつかの基本的なデータ構造とインターフェイスを定義する必要があります:

  • ポリゴン領域: 複数の地点の地理情報を格納する配列;
  • Point: 緯度と経度の情報を含む構造体;
  • Client request: ユーザーのアドレス情報が含まれます。

次に、次の設計アイデアに従ってこの機能を実装できます。

  • 地図 API サービスを使用して、ポリゴン エリアの地理情報を取得し、この情報を保存します配列内;
  • クライアント要求を解析し、クライアントの位置の地理情報を取得します。
  • ポリゴン アルゴリズムを使用して、クライアントの位置がサービス範囲内にあるかどうかを判断し、対応する応答を返します。結果。

Go 言語では、go-mapbox ライブラリを使用して地図 API サービスにアクセスできます。同時に、Go 言語の組み込み数学ライブラリを使用してポリゴン アルゴリズムを実装することもできます。具体的なコード実装は次のとおりです。

package main

import (
    "fmt"
    "math"
    
    "github.com/ustroetz/go-mapbox"
)

type Point struct {
    Lat float64
    Lng float64
}

type Polygon []Point

func (p Point) ToCoordinates() *mapbox.Coordinates {
    return &mapbox.Coordinates{
        Longitude: p.Lng,
        Latitude:  p.Lat,
    }
}

func ContainsPointInPolygon(point Point, polygon Polygon) bool {
    intersectCount := 0
    polygonLength := len(polygon)

    if polygonLength < 3 {
        return false
    }

    endPoint := Point{Lat: 9999.0, Lng: point.Lng}

    for i := 0; i < len(polygon); i++ {
        startPoint := polygon[i]
        nextPointIndex := (i + 1) % len(polygon)
        nextPoint := polygon[nextPointIndex]

        if startPoint.Lng == nextPoint.Lng && endPoint.Lng == startPoint.Lng && (point.Lng == startPoint.Lng && (point.Lat > startPoint.Lat) == (point.Lat < endPoint.Lat)) {
            return true
        }

        if point.Lng > math.Min(startPoint.Lng, nextPoint.Lng) && point.Lng <= math.Max(startPoint.Lng, nextPoint.Lng) {
            deltaLat := nextPoint.Lat - startPoint.Lat
            if deltaLat == 0 {
                continue
            }
            intersectLat := startPoint.Lat + (point.Lng-startPoint.Lng)*(nextPoint.Lat-startPoint.Lat)/(nextPoint.Lng-startPoint.Lng)
            if intersectLat == point.Lat {
                return true
            }
            if intersectLat > point.Lat {
                intersectCount++
            }
        }
    }

    return intersectCount%2 != 0
}

func InDeliveryArea(point Point, apiKey string) bool {
    client := mapbox.New(apiKey)

    // 可以使用自己的多边形坐标
    geojson, _, _ := client.MapMatching().GetMapMatching(
        []mapbox.Coordinates{
            *point.ToCoordinates(),
        },
           nil,
    )
    polygon := geojson.Features[0].Geometry.Coordinates[0].([]interface{})
    var polygonArray Polygon
    for _, item := range polygon {
        arr := item.([]interface{})
        p := Point{Lat: arr[1].(float64), Lng: arr[0].(float64)}
        polygonArray = append(polygonArray, p)
    }
    fmt.Println("多边形坐标: ", polygonArray)

    return ContainsPointInPolygon(point, polygonArray)
}

func main() {
    point := Point{
        Lat: 31.146922,
        Lng: 121.362282,
    }

    apiKey := "YOUR_ACCESS_TOKEN"

    result := InDeliveryArea(point, apiKey)

    fmt.Println("坐标是否在配送范围内:", result)
}

上記は、基本的な Go 言語実装コードの例です。このコードを実行する前に、まずマップ API バックグラウンドからアクセス トークンを取得する必要があります。 YOUR_ACCESS_TOKEN をトークンに置き換えるだけです。さらに、マップ API が提供するポリゴン クエリ インターフェイスに、対応する座標と関連パラメータを入力する必要もあります。上記のコードを実行すると、座標位置がサービス範囲内にあるかどうかを表すブール値を取得できます。

  1. 再利用可能なライブラリへのカプセル化

上記のサンプル コードは、テイクアウト注文システムのテイクアウト配達範囲機能を完成させるのに役立ちます。ただし、実際のアプリケーションでは、この機能は複数のページまたはモジュールで使用される場合があります。コードを繰り返し記述する手間を避けるために、コードを再利用可能なライブラリにカプセル化する必要があります。具体的には:

  • 上記の IndeliveryArea 関数を、外部から呼び出せる関数にカプセル化できます。
  • さらに、プログラムの堅牢性を確保するために、外部入力パラメーターをチェックおよび検証することもできます。

たとえば、コードを再構成して、多角形の取得と多角形内の点の判定の 2 つの操作を分離すると、その後の拡張も容易になります。

次は、Go 言語を再利用可能なライブラリにカプセル化するサンプル コードです:

package delivery

import (
    "fmt"
    "math"
    
    "github.com/ustroetz/go-mapbox"
)

type Point struct {
    Lat float64
    Lng float64
}

type Polygon []Point

type DeliveryArea struct {
    polygon Polygon
    client  *mapbox.Client
}

func NewDeliveryArea(apiKey string, polygonArray []Point) *DeliveryArea {
    client := mapbox.New(apiKey)

    var polygon Polygon
    for _, p := range polygonArray {
        polygon = append(polygon, p)
    }

    return &DeliveryArea{polygon: polygon, client: client}
}

func (p Point) ToCoordinates() *mapbox.Coordinates {
    return &mapbox.Coordinates{
        Longitude: p.Lng,
        Latitude:  p.Lat,
    }
}

func (d *DeliveryArea) containsPoint(point Point) bool {
    intersectCount := 0
    polygonLength := len(d.polygon)

    if polygonLength < 3 {
        return false
    }

    endPoint := Point{Lat: 9999.0, Lng: point.Lng}

    for i := 0; i < len(d.polygon); i++ {
        startPoint := d.polygon[i]
        nextPointIndex := (i + 1) % len(d.polygon)
        nextPoint := d.polygon[nextPointIndex]

        if startPoint.Lng == nextPoint.Lng && endPoint.Lng == startPoint.Lng && (point.Lng == startPoint.Lng && (point.Lat > startPoint.Lat) == (point.Lat < endPoint.Lat)) {
            return true
        }

        if point.Lng > math.Min(startPoint.Lng, nextPoint.Lng) && point.Lng <= math.Max(startPoint.Lng, nextPoint.Lng) {
            deltaLat := nextPoint.Lat - startPoint.Lat
            if deltaLat == 0 {
                continue
            }
            intersectLat := startPoint.Lat + (point.Lng-startPoint.Lng)*(nextPoint.Lat-startPoint.Lat)/(nextPoint.Lng-startPoint.Lng)
            if intersectLat == point.Lat {
                return true
            }
            if intersectLat > point.Lat {
                intersectCount++
            }
        }
    }

    return intersectCount%2 != 0
}

func (d *DeliveryArea) Contains(point Point) bool {
    resp, _, err := d.client.MapMatching().GetMapMatching(
        []mapbox.Coordinates{
            *point.ToCoordinates(),
        },
           nil,
    )
    if err != nil {
        fmt.Printf("MapMatching error: %s
", err)
        return false
    }
    geojson := resp.Features[0].Geometry.Coordinates[0].([]interface{})

    var polygonArray Polygon
    for _, item := range geojson {
        arr := item.([]interface{})
        p := Point{Lat: arr[1].(float64), Lng: arr[0].(float64)}
        polygonArray = append(polygonArray, p)
    }

    return d.containsPoint(point)
}

ここでは、ファクトリ パターンを使用して DeliveryArea 構造を作成します。内部ロジックは比較的明確であるため、保守が容易であることがわかります。以下は、上記のカプセル化されたライブラリを使用したサンプル コードです:

package main

import (
    "fmt"

    "github.com/username/repo_deliver_area/delivery"
)

func main() {
    polygonArray := []delivery.Point{
        {Lat: 31.23039, Lng: 121.4737},
        {Lat: 31.23886, Lng: 121.50016},
        {Lat: 31.19394, Lng: 121.5276},
        {Lat: 31.18667, Lng: 121.49978},
    }

    apiKey := "YOUR_ACCESS_TOKEN"

    deliveryArea := delivery.NewDeliveryArea(apiKey, polygonArray)

    point := delivery.Point{
        Lat: 31.146922,
        Lng: 121.362282,
    }

    result := deliveryArea.Contains(point)

    fmt.Println(result)
}

このコードを実行する前に、ライブラリ ファイルを指定された場所に配置し、インポート パスの username/repo_deliver_area# を置き換える必要があります。 # を入力し、マップ API のアクセス トークンを YOUR_ACCESS_TOKEN に置き換えます。最終出力は、座標の位置がサービス範囲内にあるかどうかを表すブール値になります。

以上がGo言語を使ってオーダーシステムのテイクアウト配達範囲機能を開発する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。