265 lines
6.1 KiB
Go
265 lines
6.1 KiB
Go
package mbtiles
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"mime"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.zhouxhere.com/zhouxhere/maptile/model"
|
|
"git.zhouxhere.com/zhouxhere/maptile/protobuf"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"github.com/pkg/errors"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
type MBTiles struct {
|
|
*sql.DB
|
|
}
|
|
|
|
func ReadMBTiles() (map[string]*MBTiles, error) {
|
|
filePath, err := filepath.Abs("mbtiles/")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
files, err := filepath.Glob(filePath + "/*.mbtiles")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mbtiles := make(map[string]*MBTiles)
|
|
for _, file := range files {
|
|
mbtile, err := New(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fileName := filepath.Base(file)
|
|
fileType := filepath.Ext(file)
|
|
mbtiles[strings.TrimSuffix(fileName, fileType)] = mbtile
|
|
}
|
|
return mbtiles, nil
|
|
}
|
|
|
|
func New(name string) (*MBTiles, error) {
|
|
db, err := sql.Open("sqlite3", name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// defer db.Close()
|
|
return &MBTiles{
|
|
db,
|
|
}, nil
|
|
}
|
|
|
|
func (m *MBTiles) Close() {
|
|
m.DB.Close()
|
|
}
|
|
|
|
func (m *MBTiles) GetMetadata() (map[string]string, error) {
|
|
rows, err := m.Query("SELECT name, value FROM metadata")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
meta := make(map[string]string)
|
|
for rows.Next() {
|
|
var name, value string
|
|
err = rows.Scan(&name, &value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta[name] = value
|
|
}
|
|
|
|
return meta, nil
|
|
}
|
|
|
|
func (m *MBTiles) GetTileJSON() (*model.TileJSON, error) {
|
|
metaData, err := m.GetMetadata()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tileJSON := &model.TileJSON{
|
|
TileJSON: "3.0.0",
|
|
// Tiles: []string{fmt.Sprint("http://localhost:8080/api/v1/mbtiles/%d/{z}/{x}/{y}",)},
|
|
}
|
|
|
|
for name, value := range metaData {
|
|
switch name {
|
|
case "name":
|
|
tileJSON.Name = value
|
|
case "description":
|
|
tileJSON.Description = value
|
|
case "version":
|
|
tileJSON.Version = parseVersion(value)
|
|
case "maxzoom":
|
|
zoom, _ := strconv.Atoi(value)
|
|
tileJSON.MaxZoom = &zoom
|
|
case "minzoom":
|
|
zoom, _ := strconv.Atoi(value)
|
|
tileJSON.MinZoom = &zoom
|
|
case "fillzoom":
|
|
zoom, _ := strconv.Atoi(value)
|
|
tileJSON.FillZoom = &zoom
|
|
case "format":
|
|
tileJSON.Format = value
|
|
case "bounds":
|
|
bounds := strings.Split(value, ",")
|
|
minX, _ := strconv.ParseFloat(bounds[0], 64)
|
|
minY, _ := strconv.ParseFloat(bounds[1], 64)
|
|
maxX, _ := strconv.ParseFloat(bounds[2], 64)
|
|
maxY, _ := strconv.ParseFloat(bounds[3], 64)
|
|
tileJSON.Bounds = []float64{minX, minY, maxX, maxY}
|
|
case "center":
|
|
center := strings.Split(value, ",")
|
|
x, _ := strconv.ParseFloat(center[0], 64)
|
|
y, _ := strconv.ParseFloat(center[1], 64)
|
|
zoom, _ := strconv.Atoi(center[2])
|
|
tileJSON.Center = []float64{x, y, float64(zoom)}
|
|
case "json":
|
|
var layerData map[string]interface{}
|
|
err := json.Unmarshal([]byte(value), &layerData)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
vectorLayersData, ok := layerData["vector_layers"].([]interface{})
|
|
if !ok {
|
|
// fmt.Println("Error asserting vector_layers to []interface{}")
|
|
continue
|
|
}
|
|
|
|
vectorLayers := make([]model.VectorLayer, len(vectorLayersData))
|
|
for i, layerData := range vectorLayersData {
|
|
layerMap, ok := layerData.(map[string]interface{})
|
|
if !ok {
|
|
// fmt.Println("Error asserting layer data to map[string]interface{}")
|
|
continue
|
|
}
|
|
|
|
layerJSON, err := json.Marshal(layerMap)
|
|
if err != nil {
|
|
// fmt.Println("Error marshalling layer data to JSON:", err)
|
|
continue
|
|
}
|
|
|
|
var vectorLayer model.VectorLayer
|
|
err = json.Unmarshal(layerJSON, &vectorLayer)
|
|
if err != nil {
|
|
// fmt.Println("Error unmarshalling layer data to VectorLayer:", err)
|
|
continue
|
|
}
|
|
|
|
vectorLayers[i] = vectorLayer
|
|
}
|
|
|
|
tileJSON.VectorLayers = vectorLayers
|
|
}
|
|
}
|
|
|
|
return tileJSON, nil
|
|
}
|
|
|
|
func (m *MBTiles) GetTile(z, x, y int) ([]byte, string, error) {
|
|
|
|
metaData, err := m.GetMetadata()
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
mineType := mime.TypeByExtension("." + metaData["format"])
|
|
|
|
realY := (1 << uint(z)) - y - 1
|
|
|
|
rows, err := m.Query("SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?", z, x, realY)
|
|
if err != nil {
|
|
return nil, mineType, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
if rows.Next() {
|
|
var tileData []byte
|
|
err = rows.Scan(&tileData)
|
|
if err != nil {
|
|
return nil, mineType, err
|
|
}
|
|
|
|
// return tileData, mineType, nil
|
|
|
|
// fmt.Println("Raw tileData:", tileData) // 打印原始 tileData
|
|
// fmt.Println("Raw tileData (hex):", hex.EncodeToString(tileData)) // 打印原始 tileData 的十六进制表示
|
|
|
|
// if tileData[0] == 0x1f && tileData[1] == 0x8b { // 检查 gzip 魔数
|
|
// reader, err := gzip.NewReader(bytes.NewReader(tileData))
|
|
// if err != nil {
|
|
// return nil, "", err
|
|
// }
|
|
// defer reader.Close()
|
|
|
|
// uncompressedData, err := io.ReadAll(reader)
|
|
// if err != nil {
|
|
// return nil, "", err
|
|
// }
|
|
|
|
// tileData = uncompressedData
|
|
// }
|
|
// tiles := &protobuf.Tile{Layers: []*protobuf.Tile_Layer{}}
|
|
|
|
// err = proto.Unmarshal(tileData, tiles)
|
|
// if err != nil {
|
|
// // fmt.Println("Error unmarshalling tileData:", err) // 打印解析错误
|
|
// return nil, "", err
|
|
// }
|
|
|
|
// fmt.Println("Parsed tileData:", tiles) // 打印解析后的 tileData
|
|
// if metaData["format"] == "pbf" {
|
|
// return tileData, "application/x-protobuf", nil
|
|
// }
|
|
|
|
return tileData, "application/x-protobuf", nil
|
|
}
|
|
|
|
blankTile := &protobuf.Tile{
|
|
Layers: []*protobuf.Tile_Layer{},
|
|
}
|
|
|
|
tileData, err := proto.Marshal(blankTile)
|
|
if err != nil {
|
|
return nil, "", errors.Wrap(err, "failed to marshal blank tile")
|
|
}
|
|
|
|
return tileData, "application/x-protobuf", nil
|
|
}
|
|
|
|
func parseVersion(version string) string {
|
|
// 将版本号字符串按点分割
|
|
parts := strings.SplitN(version, ".", 3)
|
|
|
|
// 初始化默认值
|
|
major, minor, patch := 0, 0, 0
|
|
|
|
// 解析每个部分并转换为整数
|
|
if len(parts) > 0 {
|
|
major, _ = strconv.Atoi(parts[0])
|
|
}
|
|
if len(parts) > 1 {
|
|
minor, _ = strconv.Atoi(parts[1])
|
|
}
|
|
if len(parts) > 2 {
|
|
patch, _ = strconv.Atoi(parts[2])
|
|
}
|
|
|
|
return fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
|
}
|
|
|
|
func invertYValue(zoom uint8, y uint32) uint32 {
|
|
return (1 << zoom) - 1 - y
|
|
}
|