golang實現shape屬性編輯工具

需求

  • 實現shape文件屬性列表展現
  • 導出shape屬性類表爲Excel
  • 經過關聯Excel表字段,新增shape字段

分析

  • 讀取shape文件:使用第三方包github.com/jonas-p/go-shp@v0.1.1
  • excel文件操做:使用第三方包github.com/tealeg/xlsx
  • 頁面使用HTML佈局:後端web框架使用gin,前端使用layerui

工程目錄結構

├─excel
├─html
├─shape
├─src
│ └─main
└─staticjavascript

  • excel目錄:須要關聯讀取的excel
  • html目錄:前端html頁面
  • shape目錄:須要操做的shape
  • src目錄:源文件
  • static目錄:靜態文件

代碼解讀

讀取shape

  • 讀取shape屬性表頭字段,主要代碼以下:
func GetCols(shapefile string) []shp.Field {
    shape, err := shp.Open(shapefile)
    if err != nil {
        fmt.Errorf("UpdateShape err: %v", err)
    }
    defer shape.Close()
    fields := shape.Fields()
    return fields
}
複製代碼
  • 讀取shape屬性表數據,主要代碼以下:
shape, err := shp.Open(filepath)
if err != nil {
    fmt.Errorf("UpdateShape err: %v", err)
}
defer shape.Close()
fields := shape.Fields()

results := make([]map[string]string, 0)
for shape.Next() {
    n, p := shape.Shape()
    fmt.Println(n, p, fields, startPageNumber)
    if n >= (startPageNumber-1) && n <= (endPageNumber-1) {
        var item map[string]string
        item = make(map[string]string)
        for k, f := range fields {
            val := shape.ReadAttribute(n, k)
            fmt.Println(n, p, k, f, val)
            name := string(f.Name[:])
            name = strings.Trim(strings.Replace(name, "\u0000", "", -1), " ")
            dec := mahonia.NewDecoder(Encoding)
            valutf8 := dec.ConvertString(val)
            item[name] = valutf8
        }
        results = append(results, item)
    }
    if n >= endPageNumber {
        break
    }
}
複製代碼

導出Excel

  • shape數據保存爲Excel文件,主要代碼以下:
// 讀取shape值
shape, err := shp.Open(ShapeFileName)
if err != nil {
    fmt.Errorf("UpdateShape err: %v", err)
}
defer shape.Close()
fields := shape.Fields()

// 新建excel文件
file := xlsx.NewFile()
sheet, _ := file.AddSheet("Sheet1")
// add Header
row := sheet.AddRow()
row.SetHeightCM(1) //設置每行的高度
for k, f := range fields {
    cell := row.AddCell()
    cell.Value = f.String()
    fmt.Println(k)
}

for shape.Next() {
    row := sheet.AddRow()
    row.SetHeightCM(1) //設置每行的高度
    n, p := shape.Shape()

    for k, f := range fields {
        val := shape.ReadAttribute(n, k)
        dec := mahonia.NewDecoder(Encoding)
        val = dec.ConvertString(val)

        fmt.Println(n, p, k, f, val)
        cell := row.AddCell()
        // 去除特殊字符
        getVal := strings.Replace(val, " ", "", -1)
        getVal = strings.Replace(strconv.Quote(getVal), "\x00", "", -1)
        getVal = strings.Replace(getVal, "\"", "", -1)
        getVal = strings.Replace(getVal, "\\x00", "", -1)
        cell.Value = getVal
    }

}
//保存excel文件
errXlsx := file.Save("file.xlsx")
複製代碼
  • 下載Excel文件html

    後端golang主要代碼:前端

func FileDownload(c *gin.Context, filename string) {
	c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", 		filename)) //fmt.Sprintf("attachment; filename=%s", filename)對下載的文件重命名
	c.Writer.Header().Add("Content-Type", "application/octet-stream")
	c.File(filename)
}
複製代碼

​ 前端js代碼java

$("#exports").click(function () {
    if(shapename == ""){
        layer.msg("請先選擇文件")
    }else{
        window.location.href = "/exports";
    }
});
複製代碼

字段關聯

  • 讀取Excel文件表頭字段
r.GET("/getExcelHead", func(context *gin.Context) {
    filename := context.Query("filename")
    fmt.Println("filename==" + filename)
    type Result struct {
        Code int               `json:"code"`
        Msg  string            `json:"msg"`
        Data map[string]string `json:"data"`
    }
    var data = make(map[string]string)
    data = readExcelHead(filename)
    var result Result
    result.Code = 0
    result.Msg = "success"
    result.Data = data
    context.JSON(http.StatusOK, result)
})
複製代碼
  • 讀取shape屬性表頭字段:如上GetCols函數git

  • Excel和shape字段關聯github

image-20191231111204206

  • shape新增字段和關聯屬性值Excel字段

image-20191231111255853

新增shape字段

  • 新增字段,主要代碼以下:
addFields := []shp.Field{}
dictValStr := "#"
for bindKey, bindVal := range obj.AddList {
    fmt.Println(bindKey, bindVal, bindVal["newFiledText"], bindVal["bindFiledText"])
    newFiledText := bindVal["newFiledText"]
    fmt.Println("newFiledText==" + newFiledText)
    addFields = append(addFields, shp.StringField(newFiledText, 64))
    dictValStr = dictValStr + bindVal["bindFiledText"] + "#"
}
...
複製代碼
  • 遍歷shape屬性,主要代碼以下:
shp.UpdateShape(shapeFile, addFields, func(shape *shp.Reader, shapeNew *shp.Writer) {
    fieldsObj := shape.Fields()
    for shape.Next() {
        n, p := shape.Shape()
        shapeNew.Write(p)
        dictKeyStr = "#"
        for key, val := range obj.JoinObj {
            fmt.Println(key, val)
            shpVal := shape.GetValue(key)
            getVal := strings.Replace(shpVal, " ", "", -1)
            getVal = strings.Replace(strconv.Quote(getVal), "\x00", "", -1)
            getVal = strings.Replace(getVal, "\"", "", -1)
            getVal = strings.Replace(getVal, "\\x00", "", -1)
            dictKeyStr = dictKeyStr + getVal + "#"
        }
        for k, f := range fieldsObj {
            val := shape.ReadAttribute(n, k)
            dec := mahonia.NewDecoder(Encoding)
            val = dec.ConvertString(val)
            shapeNew.WriteAttribute(n, k, val)
            fmt.Println(k, f)
        }
        teampBindField := dictValStr[1 : len(dictValStr)-1]
        bindFileds := strings.Split(teampBindField, "#")
        for idx := 0; idx < len(bindFileds); idx++ {
            bindFiled := bindFileds[idx]
            newVal := DictData[dictKeyStr][bindFiled]
            shapeNew.WriteAttribute(n, len(fieldsObj)+idx, newVal)
        }
    }
})
複製代碼

保存shape文件

  • 上面函數shp.UpdateShape 是在第三方go-shp 庫擴展而來,主要實現生成新臨時shape文件,並把這個臨時shape文件替換本來的文件。主要代碼以下:
func UpdateShape(src string, addFields []Field, callback func(shape *Reader, shapeNew *Writer)) {
	var dist string = time.Now().Format("teap_20060102150405") + ".shp"
	shape, err := Open(src)
	if err != nil {
		fmt.Errorf("UpdateShape err: %v", err)
	}
	defer shape.Close()

	// fields to write
	fields := shape.Fields()

	fields = append(fields, addFields...)

	//shapeNew, errNew := shp.Append("test.shp")
	shapeNew, errNew := Create(dist, POINT)
	if errNew != nil {
		fmt.Errorf("UpdateShape err: %v", errNew)
	}
	defer shapeNew.Close()
	shapeNew.SetFields(fields)
	//回調函數
	callback(shape, shapeNew)
	shape.Close()
	shapeNew.Close()
	distarr := strings.Split(dist, ".")
	distName := distarr[0]
	srcarr := strings.Split(src, ".")
	srcName := srcarr[0]
	if len(srcarr) > 2 { // 說明路徑中有多個.
		srcName = "."
		for i := 0; i < len(srcarr)-1; i++ {
			srcName = srcName + srcarr[i]
		}
	}
	shpSrc := srcName + ".shp"
	dbfSrc := srcName + ".dbf"
	shxSrc := srcName + ".shx"
	cpgSrc := srcName + ".cpg"
	shpDist := distName + ".shp"
	dbfDist := distName + ".dbf"
	shxDist := distName + ".shx"

	Copy(shpDist, shpSrc)
	Copy(dbfDist, dbfSrc)
	Copy(shxDist, shxSrc)
	//
	cpgFileWrite, err := os.Create(cpgSrc)
	cpgFileWrite.Write([]byte("UTF-8"))

	os.Remove(shpDist)
	os.Remove(dbfDist)
	os.Remove(shxDist)
}
複製代碼

文件拷貝

func Copy(src string, dist string) {
	//打開源文件
	fileRead, err := os.Open(src)
	//fileRead, err := os.Open("C:/itcase/test-視頻.avi")
	if err != nil {
		fmt.Println("Open err:", err)
		return
	}
	defer fileRead.Close()
	//建立目標文件
	fileWrite, err := os.Create(dist)
	if err != nil {
		fmt.Println("Create err:", err)
		return
	}
	defer fileWrite.Close()
	//info, _ := os.Stat(src) //Stat獲取文件屬性
	//filesize := info.Size()

	//從源文件獲取數據,放到緩衝區
	buf := make([]byte, 4096)
	//循環從源文件中獲取數據,所有寫到目標文件中
	for {
		n, err := fileRead.Read(buf)
		if err != nil && err == io.EOF {
			//getProgress(src, filesize, n)
			fmt.Printf("讀取完畢,n = d%\n:", n)
			return
		}
		fileWrite.Write(buf[:n]) //讀多少、寫多少
		//getProgress(src, filesize, n)
	}
}
複製代碼

工具操做

  • 啓動: 雙擊main.exe
  • 訪問:在瀏覽器中訪問http://localhost:8080/index/
  • 讀取shape列表:點擊左側文件列表中的shape文件,右側表格展現shape文件屬性列表。若是表格中文亂碼,請切換下拉列表編碼集

image-20191231112601233

  • 導出excel文件:點擊導出能導出表格數據

image-20191231112745716

  • 關聯新增:點擊關聯新增按鈕,在彈出頁面中,選擇要的excel文件,關聯shape和excel字段;添加新增字段和對應的綁定字段(即excel中的字段),最後點擊保存按鈕。在執行完畢後,刷新頁面讀取shape列表,檢查是否正確。

image-20191231112946743

工程地址

相關文章
相關標籤/搜索