From 1552153a2fde15d5a6adcf465ef347f106cacc22 Mon Sep 17 00:00:00 2001 From: Thomas Bonfort Date: Mon, 28 Jun 2021 18:35:52 +0200 Subject: [PATCH 1/4] add support for .tif.ovr external overviews --- cogger_test.go | 32 ++++++++----- loader.go | 94 +++++++++++++++---------------------- testdata/cog_ext_multi.tif | Bin 0 -> 4797 bytes testdata/cog_ext_ovr.tif | Bin 0 -> 4797 bytes testdata/exttest.tif | Bin 0 -> 2393 bytes testdata/exttest.tif.2 | Bin 0 -> 1299 bytes testdata/exttest.tif.4 | Bin 0 -> 889 bytes testdata/exttest.tif.ovr | Bin 0 -> 2204 bytes 8 files changed, 59 insertions(+), 67 deletions(-) create mode 100644 testdata/cog_ext_multi.tif create mode 100644 testdata/cog_ext_ovr.tif create mode 100644 testdata/exttest.tif create mode 100644 testdata/exttest.tif.2 create mode 100644 testdata/exttest.tif.4 create mode 100644 testdata/exttest.tif.ovr diff --git a/cogger_test.go b/cogger_test.go index d9eb19a..4977297 100644 --- a/cogger_test.go +++ b/cogger_test.go @@ -6,11 +6,13 @@ import ( "io" "os" "testing" + + "github.com/google/tiff" ) -func testCase(t *testing.T, filename string) { +func testCase(t *testing.T, expected_filename string, filenames ...string) { t.Helper() - f, err := os.Open("testdata/cog_" + filename) + f, err := os.Open("testdata/" + expected_filename) if err != nil { t.Fatal(err) } @@ -18,24 +20,27 @@ func testCase(t *testing.T, filename string) { _, _ = io.Copy(hasher, f) srchash := hasher.Sum(nil) f.Close() - f, err = os.Open("testdata/" + filename) - if err != nil { - t.Fatal(err) - } - defer f.Close() - _, _ = f.Seek(0, io.SeekStart) + files := make([]tiff.ReadAtReadSeeker, len(filenames)) + for i := range filenames { + f, err = os.Open("testdata/" + filenames[i]) + if err != nil { + t.Fatal(err) + } + defer f.Close() + files[i] = f + } buf := bytes.Buffer{} hasher.Reset() - _ = Rewrite(&buf, f) + _ = Rewrite(&buf, files...) _, _ = io.Copy(hasher, &buf) coghash := hasher.Sum(nil) if !bytes.Equal(coghash, srchash) { - t.Errorf("mismatch on %s: %x / %x", filename, srchash, coghash) + t.Errorf("mismatch on %v: %x / %x", filenames, srchash, coghash) } } @@ -49,6 +54,11 @@ func TestCases(t *testing.T) { "rgb.tif", } for i := range cases { - testCase(t, cases[i]) + testCase(t, "cog_"+cases[i], cases[i]) } } + +func TestMultiFiles(t *testing.T) { + testCase(t, "cog_ext_ovr.tif", "exttest.tif", "exttest.tif.ovr") + testCase(t, "cog_ext_multi.tif", "exttest.tif", "exttest.tif.2", "exttest.tif.4") +} diff --git a/loader.go b/loader.go index 5f759f2..98ac205 100644 --- a/loader.go +++ b/loader.go @@ -8,8 +8,7 @@ import ( "github.com/google/tiff" ) -func loadMultipleTIFFs(tifs []tiff.TIFF) (*cog, error) { - cog := new() +func loadMultipleTIFFs(tifs []tiff.TIFF) ([]*ifd, error) { ifds := make([]*ifd, 0) for it, tif := range tifs { tifds := tif.IFDs() @@ -18,41 +17,22 @@ func loadMultipleTIFFs(tifs []tiff.TIFF) (*cog, error) { if err != nil { return nil, err } - if ifd.SubfileType&subfileTypeReducedImage == subfileTypeReducedImage { - return nil, fmt.Errorf("cannot load multiple tifs if they contain overviews") - } if it != 0 { + //check that the additional files are smaller than the first, i.e. that they represent an overview + if ifd.ImageLength >= ifds[0].ImageLength || ifd.ImageWidth >= ifds[0].ImageWidth { + return nil, fmt.Errorf("provided tiff %d size %dx%d is larger than first tiff size %dx%d. when using multiple files, the subsequent ones must be overviews of the first one", + it, ifd.ImageWidth, ifd.ImageLength, ifds[0].ImageWidth, ifds[0].ImageLength) + } + //force to overview ifd.SubfileType |= subfileTypeReducedImage } ifds = append(ifds, ifd) } } - sort.Slice(ifds, func(i, j int) bool { - //return in order: fullres, fullresmasks, ovr1, ovr1masks, ovr2, .... - if ifds[i].ImageLength != ifds[j].ImageLength { - return ifds[i].ImageLength > ifds[j].ImageLength - } - return ifds[i].SubfileType < ifds[j].SubfileType - }) - if ifds[0].SubfileType != 0 { - return nil, fmt.Errorf("failed sort: first px=%d type=%d", ifds[0].ImageLength, ifds[0].SubfileType) - } - cog.ifd = ifds[0] - curOvr := cog.ifd - l := curOvr.ImageLength - for _, ci := range ifds[1:] { - if ci.ImageLength == l { - curOvr.AddMask(ci) - } else { - curOvr.AddOverview(ci) - curOvr = ci - l = curOvr.ImageLength - } - } - return cog, nil + return ifds, nil } -func loadSingleTIFF(tif tiff.TIFF) (*cog, error) { - cog := new() + +func loadSingleTIFF(tif tiff.TIFF) ([]*ifd, error) { tifds := tif.IFDs() ifds := make([]*ifd, len(tifds)) var err error @@ -62,29 +42,7 @@ func loadSingleTIFF(tif tiff.TIFF) (*cog, error) { return nil, err } } - sort.Slice(ifds, func(i, j int) bool { - //return in order: fullres, fullresmasks, ovr1, ovr1masks, ovr2, .... - if ifds[i].ImageLength != ifds[j].ImageLength { - return ifds[i].ImageLength > ifds[j].ImageLength - } - return ifds[i].SubfileType < ifds[j].SubfileType - }) - if ifds[0].SubfileType != 0 { - return nil, fmt.Errorf("failed sort: first px=%d type=%d", ifds[0].ImageLength, ifds[0].SubfileType) - } - cog.ifd = ifds[0] - curOvr := cog.ifd - l := curOvr.ImageLength - for _, ci := range ifds[1:] { - if ci.ImageLength == l { - curOvr.AddMask(ci) - } else { - curOvr.AddOverview(ci) - curOvr = ci - l = curOvr.ImageLength - } - } - return cog, nil + return ifds, nil } func loadIFD(r tiff.BReader, tifd tiff.IFD) (*ifd, error) { @@ -121,18 +79,42 @@ func Rewrite(out io.Writer, readers ...tiff.ReadAtReadSeeker) error { if err != nil { return fmt.Errorf("consistency check: %w", err) } - var cog *cog + var ifds []*ifd if len(tiffs) > 1 { - cog, err = loadMultipleTIFFs(tiffs) + ifds, err = loadMultipleTIFFs(tiffs) if err != nil { return fmt.Errorf("load: %w", err) } } else { - cog, err = loadSingleTIFF(tiffs[0]) + ifds, err = loadSingleTIFF(tiffs[0]) if err != nil { return fmt.Errorf("load: %w", err) } } + sort.Slice(ifds, func(i, j int) bool { + //return in order: fullres, fullresmasks, ovr1, ovr1masks, ovr2, .... + if ifds[i].ImageLength != ifds[j].ImageLength { + return ifds[i].ImageLength > ifds[j].ImageLength + } + return ifds[i].SubfileType < ifds[j].SubfileType + }) + if ifds[0].SubfileType != 0 { + return fmt.Errorf("failed sort: first px=%d type=%d", ifds[0].ImageLength, ifds[0].SubfileType) + } + cog := new() + cog.ifd = ifds[0] + curOvr := cog.ifd + l := curOvr.ImageLength + for _, ci := range ifds[1:] { + if ci.ImageLength == l { + curOvr.AddMask(ci) + } else { + curOvr.AddOverview(ci) + curOvr = ci + l = curOvr.ImageLength + } + } + err = cog.write(out) if err != nil { return fmt.Errorf("mucog write: %w", err) diff --git a/testdata/cog_ext_multi.tif b/testdata/cog_ext_multi.tif new file mode 100644 index 0000000000000000000000000000000000000000..a36c8f4cd67c6f853c52465318379fd0b0e68e6c GIT binary patch literal 4797 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 H1AF!_3J@d& literal 0 HcmV?d00001 diff --git a/testdata/cog_ext_ovr.tif b/testdata/cog_ext_ovr.tif new file mode 100644 index 0000000000000000000000000000000000000000..a36c8f4cd67c6f853c52465318379fd0b0e68e6c GIT binary patch literal 4797 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 H1AF!_3J@d& literal 0 HcmV?d00001 diff --git a/testdata/exttest.tif b/testdata/exttest.tif new file mode 100644 index 0000000000000000000000000000000000000000..adc00cdf0eb0ef47cb082f6a8ec4d724288023b4 GIT binary patch literal 2393 zcmYk#c{~%09|!O)bM%u(j*%mhTOr3{kD1)ZklDsC7R{AA4Ko`(6y?Z~BWI7JBwCue zi*hXYk%TchmU1QMQxAIT_v(-S`M%z-*Z2GReypw40DJ%dKmY&$ashxp^m71z^O0Zv zdBye1+&|3o%Re*V-z@!`4S%!AFaP{+`kT#wp#S$){rF@4%d{V5sNb?bR)9FbDF=N8 zF#+BL6HkXm!ugYe32||8mq0eEVz)aNw4%nm3{R$8Dj=H_sFq5Zuf4TN=u+hIJX;?W zwEUI3dLcsDv1ySV2`?V>(}vcFrCOGosE9U$wPWonQOjtkqEd}>wdtd`HZu`rINH0) z>;lCcy4O?mnplfKJ=3QV=abWiX?_#g?7r^Y!nv|WYR7LlV=MLq690N1nwuh{Bd*y= z2x+X2a8}JtCD5JEb{k0*_4$NH8z{xwm5iac^P69k7}ADUwIv5C#NBJBgLcX8i%jC9 z=?xVrwgT%0bMmSt&V!&6+w(D$h+|ir$sYO?5&gXNBL4XZYY*mL%*UX#-;yML7HQnrXUM4Nk4m9SVZ& z*?`0B_K35@-pH&PzjqCc&k=a=+wFJ{Fy(ukN3I9Y`fMZOO#J;kfts7AqKM2SFUR^* z*S+e3HK~_VdGu07^bh%O=taRVtP)(5ECc55!JLyhZ^Te(9Fhia-uQ_J0XQF0w_%NjQEQsh^Em{DRPNsTXGYA)2N`qX&7qIxBdo&DIQ>BY+%0g{I;UIFCu zZwv6>5{M6iNFIF+M*rUY*c(iik*!zS1kXiyJhq@V>w{EbM`5QmBT1qg&SnGG% zzQ7X2d!R87Weu+G%XklNwhBV}{YfE)X;%$l0V6vOR(v2}94~%imy+wriYRIenAvmx z96q}zBS?B5MHQzkL=_t)&&Rc2QUH)%#Fl*|u^(0}f1?i7u6;ZFT=V%~kG+l!a*1-+ z=6i=1e~qoqlPN+TxgU& zU>`g3mxJ)p_7key@9QMuXjE=vJ_FwOfiel_uJ0Qs>2Z6)lx`e<0)}I)`R>PP-gHQl zG{Hjnkxmgnx6(4GCa)*H%9l+`XCM#Fd-NG{juXcQ?D8dyWREDyB5$gq6DWPEwQ<$L{B8mwNQO1gSm{ZMmnT z{*SG{yVeKtP+qvbxAD;;%?}9~`B~aQ1O9Z#5~#2s%PE*iYJ+}AnaD?FM%e3r()YF9 z$V4@}T(mlXJ?xw;q}NAxx{Ul;35qHSpQ6HgzQcV}K=;FUZ?w1yUGRg=|9w~NwoS9V ziC^bbX`TIF=4!H*uL0*209+UDr~94#LxlJGbs(S}^rxI~QpbF)ew`ykBF#d6gaisU zI-SlWJcH_HTJz_aC=Nf0(e_r)v-1g;C}#&ji|v))H0oBNOz$EI+tFajEUQwK{MHn= z1rb$=QQoy1I*m~Es@hVQ*z@?_?C%NRh{8|4OZo%TL znQs+Q&*n55-3CwhUGK(kVE2QM#NX=Rgigj(ypd05`fN?I($1*g8}*}$1|A|Xg>RRA zTq!aIh}tQ02#dng5PoMOB!aw_Qx@4c6Gki;_^uq!;2{4gq{dp%+CNQ2kqfV{#K-c2YZ&*wzV;TC>tBQG(-Cq-A2nS-Zu)7->zyG+*$#h z9ai84!Zr8hK1U9gb3<|;z0qqh)qqEju< zU8I|~Xer$lHtH)HwJt7Y@xJqTH|!5I6itjsS^&g0Uq)co%^Q_wH~aCTdSPr3_th%<+EG z%W^Uo3BAY{X~SoLPFsN}Rpk zF3+d;6F9DLPf<4iHTH=J5zIWcb@uk$Ua?jZ>s_~=;lji&L~Sw3$24>79^62ZaAMpg z?hW<<9_XVNfyp#}ohd%Rlc~?k%xTdw6A-olX7J zlp|P${d0yw^JH22Js-$b39D%yF`1WMr_FCsB5wDi`54^0rSIbU&@j_+i;8H4|A@C XN0J5qUVRA9?pijN<<|KEQQ`X!EvQjT literal 0 HcmV?d00001 diff --git a/testdata/exttest.tif.2 b/testdata/exttest.tif.2 new file mode 100644 index 0000000000000000000000000000000000000000..8d36362d3fce16cc437757c1a4e1dd4e990e3f04 GIT binary patch literal 1299 zcmYk$dsNZ~6bEpaq}2jv39c0Bp>DQhS!!AbQv{N!^!tG#1cFaY42@hkAGj>jm8m%p zb)~L{Yn50YvlAvOi*iLk4_;{|f}N}s*UCC(>1oCSkG zX!EkOt=Yd;0--)AxJwy61vt78<>^{MNOcR9()~RuNoikQ84=+yN2AlJVLr0Rucf3` zYGeAjxbGWcd9<+8<*ZG0+Dx}aZSTZee+QED6b95G&lA+F`0n%-HKadjFS<6j(lfTV z#b*ZdpNW)<+sxVE4-T1z-LYd=G7EQkkle-Qqo!MpnR|NZcGK07epA6o?ruhC#tc05 zMyimp1m}UQ=}p@JdX>}1C3l{6(#LLx)E>K^*m*^RkUePDQ-b=tn`>xNUGt5LM7r4k5=Z(cgf1)P1!vVyJ(A}1%9D# zq`z=ibIBQ5>)2?~d9G{C`EmG%yiW{_?z?x2Ywa6986vRjocki$mmp-l>uL7s$aWY? zi4W-UlirX&!PZ9wTnf?Ml&6OuQ3XgzlPWoza`;xDyZ1w)Jc!;*3RuT8ljN|RkJ|$E zoVR@)Z*y063i}_g-taJ})~9b=FZ-KJEy8?zH#YX{+KYR7F8-ya_P@uf)os1&vZ5Tuc?2Sc>Wil2pQB@NCl+Bf%J<8-vKsjWIa92$9_s(K!!y%gt0d=P|0XdV;Fv^%At`}rOhBfv40>Wll2JM- zV4G0z?HrSA-3-@Ek4sBs%7UZlkLgwQnas->waF}`@~prpQnu!5jdDdi%c#-Bu=9~u zce39Lf`5GKj#lsHNQkx~U4GM`xHR$BYcc1t!?8Ev*Se_#+6J_3UkXZ#+|PK=b1EOY zQjXa&+|VScv|caSczDN8c9$9(6A_k{-nf7(u`Z{?)QvEYIO;Rvc}za9u|&|A*>KzU zHw)8VaD3DQk;@-#GEH#$XXI%zB9rJa5$^FEc$FDyAPH+I`Q1$+~w<3ctI z%jyt#`t@?groD$QS_X1Xbg_wm6caT7({M9tWTYugpNdo35nr)My_Nykjf#K##647v|Md=bHbLoI|wr2y7B!i;vZ zl)3E(D2pQ*0ShduC54=z;ZylB8nk^ z539l|0K0dTS?p?sMg9UJRudRiZdL;M5Qz07n46Ya%fQ1RWi62*M_N_PN-zgYG%G!9 zu#_WU^#&3{v)o3=ssh_XNajILTbZyL)NbC!7)RSw1vQ{Oe4E*U8G)4^r6TarO!%r1 R!l7C!mK6OMy~gSf{SQ92GSUD5 literal 0 HcmV?d00001 diff --git a/testdata/exttest.tif.4 b/testdata/exttest.tif.4 new file mode 100644 index 0000000000000000000000000000000000000000..da53c36e6cf5fff546aa9f090979cb12d850a859 GIT binary patch literal 889 zcmebD)MDUZU|`^7U|?isU<9%pfS3`9%>-qGR53%@tUxv!lnv4&io}*dV%s9IouF)x zCC*4}7e*Gay{mxcxI)?InHU&?k@Nzk7#bK90xVR-c-fm69tP}4F_YzQb5aVlSQ4fy z-s6@WxZ_Egtz3skO2Caj(|pyZc%=sIc+(fFIL#+5*mB9WaQ!)+YL|CD`BrN@=Ym7= zFIBN#_s)RjCS9p!b4`}|X@0d@8fG3i{p#w_T~D|+>aDwx6LRWL-|_S<{yLkqeYf2; z-+Dy%yY-T7kNJ1q%MbM4`{;Z90S5gIZ}`MMH_uJn7@`C`kzbx66Uk7-`DlR%dcT;_14D*Tzma3oM+nkT+J(QzX$R5y|0zL^ZrL*96x_A z`=gIPL*KsF=V5zMQWa~rJbVe~o3iTIeb39co;&_^Pn_NS>a)+^{xGcbPw&3?{&UsF zxckeeKm7jw&xZZ+52Qc*Z`8m1?ccr$%pVyI3{<$8?F5_|S#39@NVDq~G~eX3{jfxv z+os^~MPB`YC)WIShD@&oZ7(YC6?XaPyjs+?FzLCt>&NDul5T~I-%8sX9=a{-Uif&U zyzV9D^@`R;D)q`%LN1e4b%j!9t2-Dqo!7L#v}CRJsh}mRb=Mdj*=wK^EV9)|d$Y-0 z6JMh@=gqW(Lnc~i7f)%m@-r^EZlkq%OQoIv<}1$~w2HsvIt6SIdG8W9C0Ew{l1Q(r z=cOrYb-gc(oHg~mJmsye{}oYQ*T5@Nb$x@ciu#6zUY(j78-7i+H#PFw)U~5SVmOD57mRRr7%v;meuFbwJc6Mv-?P+iK=HC(L zJz98Yy6)NHyW+lAOYct4y<2`yy!UD4z3FS;R^Jyt`?dD|^tXTOA4u@AHa?i4$J_i+ z!cVmI;fy@l_D2$Zs-2H!tkdm&EOE}X_wkH(w*5~e`CKPHnW^VH`KhE|=+vh(^J1qz zlk7{K`E2I8+}Y11&y~)7KJ#7e{1;MutqWhw((7IPQp#`Y(wDRH<}QCF)wguzt68h` G8X5p5ZDW7{ literal 0 HcmV?d00001 diff --git a/testdata/exttest.tif.ovr b/testdata/exttest.tif.ovr new file mode 100644 index 0000000000000000000000000000000000000000..a6054045aa57b93ad8b04f784b17989e03e4a549 GIT binary patch literal 2204 zcmciCdsGrw9tUtt%jp4*C6F3AmDZYpX=;{(`2fsOkcS|QqWK;*G{(|Z!!%!02Ih(& zmAOr_Zey6P=}D8GmOB|x%F9~){LG7DbiZL5r2W&h7!c_d(! zQ6~S+5Wjqm{_JEk0D!b=wr%;1|DD!%`R0$bEB=3_l`Mb3A8BQ4{yXh*3_w2ME4-Hj z1ZZC5AA`T}1;o~(6cuaf#ickw%P?_Y-qvLz?JpdOCmwX*`EBK5ghWC7Pgs}gkVMkK zx{`(a!Wl}dGq2Tfd zac+cdB@}(-&*LqWsVANlm!5~W+?IfNFRsXO?gJfH%1K=1u{0ZFW)H(x6_vTz-Z&B6 zIU16sLRdh8AIX{R;Rei3zKoN>6odW-71G|!9TlHCklG0_WO^HGfj7H#13Aa8242kU>?!abK}82(YtLqCMG15!d+4s zIqWIja%$X5T3Nl$T>9?W3{pm#S~x6|15Ny%6T2n?q_?n^=oju_m_N~q<#+@yC&Mu$ ztIn~YJ}w)HlZA=)6Z*&wDm-ZxOYnc%>Jc2SP|(RVB^ewOp^&2!=t}M^BT@wchDND) zdzYfs6l`29jpq?S>8G&yNwl_vilY%=@pXnWQ!GrEDn;#K5lV?9I5G=-=ex*xhR2DQ zRniW7Rd4 zWt#hW`_Fy*XM=gUmIgW)7cW>FU8%9g1y|{4=Z*Y?utZu)q9BhUNUDC~cyW|w$i6%Y z1*NA<3KX_iW3oS0WlSz$YJSw_B*xXf;n`hFP07h?5GgwhEsPWuuK8wa1R|oDVY}-= z)YPKgNYm(G+SS&GOPs-WgKc7W$TWYK@eY0QiK=7z!xHd1bDLwq{ozyTTPm436OP@C zAyKwC<#MsNG<9f}^!z&*cz16y-GlYuUpdj_Gd~;C?Ip>uB-d-1m+IpN)kv1pU}o4z zMo(5AFlZ!mb2R(e0#sJ;lHrra>RC9cDjm*_DiyqapLd%7I@YJY_3bIU+OnBn?jexV zxzXX0sSo!;+hxycZ|WD9>}9jYrP}F_OQU8c`FPM8UnP%xrrRp0nAXid{d3aw^6*Lj2YAjblZwC3vY#0F?n8>pED9 zH`oQJSr!^IzaI#o9VjZ>QiBRQOF-;GscW`qiU{WjxIJj92(0O%l@ZbqXtfxsHH$e< z;J9I^5^F6gnN4W)#HfdDwM9%0L3${Vs)uR2vdakupFp+IUT0Qtfe7+HLIZBsq0Uwi z5tt+Wu$>wwlm8UW8Ne9+cDyIdqQrj(!K;n}hn~m85mt)E?uR4sc=bmfb~pE%1mf)} z4t+cC_o7jTyIc<1)z_|@W9`5E+&ANcBZM7z7GLjvF2S?LU5ImO}Jbjf)%dtyEhDwv(Q$p^RW6{rF&r(E)n{>SL?OgaKo{Zrn*UuAy zoN1Qz(Dbi_C<_a=xn^d8te0>}u4~>}B46wjZ$ZrjG=R~}*a;|lrm)=_!@Q=qJ?#2$ z5EjVPHrvB{O>b`Zc5^tr^U!8gr6YWE{H5K#Zf9gphkNTp&QFPT4e%RoUE+OvNmi|H z&ModW?pU`enWr-;tisDVgIjzExBrCd>pd;EvVB5Z;#&!O2mjzfh8yDswzDz zS`7gf$EbO5c3eLMTb0nyW7H%KK-j|M0iH;dItY=pG6s2iarzJx_?S7whf6Ysp;*Pq zVLoFx>m`&up8b+9(&vsqC9|v%zTS9xbSr3W!DuxCSU9#72QD6~PJ@+dwsIWLYN{LI zW!kM$54N`2fGpQpgU}T^0Rme&ZjB>XjSJG~{8!eT1i>poBjeJ9we)1oguuY6owNZJ hU!A;&U|)M}gR2t0zL-{1|Hg(Ry#D6mPl){d-vA4fm@ohU literal 0 HcmV?d00001 From 428b53e565eb1037063599e41a2f2fd27a1acd7c Mon Sep 17 00:00:00 2001 From: Thomas Bonfort Date: Mon, 28 Jun 2021 18:42:37 +0200 Subject: [PATCH 2/4] add missing error check --- loader.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/loader.go b/loader.go index 98ac205..8a9c06d 100644 --- a/loader.go +++ b/loader.go @@ -107,7 +107,10 @@ func Rewrite(out io.Writer, readers ...tiff.ReadAtReadSeeker) error { l := curOvr.ImageLength for _, ci := range ifds[1:] { if ci.ImageLength == l { - curOvr.AddMask(ci) + err = curOvr.AddMask(ci) + if err != nil { + return err + } } else { curOvr.AddOverview(ci) curOvr = ci From 1c24b61c9779f3000c36f747a72ae0286889128c Mon Sep 17 00:00:00 2001 From: Thomas Bonfort Date: Mon, 28 Jun 2021 18:46:05 +0200 Subject: [PATCH 3/4] fixup readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 98a75f9..448f18e 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,22 @@ go install github.com/airbusgeo/cogger/cmd/cogger@latest ### Binary +#### With internal overviews + ```bash gdal_translate -of GTIFF -co BIGTIFF=YES -co TILED=YES -co COMPRESS=ZSTD -co NUM_THREADS=4 input.file geotif.tif gdaladdo --config GDAL_NUM_THREADS 4 geotif.tif 2 4 8 16 32 cogger -output mycog.tif geotif.tif ``` +#### With external overviews + +```bash +gdal_translate -of GTIFF -co BIGTIFF=YES -co TILED=YES -co COMPRESS=ZSTD -co NUM_THREADS=4 input.file geotif.tif +gdaladdo -ro --config GDAL_NUM_THREADS 4 geotif.tif 2 4 8 16 32 #creates geotif.tif.ovr +cogger -output mycog.tif geotif.tif geotif.tif.ovr +``` + ### Library The cogger API consists of a single function: From 06b078f7629fdfe1759d5c309505ca44b8eccb35 Mon Sep 17 00:00:00 2001 From: Thomas Bonfort Date: Mon, 28 Jun 2021 19:03:50 +0200 Subject: [PATCH 4/4] fixup readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 448f18e..fea84cb 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ go install github.com/airbusgeo/cogger/cmd/cogger@latest ```bash gdal_translate -of GTIFF -co BIGTIFF=YES -co TILED=YES -co COMPRESS=ZSTD -co NUM_THREADS=4 input.file geotif.tif -gdaladdo --config GDAL_NUM_THREADS 4 geotif.tif 2 4 8 16 32 +gdaladdo --config GDAL_NUM_THREADS 4 --config COMPRESS_OVERVIEW ZSTD geotif.tif 2 4 8 16 32 cogger -output mycog.tif geotif.tif ``` @@ -47,7 +47,7 @@ cogger -output mycog.tif geotif.tif ```bash gdal_translate -of GTIFF -co BIGTIFF=YES -co TILED=YES -co COMPRESS=ZSTD -co NUM_THREADS=4 input.file geotif.tif -gdaladdo -ro --config GDAL_NUM_THREADS 4 geotif.tif 2 4 8 16 32 #creates geotif.tif.ovr +gdaladdo -ro --config GDAL_NUM_THREADS 4 --config COMPRESS_OVERVIEW ZSTD geotif.tif 2 4 8 16 32 #creates geotif.tif.ovr cogger -output mycog.tif geotif.tif geotif.tif.ovr ```