Skip to content

Commit

Permalink
Added function for converting GCPs into a GeoTransform array (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
andyzimmerman1 authored Nov 6, 2023
1 parent 67bb5f9 commit 50ce7da
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 0 deletions.
4 changes: 4 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func ErrLogger(fn ErrorHandler) interface {
NearblackOption
DemOption
SetGCPsOption
GCPsToGeoTransformOption
RegisterPluginOption
} {
return errorCallback{fn}
Expand Down Expand Up @@ -381,6 +382,9 @@ func (ec errorCallback) setDemOpt(o *demOpts) {
func (ec errorCallback) setSetGCPsOpt(o *setGCPsOpts) {
o.errorHandler = ec.fn
}
func (ec errorCallback) setGCPsToGeoTransformOpts(o *gcpsToGeoTransformOpts) {
o.errorHandler = ec.fn
}
func (ec errorCallback) setRegisterPluginOpt(o *registerPluginOpts) {
o.errorHandler = ec.fn
}
Expand Down
16 changes: 16 additions & 0 deletions godal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1874,4 +1874,20 @@ GDAL_GCP *goGCPListToGDALGCP(goGCPList GCPList, int numGCPs) {
}

return ret;
}

void godalGCPListToGeoTransform(cctx *ctx, goGCPList GCPList, int numGCPs, double *gt){
godalWrap(ctx);

GDAL_GCP *GDALGCPList = goGCPListToGDALGCP(GCPList, numGCPs);

int ret = GDALGCPsToGeoTransform(numGCPs, GDALGCPList, gt, TRUE);
if(ret!=TRUE) {
forceError(ctx);
}

GDALDeinitGCPs(numGCPs, GDALGCPList);
CPLFree(GDALGCPList);

godalUnwrap();
}
57 changes: 57 additions & 0 deletions godal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4099,6 +4099,63 @@ func (ds *Dataset) SetGCPs(GCPList []GCP, opts ...SetGCPsOption) error {
return nil
}

// Convert list of GCPs to a GDAL GeoTransorm array
func GCPsToGeoTransform(GCPList []GCP, opts ...GCPsToGeoTransformOption) ([6]float64, error) {
gco := gcpsToGeoTransformOpts{}
for _, opt := range opts {
opt.setGCPsToGeoTransformOpts(&gco)
}

// Convert `[]GCP` -> `C.goGCPList`
var gcpList C.goGCPList
var (
ids = make([]string, len(GCPList))
infos = make([]string, len(GCPList))
gcpPixels = make([]float64, len(GCPList))
gcpLines = make([]float64, len(GCPList))
gcpXs = make([]float64, len(GCPList))
gcpYs = make([]float64, len(GCPList))
gcpZs = make([]float64, len(GCPList))
)
for i, g := range GCPList {
ids[i] = g.PszId
infos[i] = g.PszInfo
gcpPixels[i] = (g.DfGCPPixel)
gcpLines[i] = (g.DfGCPLine)
gcpXs[i] = (g.DfGCPX)
gcpYs[i] = (g.DfGCPY)
gcpZs[i] = (g.DfGCPZ)
}
cIds := sliceToCStringArray(ids)
defer cIds.free()
cInfos := sliceToCStringArray(infos)
defer cInfos.free()

gcpList.pszIds = cIds.cPointer()
gcpList.pszInfos = cInfos.cPointer()
gcpList.dfGCPPixels = cDoubleArray(gcpPixels)
gcpList.dfGCPLines = cDoubleArray(gcpLines)
gcpList.dfGCPXs = cDoubleArray(gcpXs)
gcpList.dfGCPYs = cDoubleArray(gcpYs)
gcpList.dfGCPZs = cDoubleArray(gcpZs)

gt := make([]C.double, 6)
cgt := (*C.double)(unsafe.Pointer(&gt[0]))
ret := [6]float64{}
var cgc = createCGOContext(nil, gco.errorHandler)
C.godalGCPListToGeoTransform(cgc.cPointer(), gcpList, C.int(len(GCPList)), cgt)
if err := cgc.close(); err != nil {
return ret, err
}

// Copy the values from the C Array into a Go array
for i := range gt {
ret[i] = float64(gt[i])
}

return ret, nil
}

type cgoContext struct {
cctx *C.cctx
opts cStringArray
Expand Down
1 change: 1 addition & 0 deletions godal.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ extern "C" {
void godalSetGCPs(cctx *ctx, GDALDatasetH hSrcDS, int numGCPs, goGCPList GCPList, const char *pszGCPProjection);
void godalSetGCPs2(cctx *ctx, GDALDatasetH hSrcDS, int numGCPs, goGCPList GCPList, OGRSpatialReferenceH hSRS);
GDAL_GCP *goGCPListToGDALGCP(goGCPList GCPList, int numGCPs);
void godalGCPListToGeoTransform(cctx *ctx, goGCPList GCPList, int numGCPs, double *gt);
#ifdef __cplusplus
}
#endif
Expand Down
78 changes: 78 additions & 0 deletions godal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4799,6 +4799,84 @@ func TestSetGCPs2InvalidDataset(t *testing.T) {
assert.Error(t, err)
}

func TestGCPsToGeoTransformEmptyList(t *testing.T) {
var gcpList []GCP = []GCP{}

ehc := eh()
_, err := GCPsToGeoTransform(gcpList, ErrLogger(ehc.ErrorHandler))
assert.Error(t, err)
}

func TestGCPsToGeoTransformInsufficientGCPs(t *testing.T) {
var gcpList []GCP = []GCP{
{
PszId: "0",
PszInfo: "",
DfGCPPixel: 0.5,
DfGCPLine: 0.5,
DfGCPX: 0,
DfGCPY: 0,
DfGCPZ: 0,
},
}

ehc := eh()
_, err := GCPsToGeoTransform(gcpList, ErrLogger(ehc.ErrorHandler))
assert.Error(t, err)
}

func TestGCPsToGeoTransform(t *testing.T) {
var gcpList []GCP = []GCP{
{
PszId: "0",
PszInfo: "",
DfGCPPixel: 0.5,
DfGCPLine: 0.5,
DfGCPX: 0,
DfGCPY: 0,
DfGCPZ: 0,
},
{
PszId: "1",
PszInfo: "",
DfGCPPixel: 1000.5,
DfGCPLine: 0.5,
DfGCPX: 10,
DfGCPY: 0,
DfGCPZ: 1,
},
{
PszId: "2",
PszInfo: "",
DfGCPPixel: 1000.5,
DfGCPLine: 100.5,
DfGCPX: 10,
DfGCPY: 20,
DfGCPZ: 1,
},
{
PszId: "3",
PszInfo: "",
DfGCPPixel: 0.5,
DfGCPLine: 100.5,
DfGCPX: 0,
DfGCPY: 20,
DfGCPZ: 1,
},
}

geoTransform, err := GCPsToGeoTransform(gcpList)
assert.NoError(t, err)

// Check `Get` method after settings GCPs
assert.Equal(t, -0.005, geoTransform[0])
assert.Equal(t, 0.01, geoTransform[1])
assert.Equal(t, 0.0, geoTransform[2])
assert.Equal(t, -0.1, geoTransform[3])
assert.Equal(t, 0.0, geoTransform[4])
assert.Equal(t, 0.2, geoTransform[5])
}

func TestDemHillshade(t *testing.T) {
// 1. Create an image, linearly interpolated, from dark (on the left) to white (on the right), using `Grid()`
var (
Expand Down
10 changes: 10 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,7 @@ type demOpts struct {
type DemOption interface {
setDemOpt(demOpt *demOpts)
}

type setGCPsOpts struct {
errorHandler ErrorHandler
projString string
Expand All @@ -1273,6 +1274,15 @@ type SetGCPsOption interface {
setSetGCPsOpt(sgOpt *setGCPsOpts)
}

type gcpsToGeoTransformOpts struct {
errorHandler ErrorHandler
}

// GCPsToGeoTransformOption is an option that can be passed to GCPsToGeoTransform()
type GCPsToGeoTransformOption interface {
setGCPsToGeoTransformOpts(gcpGtOpt *gcpsToGeoTransformOpts)
}

type gcpProjStringOpt struct {
projString string
}
Expand Down

0 comments on commit 50ce7da

Please sign in to comment.