├── .github └── workflows │ └── main.yml ├── .golangci.yml ├── LICENSE ├── README.md ├── cmd └── mbtiles-server │ └── main.go ├── go.mod ├── go.sum ├── mbtiles.go ├── reader.go ├── reader_test.go ├── testdata └── openstreetmap.org.mbtiles ├── types.go ├── writer.go └── writer_test.go /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v* 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 14 | - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed 15 | - run: go build ./... 16 | - run: go test ./... 17 | lint: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 21 | - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed 22 | - uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 23 | with: 24 | version: v1.62.2 25 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | go: '1.23' 3 | 4 | linters: 5 | enable: 6 | - asasalint 7 | - asciicheck 8 | - bidichk 9 | - bodyclose 10 | - canonicalheader 11 | - containedctx 12 | - contextcheck 13 | - copyloopvar 14 | - decorder 15 | - dogsled 16 | - dupl 17 | - dupword 18 | - durationcheck 19 | - err113 20 | - errcheck 21 | - errchkjson 22 | - errname 23 | - errorlint 24 | - fatcontext 25 | - forbidigo 26 | - forcetypeassert 27 | - gci 28 | - gocheckcompilerdirectives 29 | - gochecksumtype 30 | - gocritic 31 | - godot 32 | - gofmt 33 | - gofumpt 34 | - goheader 35 | - goimports 36 | - gomoddirectives 37 | - gomodguard 38 | - goprintffuncname 39 | - gosimple 40 | - gosmopolitan 41 | - govet 42 | - grouper 43 | - iface 44 | - importas 45 | - inamedparam 46 | - ineffassign 47 | - interfacebloat 48 | - intrange 49 | - ireturn 50 | - loggercheck 51 | - makezero 52 | - mirror 53 | - misspell 54 | - musttag 55 | - nilerr 56 | - nolintlint 57 | - nosprintfhostport 58 | - perfsprint 59 | - prealloc 60 | - predeclared 61 | - promlinter 62 | - protogetter 63 | - reassign 64 | - revive 65 | - rowserrcheck 66 | - sloglint 67 | - spancheck 68 | - sqlclosecheck 69 | - staticcheck 70 | - stylecheck 71 | - tagalign 72 | - tenv 73 | - testableexamples 74 | - testifylint 75 | - thelper 76 | - typecheck 77 | - unconvert 78 | - unparam 79 | - unused 80 | - usestdlibvars 81 | - wastedassign 82 | - whitespace 83 | - zerologlint 84 | disable: 85 | - cyclop 86 | - depguard 87 | - exhaustive 88 | - exhaustruct 89 | - funlen 90 | - ginkgolinter 91 | - gochecknoglobals 92 | - gochecknoinits 93 | - gocognit 94 | - goconst 95 | - gocyclo 96 | - godox 97 | - gosec 98 | - lll 99 | - maintidx 100 | - nakedret 101 | - nestif 102 | - nilnil 103 | - nlreturn 104 | - noctx 105 | - nonamedreturns 106 | - paralleltest 107 | - recvcheck 108 | - tagliatelle 109 | - testpackage 110 | - tparallel 111 | - varnamelen 112 | - wrapcheck 113 | - wsl 114 | 115 | linters-settings: 116 | forbidigo: 117 | forbid: 118 | - ^archive/zip\. 119 | - ^compress/gzip\. 120 | - ^fmt\.Print.*$ 121 | - ^ioutil\..*$ 122 | - ^os\.(DirEntry|ErrExist|ErrNotExist|FileInfo|FileMode|Is.*|Mode.*)$ 123 | gci: 124 | sections: 125 | - standard 126 | - default 127 | - prefix(github.com/twpayne/go-mbtiles) 128 | gocritic: 129 | enable-all: true 130 | disabled-checks: 131 | - emptyFallthrough 132 | - hugeParam 133 | - rangeValCopy 134 | - unnamedResult 135 | - whyNoLint 136 | gofumpt: 137 | extra-rules: true 138 | module-path: github.com/twpayne/go-mbtiles 139 | goimports: 140 | local-prefixes: github.com/twpayne/go-mbtiles 141 | govet: 142 | disable: 143 | - fieldalignment 144 | - shadow 145 | enable-all: true 146 | misspell: 147 | locale: US 148 | ignore-words: 149 | - ackward 150 | stylecheck: 151 | checks: 152 | - all 153 | 154 | issues: 155 | include: 156 | - EXC0011 # include issues about comments from `stylecheck` 157 | exclude-rules: 158 | - linters: 159 | - err113 160 | text: do not define dynamic errors, use wrapped static errors instead 161 | - linters: 162 | - revive 163 | text: unused-parameter 164 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Tom Payne 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-mbtiles 2 | 3 | [](https://pkg.go.dev/github.com/twpayne/go-mbtiles) 4 | 5 | Package `mbtiles` reads and writes files in the [MBTiles 6 | format](https://github.com/mapbox/mbtiles-spec). 7 | 8 | ## Running the http server 9 | 10 | ``` 11 | go install github.com/twpayne/go-mbtiles/cmd/mbtiles-server 12 | go run github.com/twpayne/go-mbtiles/cmd/mbtiles-server -addr localhost:9091 -dsn ./testdata/openstreetmap.org.mbtiles 13 | ``` 14 | 15 | ## Reading mbtiles files 16 | 17 | MBTiles files are SQLite databases, and opened using a DSN string. 18 | 19 | Create a reader and read a tile with: 20 | 21 | ```golang 22 | reader, err := mbtiles.NewReader("./testdata/openstreetmap.org.mbtiles") 23 | if err != nil { 24 | panic(err) 25 | } 26 | tile, err := reader.SelectTile(0, 0, 0) 27 | if err != nil { 28 | if errors.Is(err, sql.ErrNoRows) { 29 | fmt.Printf("tile doesn't exist: %d, %d, %d\n", 0, 0, 0) 30 | return 31 | } else { 32 | panic(err) 33 | } 34 | } 35 | fmt.Printf("tile data: %+v\n", tile) 36 | ``` 37 | 38 | Note that SQLite will happily open a non-existent file to read without 39 | throwing an error. It is recommend that you check for existence of the file 40 | first, if you're using a file path as your DSN. For example: 41 | 42 | ```golang 43 | if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { 44 | panic(fmt.Sprintf("mbtiles file doesn't exist (%s): %v", filename, err)) 45 | } 46 | ``` 47 | 48 | The `Reader` type includes a `ServeHTTP` function. You can use it to create a 49 | reader for your mbtiles. 50 | 51 | ## Writing mbtiles files 52 | 53 | This package supports writing files in the [MBTiles 54 | v1.3](https://github.com/mapbox/mbtiles-spec/blob/master/1.3/spec.md) format. 55 | 56 | The library will not fill out all the required fields in the MBTiles 57 | specification, instead it provides the tools to be compliant. 58 | 59 | Of note: 60 | * The caller is responsible for populating the correct metadata into the 61 | metadata according to the spec. 62 | * The caller is responsible for gzip'ing the tile data before calling 63 | `InsertTile` or `BulkInsertTile`. The spec requires tiles to be compressed 64 | with gzip. How the caller implements the compression is outside the scope of 65 | this package. 66 | * For the `json` key in the metadata table, helper types are provided in this 67 | package as `mbtiles.MetadataJSON`. This type can be marshaled to a string 68 | and inserted into the metadata table for spec compliance for vector MBTiles 69 | files. 70 | * go-mbtiles will invert the Y coordinate to TMS to be compliant with the 71 | mbtiles spec. 72 | * go-mbtiles will create a metadata table if it doesn't exist, the first time 73 | `InsertMetadata` is called. 74 | * go-mbtiles will create a tiles table if it doesn't exist, the first time 75 | `InsertTile` or `BulkInsertTile` is called. 76 | 77 | 78 | ### Performance Tips 79 | 80 | MBTiles files are SQLite databases. To quickly bulk insert a large number 81 | of rows into the database, certain optimizations may be necessary to improve 82 | write performance. 83 | 84 | SQLite is a a single writer database, which means only one write can occur at 85 | a time. The database will otherwise be locked. Because of this, any write that 86 | is not in a transaction will automatically be wrapped in a transaction, which 87 | is slow. Consider the `BulkInsertTile` command, which will wrap all of the 88 | inserts in a single transaction. 89 | 90 | There are other optimizations exposed through the `Writer` interface. You 91 | should understand their implications for your use case before turning them on. 92 | * `JournalModeMemory` switches the journal mode from disk to memory. In bulk 93 | import scenarios, this is likely a very safe performance optimization to turn 94 | on. 95 | * `SynchronousOff` allows SQLite to continue processing as soon as data is 96 | handed off to the operating system to be written (instead of wait for 97 | confirmation that the write was successful). This is likely safe for bulk 98 | writes, but likely will result in a corrupted database if the process is 99 | interrupted or computer loses power. 100 | 101 | The performance improvements in go-mbtiles are motivated by the research in 102 | this [StackOverflow post about SQLite INSERT 103 | performance](https://stackoverflow.com/questions/1711631/improve-insert-per-second-performance-of-sqlite). 104 | 105 | ## License 106 | 107 | BSD-2-Clause in [LICENSE](./LICENSE). 108 | 109 | ## Contributors 110 | 111 | * Tom Payne (@twpayne) 112 | * Joe Polastre (@polastre), FlightAware 113 | -------------------------------------------------------------------------------- /cmd/mbtiles-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "html/template" 6 | "log" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | 11 | "github.com/gorilla/handlers" 12 | "github.com/gorilla/mux" 13 | _ "modernc.org/sqlite" // Register sqlite database driver. 14 | 15 | "github.com/twpayne/go-mbtiles" 16 | ) 17 | 18 | var indexHTML = template.Must(template.New("index.html").Parse(` 19 |
20 |