最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang實(shí)現(xiàn)webgis后端開(kāi)發(fā)的步驟詳解

 更新時(shí)間:2023年06月07日 09:18:13   作者:努力的悟空  
這篇文章主要介紹如何用golang結(jié)合postgis數(shù)據(jù)庫(kù),使用gin、grom框架實(shí)現(xiàn)后端的MVC的接口搭建,文中有詳細(xì)的流程步驟及代碼示例,需要的朋友可以參考下

前言

最近都在研究一門(mén)新語(yǔ)言golang,以及如何利用golang完成一個(gè)webgis后端平臺(tái)的開(kāi)發(fā)。go語(yǔ)言作為一門(mén)強(qiáng)類(lèi)型語(yǔ)言,在web后端開(kāi)發(fā)中目前有高性能、語(yǔ)法簡(jiǎn)潔、編譯速度快、自帶高并發(fā)的特性,它既沒(méi)有C/C++這樣有著復(fù)雜,冗余的語(yǔ)法,又擁有一些弱類(lèi)型語(yǔ)言的特性,比如自帶垃圾回收機(jī)制,自帶變量類(lèi)型判斷,關(guān)系是golang的打包是所有語(yǔ)言中最為先進(jìn)的,編譯器只會(huì)打包引入的代碼,而不是想java,python一樣會(huì)將導(dǎo)入的庫(kù)整體打包。同樣一個(gè)項(xiàng)目,用go打包出來(lái)可能只有幾M,而用python打包出來(lái)會(huì)有幾十上百兆。其運(yùn)行速度和Java不相上下,部分計(jì)算還快過(guò)java,但是占用的內(nèi)存比java小太多。不得不說(shuō)golang是一門(mén)偉大的語(yǔ)言,大名鼎鼎的容器dock,yarn都是go語(yǔ)言寫(xiě)出來(lái)的。

但是golang的缺點(diǎn)也很明顯,就是生態(tài)不夠完善,參考資料太少。很多功能都沒(méi)有現(xiàn)成的需要自己手寫(xiě)一一實(shí)現(xiàn),并且golang的各種類(lèi)庫(kù)的作者也是非常隨意,各種變量,函數(shù)想改就改,讓使用者門(mén)非??鄲?,只有按個(gè)看源碼來(lái)學(xué)習(xí)功能。

一、整體思路

一個(gè)健全的webgis后端必須實(shí)現(xiàn)以下幾個(gè)功能:

1、postgis數(shù)據(jù)庫(kù)和model的綁定。

2、如何將pg庫(kù)中的要素轉(zhuǎn)換為geojson

3、將前端傳入的geojson儲(chǔ)存到數(shù)據(jù)庫(kù)

4、動(dòng)態(tài)矢量瓦片的實(shí)現(xiàn)

5、實(shí)現(xiàn)地圖數(shù)據(jù)幾何分析功能

6、完成各類(lèi)復(fù)雜業(yè)務(wù)的分析模型

二、實(shí)現(xiàn)步驟

1.postgis數(shù)據(jù)庫(kù)和model的綁定

參考grom庫(kù)的官方文檔https://gorm.io/zh_CN/docs/create.html

grom是golang對(duì)數(shù)據(jù)庫(kù)實(shí)現(xiàn)orm操作的第三方庫(kù),在github上面獲得了廣泛的好評(píng),目前支持MySQL, PostgreSQL, SQLite, SQL Server 和 TiDB這幾種數(shù)據(jù)庫(kù)。

首先在項(xiàng)目中創(chuàng)建一個(gè)model的包,并在這個(gè)包中定義你需要的數(shù)據(jù)庫(kù)映射表。我們?cè)O(shè)計(jì)了幾個(gè)字段,其中g(shù)eom為幾何要素字段,MultiPolygon為幾何要素類(lèi)型,4326為坐標(biāo)系

package models
type MyTable struct {
	ID   uint   `gorm:"primary_key"`
	Name string `gorm:"type:varchar(255)"`
	Bh   string `gorm:"type:varchar(255)"`
	Geom string `gorm:"type:geometry(MultiPolygon,4326)"`
}

創(chuàng)建一個(gè)core.go 存儲(chǔ)數(shù)據(jù)庫(kù)鏈接信息,創(chuàng)建一個(gè)全局變量DB,注意在go中全局變量首字母名必須為大寫(xiě)

package models
import (
	"fmt"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)
var DB *gorm.DB
var err error
func init() {
	dsn := "host=localhost user=postgres password=1 dbname=gotest port=5432 sslmode=disable TimeZone=Asia/Shanghai"
	DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}
}

2.將pg庫(kù)中的要素轉(zhuǎn)換為geojson

(1)幾何定義

在pg數(shù)據(jù)庫(kù)中,幾何信息都是通過(guò)wkb格式進(jìn)行存儲(chǔ),所以我們需要將wkb解析為golang中我們可以操作的幾何對(duì)象,那么在解析wkb之前我們需要定義好全部的幾何要素

1、定義點(diǎn)類(lèi)型,直接用兩位浮點(diǎn)切片定義,如果需要z值就三位,我這里目前只需要二維數(shù)據(jù)就定義的2維。

type Point [2]float64

2、定義線類(lèi)型,線類(lèi)型由點(diǎn)類(lèi)型組成

type LineString []Point

3、定義環(huán)類(lèi)型,該類(lèi)型主要在存在環(huán)島面的時(shí)候使用,環(huán)類(lèi)型的特點(diǎn)就是起點(diǎn)和終點(diǎn)坐標(biāo)一致

type Ring LineString

4、定義單面類(lèi)型,該面由環(huán)類(lèi)型切片構(gòu)成,我們還可以給在該類(lèi)型中定義一些簡(jiǎn)單的幾何函數(shù)

type Polygon []Ring
//判斷兩個(gè)面是否相等
func (p Polygon) Equal(polygon Polygon) bool {
	if len(p) != len(polygon) {
		return false
	}
	for i := range p {
		if !p[i].Equal(polygon[i]) {
			return false
		}
	}
	return true
}
//復(fù)制面
func (p Polygon) Clone() Polygon {
	if p == nil {
		return p
	}
	np := make(Polygon, 0, len(p))
	for _, r := range p {
		np = append(np, r.Clone())
	}
	return np
}

5、定義聚合面MultiPolygon,該要素又面的切片構(gòu)成

type MultiPolygon []Polygon

6、做一個(gè)幾何類(lèi),整合所有幾何要素,在golang中這個(gè)被叫做接口

type Geometry interface {
	GeoJSONType() string
	Dimensions() int
	Bound() Bound
}

(2)將wkb解析為幾何類(lèi)型

wkb作為一種開(kāi)源的二進(jìn)制格式,存儲(chǔ)幾何信息具備高性能,占用空間小等特點(diǎn)。WKB編碼包括兩部分:類(lèi)型及坐標(biāo)信息, 類(lèi)型部分用一個(gè)字節(jié)表示,其中前四位表示幾何類(lèi)型,后四位表示SRID(空間參考系統(tǒng)編號(hào)),坐標(biāo)信息根據(jù)不同幾何類(lèi)型分別編碼,如點(diǎn)的坐標(biāo)用x,y兩個(gè)double類(lèi)型表示,線的坐標(biāo)是一系列的點(diǎn)坐標(biāo)等。以下代碼實(shí)現(xiàn)了將wkb轉(zhuǎn)換為上面定義的幾何要素。因?yàn)槊總€(gè)幾何類(lèi)型的解析方式不一樣這里我將每種方式單獨(dú)做成了函數(shù)。

func Unmarshal(data []byte) (Geometry, int, error) {
	order, typ, srid, geomData, err := unmarshalByteOrderType(data)
	if err != nil {
		return nil, 0, err
	}
	var g Geometry
	switch typ {
	case pointType:
		g, err = unmarshalPoint(order, geomData)
	case multiPointType:
		g, err = unmarshalMultiPoint(order, geomData)
	case lineStringType:
		g, err = unmarshalLineString(order, geomData)
	case multiLineStringType:
		g, err = unmarshalMultiLineString(order, geomData)
	case polygonType:
		g, err = unmarshalPolygon(order, geomData)
	case multiPolygonType:
		g, err = unmarshalMultiPolygon(order, geomData)
	case geometryCollectionType:
		g, _, err := NewDecoder(bytes.NewReader(data)).Decode()
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return nil, 0, ErrNotWKB
		}
		return g, srid, err
	default:
		return nil, 0, ErrUnsupportedGeometry
	}
	if err != nil {
		return nil, 0, err
	}
	return g, srid, nil
}

以下是解析MultiPolygon的代碼

func readMultiPolygon(r io.Reader, order byteOrder, buf []byte) (geo.MultiPolygon, error) {
	num, err := readUint32(r, order, buf[:4])
	if err != nil {
		return nil, err
	}
	alloc := num
	if alloc > MaxMultiAlloc {
		alloc = MaxMultiAlloc
	}
	result := make(orb.MultiPolygon, 0, alloc)
	for i := 0; i < int(num); i++ {
		pOrder, typ, _, err := readByteOrderType(r, buf)
		if err != nil {
			return nil, err
		}
		if typ != polygonType {
			return nil, errors.New("面要素錯(cuò)誤")
		}
		p, err := readPolygon(r, pOrder, buf)
		if err != nil {
			return nil, err
		}
		result = append(result, p)
	}
	return result, nil
}

(3)定義geojson類(lèi)型

先定義幾種基礎(chǔ)的結(jié)構(gòu)體

//定義Feature 結(jié)構(gòu)體
type Feature struct {
	ID         interface{}  `json:"id,omitempty"`
	Type       string       `json:"type"`
	BBox       BBox         `json:"bbox,omitempty"`
	Geometry   geo.Geometry `json:"geometry"`    //這里為上一步定義的幾何類(lèi)型
	Properties Properties   `json:"properties"` //這里為空map類(lèi)型
}
//定義FeatureCollection 結(jié)構(gòu)體
type FeatureCollection struct {
	Type     string     `json:"type"`
	BBox     BBox       `json:"bbox,omitempty"`
	Features []*Feature `json:"features"`
}

(4)數(shù)據(jù)轉(zhuǎn)換

先將grom查詢到的數(shù)據(jù)庫(kù)對(duì)象傳遞到該函數(shù),通過(guò)reflect映射字段,再將字段信息轉(zhuǎn)換為properties的map對(duì)象,最后組裝geojson返回

func Makegeojson(myTables []models.MyTable) interface{} {
	var FeaturesList []*geojson.Feature
	FeaturesList = []*geojson.Feature{}
	for _, t := range myTables {
		properties := make(map[string]interface{})
		v := reflect.ValueOf(t)
		tt := reflect.TypeOf(t)
		for i := 0; i < v.NumField(); i++ {
			if tt.Field(i).Name != "Geom" {
				properties[strings.ToLower(tt.Field(i).Name)] = v.Field(i).Interface()
			}
		}
		wkbBytes, _ := hex.DecodeString(strings.Trim(t.Geom, "  "))
		geom, _ := wkb.Unmarshal(wkbBytes)
		feature := geojson.NewFeature(geom)
		feature.Properties = properties
		FeaturesList = append(FeaturesList, feature)
	}
	features := geojson.NewFeatureCollection()
	features.Features = FeaturesList
	GeoJSON, _ := json.Marshal(features)
	var obj interface{}
	json.Unmarshal(GeoJSON, &obj)
	return obj
}

(5)數(shù)據(jù)返回

type UserController struct{}
func (uc *UserController) OutGeo(c *gin.Context) {
	name := c.PostForm("name")
	var mytable []models.MyTable
	DB := models.DB
	DB.Where("Name = ?", name).Find(&mytable)
	data := methods.Makegeojson(mytable)
	c.JSON(http.StatusOK, data)
}

通過(guò)postman調(diào)接口,數(shù)據(jù)完美返回geojson

2.前端傳入的geojson儲(chǔ)存到數(shù)據(jù)庫(kù)

這一步其實(shí)和取是一樣的,只需要把思路反過(guò)來(lái),將geojson解析為我們定義的幾何結(jié)構(gòu),然后再將幾何結(jié)構(gòu)解析成wkb。直接上代碼。

幾何要素轉(zhuǎn)換為wkb

func Marshal(geom geo.Geometry, byteOrder ...binary.ByteOrder) ([]byte, error) {
	buf := bytes.NewBuffer(make([]byte, 0, wkbcommon.GeomLength(geom, false)))
	e := NewEncoder(buf)
	if len(byteOrder) > 0 {
		e.SetByteOrder(byteOrder[0])
	}
	err := e.Encode(geom)
	if err != nil {
		return nil, err
	}
	if buf.Len() == 0 {
		return nil, nil
	}
	return buf.Bytes(), nil
}
func GeoJsonToWKB(geo geojson.Feature) string {
	TempWkb, _ := wkb.Marshal(geo.Geometry)
	WkbHex := hex.EncodeToString(TempWkb)
	return WkbHex
}

完成數(shù)據(jù)存儲(chǔ)

func (uc *UserController) InGeo(c *gin.Context) {
	var jsonData geojson.FeatureCollection
	c.BindJSON(&jsonData)
	DB := models.DB
	for _, t := range jsonData.Features {
		wkb_result := methods.GeoJsonToWKB(*t)
		DB.Model(models.MyTable{}).Create(map[string]interface{}{
			"Bh":   t.Properties["bh"],
			"Name": t.Properties["name"],
			"geom": clause.Expr{SQL: "ST_GeomFromWKB(decode(?, 'hex'))", Vars: []interface{}{wkb_result}},
		})
	}
	c.JSON(http.StatusOK, "ok")
}

至于更新功能也是一樣的,幾何更新只需要更新geom字段就行了。

3、其他功能實(shí)現(xiàn)

動(dòng)態(tài)矢量瓦片直接用go語(yǔ)言重寫(xiě)我之前博客用python做的那部分即可,至于復(fù)雜的地理數(shù)據(jù)分析可以采用postgis函數(shù)實(shí)現(xiàn),復(fù)雜的業(yè)務(wù)分析模塊可以直接使用golang調(diào)用fme實(shí)現(xiàn),這里就不過(guò)多介紹,后期博客會(huì)更新相關(guān)內(nèi)容。

總結(jié)

golang實(shí)在是太COOL了,語(yǔ)法簡(jiǎn)潔,運(yùn)行高效,部署簡(jiǎn)單,打包完美,我愿稱之為python之后最好用的語(yǔ)言,唯一的缺陷就是生態(tài)還有所欠缺,不過(guò)隨著開(kāi)發(fā)者們的擁護(hù),我相信golang會(huì)有光明的未來(lái)。

以上就是golang實(shí)現(xiàn)webgis后端開(kāi)發(fā)的步驟詳解的詳細(xì)內(nèi)容,更多關(guān)于golang實(shí)現(xiàn)webgis后端開(kāi)發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 十個(gè)Go map面試??紗?wèn)題合集

    十個(gè)Go map面試??紗?wèn)題合集

    go面試中,map相關(guān)知識(shí)點(diǎn)問(wèn)的比較多,這篇文章主要為大家整理歸納了10個(gè)??嫉膯?wèn)題,文中的示例代碼講解詳細(xì),希望對(duì)大家有一定的幫助
    2023-07-07
  • Go?error的使用方式詳解

    Go?error的使用方式詳解

    當(dāng)我們需要在Go項(xiàng)目中設(shè)計(jì)error,就不得不先知道Go error幾種常用方法,今天通過(guò)本文給大家介紹Go?error的使用方式詳解,感興趣的朋友一起看看吧
    2022-05-05
  • Go語(yǔ)言中new()和 make()的區(qū)別詳解

    Go語(yǔ)言中new()和 make()的區(qū)別詳解

    這篇文章主要介紹了Go語(yǔ)言中new()和 make()的區(qū)別詳解,本文講解了new 的主要特性、make 的主要特性,并對(duì)它們的區(qū)別做了總結(jié),需要的朋友可以參考下
    2014-10-10
  • 詳解如何讓Go語(yǔ)言中的反射加快

    詳解如何讓Go語(yǔ)言中的反射加快

    這篇文章主要為大家詳細(xì)介紹了如何讓Go語(yǔ)言中的反射加快的方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定幫助,需要的可以參考一下
    2022-08-08
  • 詳解go中的defer鏈如何被遍歷執(zhí)行

    詳解go中的defer鏈如何被遍歷執(zhí)行

    為了在退出函數(shù)前執(zhí)行一些資源清理的操作,例如關(guān)閉文件、釋放連接、釋放鎖資源等,會(huì)在函數(shù)里寫(xiě)上多個(gè)defer語(yǔ)句,多個(gè)_defer 結(jié)構(gòu)體形成一個(gè)鏈表,G 結(jié)構(gòu)體中某個(gè)字段指向此鏈表,那么go中的defer鏈如何被遍歷執(zhí)行,本文將給大家詳細(xì)的介紹,感興趣的朋友可以參考下
    2024-01-01
  • Nunu快速構(gòu)建高效可靠Go應(yīng)用腳手架使用詳解

    Nunu快速構(gòu)建高效可靠Go應(yīng)用腳手架使用詳解

    這篇文章主要為大家介紹了如何使用Nunu快速構(gòu)建高效可靠Go應(yīng)用腳手架詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Go語(yǔ)言實(shí)現(xiàn)MapReduce的示例代碼

    Go語(yǔ)言實(shí)現(xiàn)MapReduce的示例代碼

    MapReduce是一種備受歡迎的編程模型,它最初由Google開(kāi)發(fā),用于并行處理大規(guī)模數(shù)據(jù)以提取有價(jià)值的信息,本文將使用GO語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的MapReduce,需要的可以參考下
    2023-10-10
  • Golang cron 定時(shí)器和定時(shí)任務(wù)的使用場(chǎng)景

    Golang cron 定時(shí)器和定時(shí)任務(wù)的使用場(chǎng)景

    Ticker是一個(gè)周期觸發(fā)定時(shí)的計(jì)時(shí)器,它會(huì)按照一個(gè)時(shí)間間隔往channel發(fā)送系統(tǒng)當(dāng)前時(shí)間,而channel的接收者可以以固定的時(shí)間間隔從channel中讀取事件,這篇文章主要介紹了Golang cron 定時(shí)器和定時(shí)任務(wù),需要的朋友可以參考下
    2022-09-09
  • 在Go中實(shí)現(xiàn)高效可靠的鏈路追蹤系統(tǒng)

    在Go中實(shí)現(xiàn)高效可靠的鏈路追蹤系統(tǒng)

    在當(dāng)今互聯(lián)網(wǎng)應(yīng)用的架構(gòu)中,分布式系統(tǒng)已經(jīng)成為主流,分布式系統(tǒng)的優(yōu)勢(shì)在于能夠提供高可用性、高并發(fā)性和可擴(kuò)展性,本文將介紹鏈路追蹤的概念和原理,并重點(diǎn)介紹如何在Golang中實(shí)現(xiàn)高效可靠的鏈路追蹤系統(tǒng),需要的朋友可以參考下
    2023-10-10
  • 深入理解Golang之http server的實(shí)現(xiàn)

    深入理解Golang之http server的實(shí)現(xiàn)

    這篇文章主要介紹了深入理解Golang之http server的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論

阳原县| 惠安县| 军事| 临洮县| 五华县| 马山县| 灌云县| 寿阳县| 海口市| 闻喜县| 安仁县| 元朗区| 进贤县| 榆林市| 清流县| 高陵县| 木兰县| 恩平市| 都兰县| 营山县| 玛纳斯县| 青田县| 天门市| 敦煌市| 廊坊市| 玉树县| 名山县| 青海省| 盈江县| 拉萨市| 柘荣县| 綦江县| 阳西县| 汽车| 普兰县| 静宁县| 噶尔县| 馆陶县| 东丰县| 班戈县| 齐齐哈尔市|