Skip to content

Commit

Permalink
feat: use runtime.AddCleanup and remove Destroy methods
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Feb 13, 2025
1 parent 7c4cb79 commit 338c706
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 72 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ func ExamplePJ_Forward() {
if err != nil {
panic(err)
}
defer pj.Destroy()

// Start with Zürich's WGS84 latitude/longitude.
zurich4326 := proj.NewCoord(47.374444, 8.541111, 408, 0)
Expand Down
46 changes: 20 additions & 26 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,28 @@ func init() {

// A Context is a context.
type Context struct {
mutex sync.Mutex
pjContext *C.PJ_CONTEXT
mutex sync.Mutex
cPJContext *C.PJ_CONTEXT
}

// NewContext returns a new Context.
func NewContext() *Context {
pjContext := C.proj_context_create()
C.proj_log_level(pjContext, C.PJ_LOG_NONE)
c := &Context{
pjContext: pjContext,
cPJContext: pjContext,
}
runtime.SetFinalizer(c, (*Context).Destroy)
runtime.AddCleanup(c, func(pjContext *C.PJ_CONTEXT) {
C.proj_context_destroy(pjContext)
}, pjContext)
return c
}

// Destroy frees all resources associated with c.
func (c *Context) Destroy() {
c.Lock()
defer c.Unlock()
if c.pjContext != nil {
C.proj_context_destroy(c.pjContext)
c.pjContext = nil
}
}

// SetLogLevel sets the log level.
func (c *Context) SetLogLevel(logLevel LogLevel) {
c.Lock()
defer c.Unlock()
C.proj_log_level(c.pjContext, C.PJ_LOG_LEVEL(logLevel))
C.proj_log_level(c.cPJContext, C.PJ_LOG_LEVEL(logLevel))
}

// SetSearchPaths sets the paths PROJ should be exploring to find the PROJ Data files.
Expand All @@ -73,7 +65,7 @@ func (c *Context) SetSearchPaths(paths []string) {
if len(paths) > 0 {
pathPtr = unsafe.Pointer(&cPaths[0])
}
C.proj_context_set_search_paths(c.pjContext, C.int(len(cPaths)), (**C.char)(pathPtr))
C.proj_context_set_search_paths(c.cPJContext, C.int(len(cPaths)), (**C.char)(pathPtr))
}

func (c *Context) Lock() {
Expand All @@ -93,10 +85,10 @@ func (c *Context) NewCRSToCRS(sourceCRS, targetCRS string, area *Area) (*PJ, err

var cArea *C.PJ_AREA
if area != nil {
cArea = area.pjArea
cArea = area.cPJArea
}

return c.newPJ(C.proj_create_crs_to_crs(c.pjContext, cSourceCRS, cTargetCRS, cArea))
return c.newPJ(C.proj_create_crs_to_crs(c.cPJContext, cSourceCRS, cTargetCRS, cArea))
}

// NewCRSToCRSFromPJ returns a new PJ from two CRSs.
Expand All @@ -123,10 +115,10 @@ func (c *Context) NewCRSToCRSFromPJ(sourcePJ, targetPJ *PJ, area *Area, options

var cArea *C.PJ_AREA
if area != nil {
cArea = area.pjArea
cArea = area.cPJArea
}

return c.newPJ(C.proj_create_crs_to_crs_from_pj(c.pjContext, sourcePJ.pj, targetPJ.pj, cArea, cOptionsPtr))
return c.newPJ(C.proj_create_crs_to_crs_from_pj(c.cPJContext, sourcePJ.cPJ, targetPJ.cPJ, cArea, cOptionsPtr))
}

// New returns a new PJ with the given definition.
Expand All @@ -137,7 +129,7 @@ func (c *Context) New(definition string) (*PJ, error) {
cDefinition := C.CString(definition)
defer C.free(unsafe.Pointer(cDefinition))

return c.newPJ(C.proj_create(c.pjContext, cDefinition))
return c.newPJ(C.proj_create(c.cPJContext, cDefinition))
}

// NewFromArgs returns a new PJ from args.
Expand All @@ -152,7 +144,7 @@ func (c *Context) NewFromArgs(args ...string) (*PJ, error) {
cArgs[i] = cArg
}

return c.newPJ(C.proj_create_argv(c.pjContext, (C.int)(len(cArgs)), (**C.char)(unsafe.Pointer(&cArgs[0]))))
return c.newPJ(C.proj_create_argv(c.cPJContext, (C.int)(len(cArgs)), (**C.char)(unsafe.Pointer(&cArgs[0]))))
}

func (c *Context) Unlock() {
Expand All @@ -163,7 +155,7 @@ func (c *Context) Unlock() {
func (c *Context) errnoString(errno int) string {
c.Lock()
defer c.Unlock()
return C.GoString(C.proj_context_errno_string(c.pjContext, (C.int)(errno)))
return C.GoString(C.proj_context_errno_string(c.cPJContext, (C.int)(errno)))
}

// newError returns a new error with number errno.
Expand All @@ -177,14 +169,16 @@ func (c *Context) newError(errno int) *Error {
// newPJ returns a new PJ or an error.
func (c *Context) newPJ(cPJ *C.PJ) (*PJ, error) {
if cPJ == nil {
return nil, c.newError(int(C.proj_context_errno(c.pjContext)))
return nil, c.newError(int(C.proj_context_errno(c.cPJContext)))
}

pj := &PJ{
context: c,
pj: cPJ,
cPJ: cPJ,
}
runtime.SetFinalizer(pj, (*PJ).Destroy)
runtime.AddCleanup(pj, func(cPJ *C.PJ) {
C.proj_destroy(cPJ)
}, cPJ)
return pj, nil
}

Expand Down
1 change: 0 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ func ExamplePJ_Forward() {
if err != nil {
panic(err)
}
defer pj.Destroy()

// Start with Zürich's WGS84 latitude/longitude.
zurich4326 := proj.NewCoord(47.374444, 8.541111, 408, 0)
Expand Down
52 changes: 21 additions & 31 deletions pj.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
// A PJ is a projection or a transformation.
type PJ struct {
context *Context
pj *C.PJ
cPJ *C.PJ
}

// A PJInfo contains information about a PJ.
Expand All @@ -32,16 +32,6 @@ type PJInfo struct {
Accuracy float64
}

// Destroy releases all resources associated with pj.
func (pj *PJ) Destroy() {
pj.context.Lock()
defer pj.context.Unlock()
if pj.pj != nil {
C.proj_destroy(pj.pj)
pj.pj = nil
}
}

// Returns a new PJ instance whose axis order is the one expected for
// visualization purposes. If the axis order of its source or target CRS is
// northing, easting, then an axis swap operation will be inserted.
Expand All @@ -51,7 +41,7 @@ func (pj *PJ) Destroy() {
func (pj *PJ) NormalizeForVisualization() (*PJ, error) {
pj.context.Lock()
defer pj.context.Unlock()
return pj.context.newPJ(C.proj_normalize_for_visualization(pj.context.pjContext, pj.pj))
return pj.context.newPJ(C.proj_normalize_for_visualization(pj.context.cPJContext, pj.cPJ))
}

// Forward transforms coord in the forward direction.
Expand Down Expand Up @@ -88,7 +78,7 @@ func (pj *PJ) ForwardFloat64Slices(float64Slices [][]float64) error {
func (pj *PJ) Geod(a, b Coord) (float64, float64, float64) {
pj.context.Lock()
defer pj.context.Unlock()
cCoord := C.proj_geod(pj.pj, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b)))
cCoord := C.proj_geod(pj.cPJ, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b)))
cGeod := *(*C.PJ_GEOD)(unsafe.Pointer(&cCoord))
return (float64)(cGeod.s), (float64)(cGeod.a1), (float64)(cGeod.a2)
}
Expand All @@ -97,15 +87,15 @@ func (pj *PJ) Geod(a, b Coord) (float64, float64, float64) {
func (pj *PJ) GetLastUsedOperation() (*PJ, error) {
pj.context.Lock()
defer pj.context.Unlock()
return pj.context.newPJ(C.proj_trans_get_last_used_operation(pj.pj))
return pj.context.newPJ(C.proj_trans_get_last_used_operation(pj.cPJ))
}

// Info returns information about pj.
func (pj *PJ) Info() PJInfo {
pj.context.Lock()
defer pj.context.Unlock()

cProjInfo := C.proj_pj_info(pj.pj)
cProjInfo := C.proj_pj_info(pj.cPJ)
return PJInfo{
ID: C.GoString(cProjInfo.id),
Description: C.GoString(cProjInfo.description),
Expand All @@ -117,7 +107,7 @@ func (pj *PJ) Info() PJInfo {

// IsCRS returns whether pj is a CRS.
func (pj *PJ) IsCRS() bool {
return C.proj_is_crs(pj.pj) != 0
return C.proj_is_crs(pj.cPJ) != 0
}

// Inverse transforms coord in the inverse direction.
Expand Down Expand Up @@ -154,27 +144,27 @@ func (pj *PJ) InverseFloat64Slices(float64Slices [][]float64) error {
func (pj *PJ) LPDist(a, b Coord) float64 {
pj.context.Lock()
defer pj.context.Unlock()
return (float64)(C.proj_lp_dist(pj.pj, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b))))
return (float64)(C.proj_lp_dist(pj.cPJ, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b))))
}

// LPZDist returns the geodesic distance between a and b in geodetic
// coordinates, taking height above the ellipsoid into account.
func (pj *PJ) LPZDist(a, b Coord) float64 {
pj.context.Lock()
defer pj.context.Unlock()
return (float64)(C.proj_lpz_dist(pj.pj, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b))))
return (float64)(C.proj_lpz_dist(pj.cPJ, *(*C.PJ_COORD)(unsafe.Pointer(&a)), *(*C.PJ_COORD)(unsafe.Pointer(&b))))
}

// Trans transforms a single Coord in place.
func (pj *PJ) Trans(direction Direction, coord Coord) (Coord, error) {
pj.context.Lock()
defer pj.context.Unlock()

lastErrno := C.proj_errno_reset(pj.pj)
defer C.proj_errno_restore(pj.pj, lastErrno)
lastErrno := C.proj_errno_reset(pj.cPJ)
defer C.proj_errno_restore(pj.cPJ, lastErrno)

pjCoord := C.proj_trans(pj.pj, (C.PJ_DIRECTION)(direction), *(*C.PJ_COORD)(unsafe.Pointer(&coord)))
if errno := int(C.proj_errno(pj.pj)); errno != 0 {
pjCoord := C.proj_trans(pj.cPJ, (C.PJ_DIRECTION)(direction), *(*C.PJ_COORD)(unsafe.Pointer(&coord)))
if errno := int(C.proj_errno(pj.cPJ)); errno != 0 {
return Coord{}, pj.context.newError(errno)
}
return *(*Coord)(unsafe.Pointer(&pjCoord)), nil
Expand All @@ -189,10 +179,10 @@ func (pj *PJ) TransArray(direction Direction, coords []Coord) error {
pj.context.Lock()
defer pj.context.Unlock()

lastErrno := C.proj_errno_reset(pj.pj)
defer C.proj_errno_restore(pj.pj, lastErrno)
lastErrno := C.proj_errno_reset(pj.cPJ)
defer C.proj_errno_restore(pj.cPJ, lastErrno)

if errno := int(C.proj_trans_array(pj.pj, (C.PJ_DIRECTION)(direction), (C.size_t)(len(coords)), (*C.PJ_COORD)(unsafe.Pointer(&coords[0])))); errno != 0 {
if errno := int(C.proj_trans_array(pj.cPJ, (C.PJ_DIRECTION)(direction), (C.size_t)(len(coords)), (*C.PJ_COORD)(unsafe.Pointer(&coords[0])))); errno != 0 {
return pj.context.newError(errno)
}
return nil
Expand All @@ -204,11 +194,11 @@ func (pj *PJ) TransBounds(direction Direction, bounds Bounds, densifyPoints int)
defer pj.context.Unlock()

var transBounds Bounds
if C.proj_trans_bounds(pj.context.pjContext, pj.pj, (C.PJ_DIRECTION)(direction),
if C.proj_trans_bounds(pj.context.cPJContext, pj.cPJ, (C.PJ_DIRECTION)(direction),
(C.double)(bounds.XMin), (C.double)(bounds.YMin), (C.double)(bounds.XMax), (C.double)(bounds.YMax),
(*C.double)(&transBounds.XMin), (*C.double)(&transBounds.YMin), (*C.double)(&transBounds.XMax), (*C.double)(&transBounds.YMax),
C.int(densifyPoints)) == 0 {
return Bounds{}, pj.context.newError(int(C.proj_errno(pj.pj)))
return Bounds{}, pj.context.newError(int(C.proj_errno(pj.cPJ)))
}
return transBounds, nil
}
Expand Down Expand Up @@ -275,16 +265,16 @@ func (pj *PJ) TransGeneric(direction Direction, x *float64, sx, nx int, y *float
pj.context.Lock()
defer pj.context.Unlock()

lastErrno := C.proj_errno_reset(pj.pj)
defer C.proj_errno_restore(pj.pj, lastErrno)
lastErrno := C.proj_errno_reset(pj.cPJ)
defer C.proj_errno_restore(pj.cPJ, lastErrno)

if int(C.proj_trans_generic(pj.pj, (C.PJ_DIRECTION)(direction),
if int(C.proj_trans_generic(pj.cPJ, (C.PJ_DIRECTION)(direction),
(*C.double)(x), C.size_t(sx), C.size_t(nx),
(*C.double)(y), C.size_t(sy), C.size_t(ny),
(*C.double)(z), C.size_t(sz), C.size_t(nz),
(*C.double)(m), C.size_t(sm), C.size_t(nm),
)) != max(nx, ny, nz, nm) {
return pj.context.newError(int(C.proj_errno(pj.pj)))
return pj.context.newError(int(C.proj_errno(pj.cPJ)))
}

return nil
Expand Down
20 changes: 7 additions & 13 deletions proj.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (

// An Area is an area.
type Area struct {
pjArea *C.PJ_AREA
cPJArea *C.PJ_AREA
}

type Bounds struct {
Expand All @@ -40,23 +40,17 @@ type Error struct {

// NewArea returns a new Area.
func NewArea(westLonDegree, southLatDegree, eastLonDegree, northLatDegree float64) *Area {
pjArea := C.proj_area_create()
C.proj_area_set_bbox(pjArea, (C.double)(westLonDegree), (C.double)(southLatDegree), (C.double)(eastLonDegree), (C.double)(northLatDegree))
cPJArea := C.proj_area_create()
C.proj_area_set_bbox(cPJArea, (C.double)(westLonDegree), (C.double)(southLatDegree), (C.double)(eastLonDegree), (C.double)(northLatDegree))
a := &Area{
pjArea: pjArea,
cPJArea: cPJArea,
}
runtime.SetFinalizer(a, (*Area).Destroy)
runtime.AddCleanup(a, func(cPJArea *C.PJ_AREA) {
C.proj_area_destroy(cPJArea)
}, cPJArea)
return a
}

// Destroy frees all resources associated with a.
func (a *Area) Destroy() {
if a.pjArea != nil {
C.proj_area_destroy(a.pjArea)
a.pjArea = nil
}
}

// NewCoord returns a new Coord.
func NewCoord(x, y, z, m float64) Coord {
return Coord{x, y, z, m}
Expand Down

0 comments on commit 338c706

Please sign in to comment.