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 }