Skip to content

Commit 48b3f71

Browse files
authored
Support TIFFs with PlanarConfig=2 (#4)
For geotiffs created in GDAL with -co INTERLEAVE=BAND
1 parent efb73d9 commit 48b3f71

File tree

17 files changed

+417
-12
lines changed

17 files changed

+417
-12
lines changed

.github/workflows/test.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: build
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-20.04
12+
strategy:
13+
matrix:
14+
go: [ '1.16', '1.15', '1.14' ]
15+
name: golangci-lint
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v2
19+
- name: Setup Go
20+
uses: actions/setup-go@v2
21+
with:
22+
go-version: ${{ matrix.go }}
23+
- name: golangci-lint
24+
uses: reviewdog/action-golangci-lint@v1
25+
with:
26+
level: error
27+
fail_on_error: true
28+
- name: Coverage Tests
29+
run: go test . -cover -race -coverprofile=profile.cov
30+
- name: Send coverage
31+
if: ${{ matrix.go == '1.16' }}
32+
uses: shogo82148/actions-goveralls@v1
33+
with:
34+
path-to-profile: profile.cov

cog.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ type ifd struct {
111111

112112
ntags uint64
113113
ntilesx, ntilesy uint64
114+
nplanes uint64 //1 if PlanarConfiguration==1, SamplesPerPixel if PlanarConfiguration==2
114115
tagsSize uint64
115116
strileSize uint64
116117
r tiff.BReader
@@ -163,10 +164,11 @@ func (ifd *ifd) AddMask(msk *ifd) error {
163164
return nil
164165
}
165166

166-
func (ifd *ifd) structure(bigtiff bool) (tagCount, ifdSize, strileSize uint64) {
167+
func (ifd *ifd) structure(bigtiff bool) (tagCount, ifdSize, strileSize, planeCount uint64) {
167168
cnt := uint64(0)
168169
size := uint64(16) //8 for field count + 8 for next ifd offset
169170
tagSize := uint64(20)
171+
planeCount = 1
170172
if !bigtiff {
171173
size = 6 // 2 for field count + 4 for next ifd offset
172174
tagSize = 12
@@ -209,6 +211,9 @@ func (ifd *ifd) structure(bigtiff bool) (tagCount, ifdSize, strileSize uint64) {
209211
cnt++
210212
size += tagSize
211213
}
214+
if ifd.PlanarConfiguration == 2 {
215+
planeCount = uint64(ifd.SamplesPerPixel)
216+
}
212217
if len(ifd.DateTime) > 0 {
213218
cnt++
214219
size += arrayFieldSize(ifd.DateTime, bigtiff)
@@ -295,7 +300,7 @@ func (ifd *ifd) structure(bigtiff bool) (tagCount, ifdSize, strileSize uint64) {
295300
cnt++
296301
size += arrayFieldSize(ifd.RPCs, bigtiff)
297302
}
298-
return cnt, size, strileSize
303+
return cnt, size, strileSize, planeCount
299304
}
300305

301306
type tagData struct {
@@ -366,13 +371,14 @@ const (
366371
func (cog *cog) computeStructure() {
367372
ifd := cog.ifd
368373
for ifd != nil {
369-
ifd.ntags, ifd.tagsSize, ifd.strileSize = ifd.structure(cog.bigtiff)
374+
ifd.ntags, ifd.tagsSize, ifd.strileSize, ifd.nplanes = ifd.structure(cog.bigtiff)
370375
//ifd.ntilesx = uint64(math.Ceil(float64(ifd.ImageWidth) / float64(ifd.TileWidth)))
371376
//ifd.ntilesy = uint64(math.Ceil(float64(ifd.ImageLength) / float64(ifd.TileLength)))
372377
ifd.ntilesx = (ifd.ImageWidth + uint64(ifd.TileWidth) - 1) / uint64(ifd.TileWidth)
373378
ifd.ntilesy = (ifd.ImageLength + uint64(ifd.TileLength) - 1) / uint64(ifd.TileLength)
379+
374380
for _, mifd := range ifd.masks {
375-
mifd.ntags, mifd.tagsSize, mifd.strileSize = mifd.structure(cog.bigtiff)
381+
mifd.ntags, mifd.tagsSize, mifd.strileSize, mifd.nplanes = mifd.structure(cog.bigtiff)
376382
// mifd.ntilesx = uint64(math.Ceil(float64(mifd.ImageWidth) / float64(mifd.TileWidth)))
377383
// mifd.ntilesy = uint64(math.Ceil(float64(mifd.ImageLength) / float64(mifd.TileLength)))
378384
mifd.ntilesx = (mifd.ImageWidth + uint64(mifd.TileWidth) - 1) / uint64(mifd.TileWidth)
@@ -425,7 +431,7 @@ func (cog *cog) computeImageryOffsets() error {
425431
datas := cog.dataInterlacing()
426432
tiles := datas.tiles()
427433
for tile := range tiles {
428-
tileidx := tile.x + tile.y*tile.ifd.ntilesx
434+
tileidx := (tile.x+tile.y*tile.ifd.ntilesx)*tile.ifd.nplanes + tile.plane
429435
cnt := uint64(tile.ifd.TileByteCounts[tileidx])
430436
if cnt > 0 {
431437
if cog.bigtiff {
@@ -508,7 +514,7 @@ func (cog *cog) write(out io.Writer) error {
508514
buf := &bytes.Buffer{}
509515
for tile := range tiles {
510516
buf.Reset()
511-
idx := tile.x + tile.y*tile.ifd.ntilesx
517+
idx := (tile.x+tile.y*tile.ifd.ntilesx)*tile.ifd.nplanes + tile.plane
512518
if tile.ifd.TileByteCounts[idx] > 0 {
513519
_, err := tile.ifd.r.Seek(int64(tile.ifd.OriginalTileOffsets[idx]), io.SeekStart)
514520
if err != nil {
@@ -787,8 +793,9 @@ func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagDa
787793
}
788794

789795
type tile struct {
790-
ifd *ifd
791-
x, y uint64
796+
ifd *ifd
797+
x, y uint64
798+
plane uint64
792799
}
793800

794801
type datas [][]*ifd
@@ -822,10 +829,13 @@ func (d datas) tiles() chan tile {
822829
for y := uint64(0); y < ovr[0].ntilesy; y++ {
823830
for x := uint64(0); x < ovr[0].ntilesx; x++ {
824831
for _, ifd := range ovr {
825-
ch <- tile{
826-
ifd: ifd,
827-
x: x,
828-
y: y,
832+
for p := uint64(0); p < ifd.nplanes; p++ {
833+
ch <- tile{
834+
ifd: ifd,
835+
plane: p,
836+
x: x,
837+
y: y,
838+
}
829839
}
830840
}
831841
}

cogger_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package cogger
2+
3+
import (
4+
"bytes"
5+
"crypto/md5"
6+
"io"
7+
"os"
8+
"testing"
9+
)
10+
11+
func testCase(t *testing.T, filename string) {
12+
t.Helper()
13+
f, err := os.Open("testdata/cog_" + filename)
14+
if err != nil {
15+
t.Fatal(err)
16+
}
17+
hasher := md5.New()
18+
_, _ = io.Copy(hasher, f)
19+
srchash := hasher.Sum(nil)
20+
f.Close()
21+
f, err = os.Open("testdata/" + filename)
22+
if err != nil {
23+
t.Fatal(err)
24+
}
25+
defer f.Close()
26+
27+
_, _ = f.Seek(0, io.SeekStart)
28+
29+
buf := bytes.Buffer{}
30+
31+
hasher.Reset()
32+
_ = Rewrite(&buf, f)
33+
_, _ = io.Copy(hasher, &buf)
34+
35+
coghash := hasher.Sum(nil)
36+
37+
if !bytes.Equal(coghash, srchash) {
38+
t.Errorf("mismatch on %s: %x / %x", filename, srchash, coghash)
39+
}
40+
}
41+
42+
func TestCases(t *testing.T) {
43+
cases := []string{
44+
"band4mask.tif",
45+
"band4.tif",
46+
"graymask.tif",
47+
"gray.tif",
48+
"rgbmask.tif",
49+
"rgb.tif",
50+
}
51+
for i := range cases {
52+
testCase(t, cases[i])
53+
}
54+
}

loader.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ func loadIFD(r tiff.BReader, tifd tiff.IFD) (*ifd, error) {
103103
return ifd, nil
104104
}
105105

106+
// Rewrite reshuffles the tiff bytes provided as readers into a COG output
107+
// to out
106108
func Rewrite(out io.Writer, readers ...tiff.ReadAtReadSeeker) error {
107109
tiffs := []tiff.TIFF{}
108110
if len(readers) == 0 {

testdata/band4.tif

12.3 KB
Binary file not shown.

testdata/band4mask.tif

12.8 KB
Binary file not shown.

testdata/cog_band4.tif

9.3 KB
Binary file not shown.

testdata/cog_band4mask.tif

9.79 KB
Binary file not shown.

testdata/cog_gray.tif

2.4 KB
Binary file not shown.

testdata/cog_graymask.tif

2.88 KB
Binary file not shown.

0 commit comments

Comments
 (0)