前置条件在开始开发之前,我们需要先了解一下这个功能的需求和实现方式。具体而言:
需要给出一个多边形区域,即外卖配送的服务范围;当用户在下单页面输入地址时,需要通过用户所在的定位,判断是否在服务范围之内,从而决定是否接单。为了实现这个功能,我们需要使用一些工具和技术:
首先,我们需要使用一个地图api服务,来获取我们需要的服务范围数据和用户所在位置的地理信息。其次,我们需要使用多边形算法,即点在多边形内算法,来判断定位点是否在服务范围内。最后,我们需要将这些工具封装成一个代码库,以便在点餐系统中使用。设计思路在实现这个功能之前,我们需要先定义一些基本的数据结构和接口:
多边形区域:一个数组,存储了多个点的地理信息;点:一个结构体,包含经纬度信息;客户端请求:包含用户地址信息。然后,我们可以按照以下的设计思路来实现这个功能:
使用一个地图api服务,获取多边形区域的地理信息,并将这些信息存储在一个数组中;解析客户端请求,获取客户端所在位置的地理信息;使用多边形算法,判断客户端位置是否在服务范围内,并给出相应的响应结果。在go语言中,我们可以使用go-mapbox库来访问地图api服务。同时,我们也可以使用go语言中内置的math库,来实现多边形算法。具体代码实现如下:
package mainimport ( "fmt" "math" "github.com/ustroetz/go-mapbox")type point struct { lat float64 lng float64}type polygon []pointfunc (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后台获取一个access token。将token替换 your_access_token即可。另外,还需要在地图api提供的多边形查询接口中输入对应的坐标和相关参数。运行以上代码,可以得到一个代表坐标所在位置是否在服务范围内的布尔值。
封装成为可复用库上述示例代码可以帮助我们完成外卖点餐系统的外卖配送范围功能。但是,在实际应用中,这个功能可能被多个页面或模块所使用。为了避免重复编写代码的麻烦,我们需要将其封装成为一个可复用的库。具体而言:
我们可以将上述的indeliveryarea函数封装成为一个可以从外部调用的函数。另外,我们还可以对外部输入的参数进行检查和校验,以保证程序的健壮性。例如,我们可以将代码重新组织,把获取多边形和判断点在多边形内两个操作分离,这样也方便后续扩展。
以下是go语言封装成为可复用库的示例代码:
package deliveryimport ( "fmt" "math" "github.com/ustroetz/go-mapbox")type point struct { lat float64 lng float64}type polygon []pointtype 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 mainimport ( "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)}
在运行这段代码之前,需要先将库文件放置到指定位置,并替换掉import路径中的username/repo_deliver_area,以及将地图api的access token替换掉 your_access_token。最终输出将代表坐标所在位置是否在服务范围内的布尔值。
以上就是如何利用go语言开发点餐系统的外卖配送范围功能的详细内容。