# This file was automatically generated by SWIG (http://www.swig.org).
# Version 2.0.12
#
# Do not make changes to this file unless you know what you are doing--modify
# the SWIG interface file instead.

package Geo::OGR;
use base qw(Exporter);
use base qw(DynaLoader);
require Geo::OSR;
require Geo::GDAL;
package Geo::OGRc;
bootstrap Geo::OGR;
package Geo::OGR;
@EXPORT = qw();

# ---------- BASE METHODS -------------

package Geo::OGR;

sub TIEHASH {
    my ($classname,$obj) = @_;
    return bless $obj, $classname;
}

sub CLEAR { }

sub FIRSTKEY { }

sub NEXTKEY { }

sub FETCH {
    my ($self,$field) = @_;
    my $member_func = "swig_${field}_get";
    $self->$member_func();
}

sub STORE {
    my ($self,$field,$newval) = @_;
    my $member_func = "swig_${field}_set";
    $self->$member_func($newval);
}

sub this {
    my $ptr = shift;
    return tied(%$ptr);
}


# ------- FUNCTION WRAPPERS --------

package Geo::OGR;

*callback_d_cp_vp = *Geo::OGRc::callback_d_cp_vp;
*UseExceptions = *Geo::OGRc::UseExceptions;
*DontUseExceptions = *Geo::OGRc::DontUseExceptions;
*CreateGeometryFromWkb = *Geo::OGRc::CreateGeometryFromWkb;
*CreateGeometryFromWkt = *Geo::OGRc::CreateGeometryFromWkt;
*CreateGeometryFromGML = *Geo::OGRc::CreateGeometryFromGML;
*CreateGeometryFromJson = *Geo::OGRc::CreateGeometryFromJson;
*BuildPolygonFromEdges = *Geo::OGRc::BuildPolygonFromEdges;
*ApproximateArcAngles = *Geo::OGRc::ApproximateArcAngles;
*ForceToPolygon = *Geo::OGRc::ForceToPolygon;
*ForceToLineString = *Geo::OGRc::ForceToLineString;
*ForceToMultiPolygon = *Geo::OGRc::ForceToMultiPolygon;
*ForceToMultiPoint = *Geo::OGRc::ForceToMultiPoint;
*ForceToMultiLineString = *Geo::OGRc::ForceToMultiLineString;
*ForceTo = *Geo::OGRc::ForceTo;
*GetDriverCount = *Geo::OGRc::GetDriverCount;
*GetOpenDSCount = *Geo::OGRc::GetOpenDSCount;
*SetGenerate_DB2_V72_BYTE_ORDER = *Geo::OGRc::SetGenerate_DB2_V72_BYTE_ORDER;
*RegisterAll = *Geo::OGRc::RegisterAll;
*GeometryTypeToName = *Geo::OGRc::GeometryTypeToName;
*GetFieldTypeName = *Geo::OGRc::GetFieldTypeName;
*GetFieldSubTypeName = *Geo::OGRc::GetFieldSubTypeName;
*GT_Flatten = *Geo::OGRc::GT_Flatten;
*GT_SetZ = *Geo::OGRc::GT_SetZ;
*GT_SetModifier = *Geo::OGRc::GT_SetModifier;
*GT_HasZ = *Geo::OGRc::GT_HasZ;
*GT_IsSubClassOf = *Geo::OGRc::GT_IsSubClassOf;
*GT_IsCurve = *Geo::OGRc::GT_IsCurve;
*GT_IsSurface = *Geo::OGRc::GT_IsSurface;
*GT_IsNonLinear = *Geo::OGRc::GT_IsNonLinear;
*GT_GetCollection = *Geo::OGRc::GT_GetCollection;
*GT_GetCurve = *Geo::OGRc::GT_GetCurve;
*GT_GetLinear = *Geo::OGRc::GT_GetLinear;
*SetNonLinearGeometriesEnabledFlag = *Geo::OGRc::SetNonLinearGeometriesEnabledFlag;
*GetNonLinearGeometriesEnabledFlag = *Geo::OGRc::GetNonLinearGeometriesEnabledFlag;
*GetOpenDS = *Geo::OGRc::GetOpenDS;
*Open = *Geo::OGRc::Open;
*OpenShared = *Geo::OGRc::OpenShared;
*GetDriverByName = *Geo::OGRc::GetDriverByName;
*_GetDriver = *Geo::OGRc::_GetDriver;
*GeneralCmdLineProcessor = *Geo::OGRc::GeneralCmdLineProcessor;
*TermProgress_nocb = *Geo::OGRc::TermProgress_nocb;

############# Class : Geo::OGR::StyleTable ##############

package Geo::OGR::StyleTable;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::OGR );
%OWNER = ();
%ITERATORS = ();
sub new {
    my $pkg = shift;
    my $self = Geo::OGRc::new_StyleTable(@_);
    bless $self, $pkg if defined($self);
}

sub DESTROY {
    return unless $_[0]->isa('HASH');
    my $self = tied(%{$_[0]});
    return unless defined $self;
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_StyleTable($self);
        delete $OWNER{$self};
    }
}

*AddStyle = *Geo::OGRc::StyleTable_AddStyle;
*LoadStyleTable = *Geo::OGRc::StyleTable_LoadStyleTable;
*SaveStyleTable = *Geo::OGRc::StyleTable_SaveStyleTable;
*Find = *Geo::OGRc::StyleTable_Find;
*ResetStyleStringReading = *Geo::OGRc::StyleTable_ResetStyleStringReading;
*GetNextStyle = *Geo::OGRc::StyleTable_GetNextStyle;
*GetLastStyleName = *Geo::OGRc::StyleTable_GetLastStyleName;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::Driver ##############

package Geo::OGR::Driver;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::GDAL::MajorObject Geo::OGR );
%OWNER = ();
%ITERATORS = ();
*swig_name_get = *Geo::OGRc::Driver_name_get;
*swig_name_set = *Geo::OGRc::Driver_name_set;
*CreateDataSource = *Geo::OGRc::Driver_CreateDataSource;
*CopyDataSource = *Geo::OGRc::Driver_CopyDataSource;
*Open = *Geo::OGRc::Driver_Open;
*DeleteDataSource = *Geo::OGRc::Driver_DeleteDataSource;
*_TestCapability = *Geo::OGRc::Driver__TestCapability;
*GetName = *Geo::OGRc::Driver_GetName;
*Register = *Geo::OGRc::Driver_Register;
*Deregister = *Geo::OGRc::Driver_Deregister;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::DataSource ##############

package Geo::OGR::DataSource;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::GDAL::MajorObject Geo::OGR );
%OWNER = ();
%ITERATORS = ();
*swig_name_get = *Geo::OGRc::DataSource_name_get;
*swig_name_set = *Geo::OGRc::DataSource_name_set;
sub DESTROY {
    my $self;
    if ($_[0]->isa('SCALAR')) {
        $self = $_[0];
    } else {
        return unless $_[0]->isa('HASH');
        $self = tied(%{$_[0]});
        return unless defined $self;
    }
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_DataSource($self);
        delete $OWNER{$self};
    }
    $self->RELEASE_PARENTS();
}

*GetRefCount = *Geo::OGRc::DataSource_GetRefCount;
*GetSummaryRefCount = *Geo::OGRc::DataSource_GetSummaryRefCount;
*GetLayerCount = *Geo::OGRc::DataSource_GetLayerCount;
*_GetDriver = *Geo::OGRc::DataSource__GetDriver;
*GetName = *Geo::OGRc::DataSource_GetName;
*_DeleteLayer = *Geo::OGRc::DataSource__DeleteLayer;
*SyncToDisk = *Geo::OGRc::DataSource_SyncToDisk;
*FlushCache = *Geo::OGRc::DataSource_FlushCache;
*_CreateLayer = *Geo::OGRc::DataSource__CreateLayer;
*CopyLayer = *Geo::OGRc::DataSource_CopyLayer;
*GetLayerByIndex = *Geo::OGRc::DataSource_GetLayerByIndex;
*GetLayerByName = *Geo::OGRc::DataSource_GetLayerByName;
*_TestCapability = *Geo::OGRc::DataSource__TestCapability;
*_ExecuteSQL = *Geo::OGRc::DataSource__ExecuteSQL;
*_ReleaseResultSet = *Geo::OGRc::DataSource__ReleaseResultSet;
*GetStyleTable = *Geo::OGRc::DataSource_GetStyleTable;
*SetStyleTable = *Geo::OGRc::DataSource_SetStyleTable;
*StartTransaction = *Geo::OGRc::DataSource_StartTransaction;
*CommitTransaction = *Geo::OGRc::DataSource_CommitTransaction;
*RollbackTransaction = *Geo::OGRc::DataSource_RollbackTransaction;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::Layer ##############

package Geo::OGR::Layer;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::GDAL::MajorObject Geo::OGR );
%OWNER = ();
*GetRefCount = *Geo::OGRc::Layer_GetRefCount;
*SetSpatialFilter = *Geo::OGRc::Layer_SetSpatialFilter;
*SetSpatialFilterRect = *Geo::OGRc::Layer_SetSpatialFilterRect;
*GetSpatialFilter = *Geo::OGRc::Layer_GetSpatialFilter;
*SetAttributeFilter = *Geo::OGRc::Layer_SetAttributeFilter;
*ResetReading = *Geo::OGRc::Layer_ResetReading;
*GetName = *Geo::OGRc::Layer_GetName;
*GetGeomType = *Geo::OGRc::Layer_GetGeomType;
*GetGeometryColumn = *Geo::OGRc::Layer_GetGeometryColumn;
*GetFIDColumn = *Geo::OGRc::Layer_GetFIDColumn;
*GetFeature = *Geo::OGRc::Layer_GetFeature;
*GetNextFeature = *Geo::OGRc::Layer_GetNextFeature;
*SetNextByIndex = *Geo::OGRc::Layer_SetNextByIndex;
*SetFeature = *Geo::OGRc::Layer_SetFeature;
*CreateFeature = *Geo::OGRc::Layer_CreateFeature;
*DeleteFeature = *Geo::OGRc::Layer_DeleteFeature;
*SyncToDisk = *Geo::OGRc::Layer_SyncToDisk;
*GetLayerDefn = *Geo::OGRc::Layer_GetLayerDefn;
*GetFeatureCount = *Geo::OGRc::Layer_GetFeatureCount;
*GetExtent = *Geo::OGRc::Layer_GetExtent;
*_TestCapability = *Geo::OGRc::Layer__TestCapability;
*_CreateField = *Geo::OGRc::Layer__CreateField;
*_DeleteField = *Geo::OGRc::Layer__DeleteField;
*ReorderField = *Geo::OGRc::Layer_ReorderField;
*ReorderFields = *Geo::OGRc::Layer_ReorderFields;
*_AlterFieldDefn = *Geo::OGRc::Layer__AlterFieldDefn;
*CreateGeomField = *Geo::OGRc::Layer_CreateGeomField;
*StartTransaction = *Geo::OGRc::Layer_StartTransaction;
*CommitTransaction = *Geo::OGRc::Layer_CommitTransaction;
*RollbackTransaction = *Geo::OGRc::Layer_RollbackTransaction;
*FindFieldIndex = *Geo::OGRc::Layer_FindFieldIndex;
*GetSpatialRef = *Geo::OGRc::Layer_GetSpatialRef;
*GetFeaturesRead = *Geo::OGRc::Layer_GetFeaturesRead;
*SetIgnoredFields = *Geo::OGRc::Layer_SetIgnoredFields;
*Intersection = *Geo::OGRc::Layer_Intersection;
*Union = *Geo::OGRc::Layer_Union;
*SymDifference = *Geo::OGRc::Layer_SymDifference;
*Identity = *Geo::OGRc::Layer_Identity;
*Update = *Geo::OGRc::Layer_Update;
*Clip = *Geo::OGRc::Layer_Clip;
*Erase = *Geo::OGRc::Layer_Erase;
*GetStyleTable = *Geo::OGRc::Layer_GetStyleTable;
*SetStyleTable = *Geo::OGRc::Layer_SetStyleTable;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::Feature ##############

package Geo::OGR::Feature;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::OGR );
%OWNER = ();
sub DESTROY {
    my $self;
    if ($_[0]->isa('SCALAR')) {
        $self = $_[0];
    } else {
        return unless $_[0]->isa('HASH');
        $self = tied(%{$_[0]});
        return unless defined $self;
    }
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_Feature($self);
        delete $OWNER{$self};
    }
    $self->RELEASE_PARENTS();
}

use Carp;
sub new {
    my $pkg = shift;
    my $arg = blessed($_[0]);
    my $defn;
    if ($arg && $arg eq 'Geo::OGR::FeatureDefn') {
        $defn = $_[0];
    } else {
        $defn = Geo::OGR::FeatureDefn->new(@_);
    }
    my $self = Geo::OGRc::new_Feature($defn);
    bless $self, $pkg if defined($self);
}

*GetDefnRef = *Geo::OGRc::Feature_GetDefnRef;
*_SetGeometry = *Geo::OGRc::Feature__SetGeometry;
*SetGeometryDirectly = *Geo::OGRc::Feature_SetGeometryDirectly;
*GetGeometryRef = *Geo::OGRc::Feature_GetGeometryRef;
*SetGeomField = *Geo::OGRc::Feature_SetGeomField;
*SetGeomFieldDirectly = *Geo::OGRc::Feature_SetGeomFieldDirectly;
*GetGeomFieldRef = *Geo::OGRc::Feature_GetGeomFieldRef;
*Clone = *Geo::OGRc::Feature_Clone;
*Equal = *Geo::OGRc::Feature_Equal;
*GetFieldCount = *Geo::OGRc::Feature_GetFieldCount;
*GetFieldDefnRef = *Geo::OGRc::Feature_GetFieldDefnRef;
*GetGeomFieldCount = *Geo::OGRc::Feature_GetGeomFieldCount;
*GetGeomFieldDefnRef = *Geo::OGRc::Feature_GetGeomFieldDefnRef;
*GetFieldAsString = *Geo::OGRc::Feature_GetFieldAsString;
*GetFieldAsInteger = *Geo::OGRc::Feature_GetFieldAsInteger;
*GetFieldAsInteger64 = *Geo::OGRc::Feature_GetFieldAsInteger64;
*GetFieldAsDouble = *Geo::OGRc::Feature_GetFieldAsDouble;
*GetFieldAsDateTime = *Geo::OGRc::Feature_GetFieldAsDateTime;
*GetFieldAsIntegerList = *Geo::OGRc::Feature_GetFieldAsIntegerList;
*GetFieldAsDoubleList = *Geo::OGRc::Feature_GetFieldAsDoubleList;
*GetFieldAsStringList = *Geo::OGRc::Feature_GetFieldAsStringList;
*GetFieldAsBinary = *Geo::OGRc::Feature_GetFieldAsBinary;
*IsFieldSet = *Geo::OGRc::Feature_IsFieldSet;
*GetFieldIndex = *Geo::OGRc::Feature_GetFieldIndex;
*GetGeomFieldIndex = *Geo::OGRc::Feature_GetGeomFieldIndex;
*GetFID = *Geo::OGRc::Feature_GetFID;
*SetFID = *Geo::OGRc::Feature_SetFID;
*DumpReadable = *Geo::OGRc::Feature_DumpReadable;
*_UnsetField = *Geo::OGRc::Feature__UnsetField;
*SetFieldInteger64 = *Geo::OGRc::Feature_SetFieldInteger64;
*_SetField = *Geo::OGRc::Feature__SetField;
*SetFieldIntegerList = *Geo::OGRc::Feature_SetFieldIntegerList;
*SetFieldDoubleList = *Geo::OGRc::Feature_SetFieldDoubleList;
*SetFieldStringList = *Geo::OGRc::Feature_SetFieldStringList;
*SetFieldBinaryFromHexString = *Geo::OGRc::Feature_SetFieldBinaryFromHexString;
*_SetFrom = *Geo::OGRc::Feature__SetFrom;
*SetFromWithMap = *Geo::OGRc::Feature_SetFromWithMap;
*GetStyleString = *Geo::OGRc::Feature_GetStyleString;
*SetStyleString = *Geo::OGRc::Feature_SetStyleString;
*GetFieldType = *Geo::OGRc::Feature_GetFieldType;
*_Validate = *Geo::OGRc::Feature__Validate;
*FillUnsetWithDefault = *Geo::OGRc::Feature_FillUnsetWithDefault;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::FeatureDefn ##############

package Geo::OGR::FeatureDefn;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::OGR );
%OWNER = ();
sub DESTROY {
    my $self;
    if ($_[0]->isa('SCALAR')) {
        $self = $_[0];
    } else {
        return unless $_[0]->isa('HASH');
        $self = tied(%{$_[0]});
        return unless defined $self;
    }
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_FeatureDefn($self);
        delete $OWNER{$self};
    }
    $self->RELEASE_PARENTS();
}

use strict;
use warnings;
use Carp;
use Scalar::Util 'blessed';
sub new {
    my $pkg = shift;
    my %schema;
    if (@_ == 1 and ref($_[0]) eq 'HASH') {
        %schema = %{$_[0]};
    } elsif (@_ and @_ % 2 == 0) {
        %schema = @_;
    }
    my $fields = $schema{Fields};
    confess "The Fields argument is not a reference to an array." if $fields and ref($fields) ne 'ARRAY';
    $schema{Name} = '' unless exists $schema{Name};
    my $self = Geo::OGRc::new_FeatureDefn($schema{Name});
    bless $self, $pkg;
    my $gt = $schema{GeometryType};
    if ($fields) {
        $self->DeleteGeomFieldDefn(0); # either default behavior or argument specified
    } else {
        $self->GeometryType($schema{GeometryType}) if exists $schema{GeometryType};
    }
    $self->StyleIgnored($schema{StyleIgnored}) if exists $schema{StyleIgnored};
    for my $fd (@{$fields}) {
        my $d = $fd;
        if (ref($fd) eq 'HASH') {
            if ($fd->{GeometryType} or exists $Geo::OGR::Geometry::TYPE_STRING2INT{$fd->{Type}}) {
                $d = Geo::OGR::GeomFieldDefn->new(%$fd);
            } else {
                $d = Geo::OGR::FieldDefn->new(%$fd);
            }
        }
        if (blessed($d) and $d->isa('Geo::OGR::FieldDefn')) {
            AddFieldDefn($self, $d);
        } elsif (blessed($d) and $d->isa('Geo::OGR::GeomFieldDefn')) {
            AddGeomFieldDefn($self, $d);
        } else {
            confess "Item in field list does not define a field.";
        }
    }
    return $self;
}

*GetName = *Geo::OGRc::FeatureDefn_GetName;
*GetFieldCount = *Geo::OGRc::FeatureDefn_GetFieldCount;
*GetFieldDefn = *Geo::OGRc::FeatureDefn_GetFieldDefn;
*GetFieldIndex = *Geo::OGRc::FeatureDefn_GetFieldIndex;
*AddFieldDefn = *Geo::OGRc::FeatureDefn_AddFieldDefn;
*GetGeomFieldCount = *Geo::OGRc::FeatureDefn_GetGeomFieldCount;
*GetGeomFieldDefn = *Geo::OGRc::FeatureDefn_GetGeomFieldDefn;
*GetGeomFieldIndex = *Geo::OGRc::FeatureDefn_GetGeomFieldIndex;
*AddGeomFieldDefn = *Geo::OGRc::FeatureDefn_AddGeomFieldDefn;
*DeleteGeomFieldDefn = *Geo::OGRc::FeatureDefn_DeleteGeomFieldDefn;
*GetGeomType = *Geo::OGRc::FeatureDefn_GetGeomType;
*SetGeomType = *Geo::OGRc::FeatureDefn_SetGeomType;
*GetReferenceCount = *Geo::OGRc::FeatureDefn_GetReferenceCount;
*IsGeometryIgnored = *Geo::OGRc::FeatureDefn_IsGeometryIgnored;
*SetGeometryIgnored = *Geo::OGRc::FeatureDefn_SetGeometryIgnored;
*IsStyleIgnored = *Geo::OGRc::FeatureDefn_IsStyleIgnored;
*SetStyleIgnored = *Geo::OGRc::FeatureDefn_SetStyleIgnored;
*IsSame = *Geo::OGRc::FeatureDefn_IsSame;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::FieldDefn ##############

package Geo::OGR::FieldDefn;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::OGR );
%OWNER = ();
sub DESTROY {
    my $self;
    if ($_[0]->isa('SCALAR')) {
        $self = $_[0];
    } else {
        return unless $_[0]->isa('HASH');
        $self = tied(%{$_[0]});
        return unless defined $self;
    }
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_FieldDefn($self);
        delete $OWNER{$self};
    }
    $self->RELEASE_PARENTS();
}

use Carp;
sub new {
    my $pkg = shift;
    my ($name, $type) = ('unnamed', 'String');
    my %args;
    if (@_ == 0) {
    } elsif (@_ == 1) {
        $name = shift;
    } elsif (@_ == 2 and not $Geo::OGR::FieldDefn::SCHEMA_KEYS{$_[0]}) {
        $name = shift;
        $type = shift;
    } else {
        my %named = @_;
        for my $key (keys %named) {
            if ($Geo::OGR::FieldDefn::SCHEMA_KEYS{$key}) {
                $args{$key} = $named{$key};
            } else {
                carp "Unrecognized argument: '$key'.";
            }
        }
        $name = $args{Name} if exists $args{Name};
        delete $args{Name};
        $type = $args{Type} if exists $args{Type};
        delete $args{Type};
    }
    confess "Unknown field type: '$type'." unless exists $Geo::OGR::FieldDefn::TYPE_STRING2INT{$type};
    $type = $Geo::OGR::FieldDefn::TYPE_STRING2INT{$type};
    my $self = Geo::OGRc::new_FieldDefn($name, $type);
    if (defined($self)) {
        bless $self, $pkg;
        $self->Schema(%args);
    }
    return $self;
}

*GetName = *Geo::OGRc::FieldDefn_GetName;
*GetNameRef = *Geo::OGRc::FieldDefn_GetNameRef;
*SetName = *Geo::OGRc::FieldDefn_SetName;
*GetType = *Geo::OGRc::FieldDefn_GetType;
*SetType = *Geo::OGRc::FieldDefn_SetType;
*GetSubType = *Geo::OGRc::FieldDefn_GetSubType;
*SetSubType = *Geo::OGRc::FieldDefn_SetSubType;
*GetJustify = *Geo::OGRc::FieldDefn_GetJustify;
*SetJustify = *Geo::OGRc::FieldDefn_SetJustify;
*GetWidth = *Geo::OGRc::FieldDefn_GetWidth;
*SetWidth = *Geo::OGRc::FieldDefn_SetWidth;
*GetPrecision = *Geo::OGRc::FieldDefn_GetPrecision;
*SetPrecision = *Geo::OGRc::FieldDefn_SetPrecision;
*GetTypeName = *Geo::OGRc::FieldDefn_GetTypeName;
*GetFieldTypeName = *Geo::OGRc::FieldDefn_GetFieldTypeName;
*IsIgnored = *Geo::OGRc::FieldDefn_IsIgnored;
*SetIgnored = *Geo::OGRc::FieldDefn_SetIgnored;
*IsNullable = *Geo::OGRc::FieldDefn_IsNullable;
*SetNullable = *Geo::OGRc::FieldDefn_SetNullable;
*GetDefault = *Geo::OGRc::FieldDefn_GetDefault;
*SetDefault = *Geo::OGRc::FieldDefn_SetDefault;
*IsDefaultDriverSpecific = *Geo::OGRc::FieldDefn_IsDefaultDriverSpecific;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::GeomFieldDefn ##############

package Geo::OGR::GeomFieldDefn;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::OGR );
%OWNER = ();
%ITERATORS = ();
sub DESTROY {
    return unless $_[0]->isa('HASH');
    my $self = tied(%{$_[0]});
    return unless defined $self;
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_GeomFieldDefn($self);
        delete $OWNER{$self};
    }
}

use Carp;
sub new {
    my $pkg = shift;
    my ($name, $type) = ('geom', 'Unknown');
    my %args;
    if (@_ == 0) {
    } elsif (@_ == 1) {
        $name = shift;
    } elsif (@_ == 2 and not $Geo::OGR::GeomFieldDefn::SCHEMA_KEYS{$_[0]}) {
        $name = shift;
        $type = shift;
    } else {
        my %named = @_;
        for my $key (keys %named) {
            if ($Geo::OGR::GeomFieldDefn::SCHEMA_KEYS{$key}) {
                $args{$key} = $named{$key};
            } else {
                carp "Unrecognized argument: '$key'.";
            }
        }
        $name = $args{Name} if exists $args{Name};
        delete $args{Name};
        $type = $args{Type} if $args{Type};
        delete $args{Type};
        $type = $args{GeometryType} if $args{GeometryType};
        delete $args{GeometryType};
    }
    confess "Unknown geometry type: '$type'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
    $type = $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
    my $self = Geo::OGRc::new_GeomFieldDefn($name, $type);
    if (defined($self)) {
        bless $self, $pkg;
        $self->Schema(%args);
    }
    return $self;
}

*GetName = *Geo::OGRc::GeomFieldDefn_GetName;
*GetNameRef = *Geo::OGRc::GeomFieldDefn_GetNameRef;
*SetName = *Geo::OGRc::GeomFieldDefn_SetName;
*GetType = *Geo::OGRc::GeomFieldDefn_GetType;
*SetType = *Geo::OGRc::GeomFieldDefn_SetType;
*GetSpatialRef = *Geo::OGRc::GeomFieldDefn_GetSpatialRef;
*SetSpatialRef = *Geo::OGRc::GeomFieldDefn_SetSpatialRef;
*IsIgnored = *Geo::OGRc::GeomFieldDefn_IsIgnored;
*SetIgnored = *Geo::OGRc::GeomFieldDefn_SetIgnored;
*IsNullable = *Geo::OGRc::GeomFieldDefn_IsNullable;
*SetNullable = *Geo::OGRc::GeomFieldDefn_SetNullable;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


############# Class : Geo::OGR::Geometry ##############

package Geo::OGR::Geometry;
use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);
@ISA = qw( Geo::OGR );
%OWNER = ();
sub DESTROY {
    my $self;
    if ($_[0]->isa('SCALAR')) {
        $self = $_[0];
    } else {
        return unless $_[0]->isa('HASH');
        $self = tied(%{$_[0]});
        return unless defined $self;
    }
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        Geo::OGRc::delete_Geometry($self);
        delete $OWNER{$self};
    }
    $self->RELEASE_PARENTS();
}

use Carp;
sub new {
    my $pkg = shift;
    my ($type, $wkt, $wkb, $gml, $json, $srs, $points, $arc);
    my %param;
    if (@_ == 1 and ref($_[0]) eq 'HASH') {
        %param = %{$_[0]};
    } elsif (@_ % 2 == 0) {
        %param = @_;
    } else {
        ($param{GeometryType}) = @_;
    }
    $type = ($param{type} or $param{Type} or $param{GeometryType});
    $srs = ($param{srs} or $param{SRS});
    $wkt = ($param{wkt} or $param{WKT});
    $wkb = ($param{wkb} or $param{WKB});
    my $hex = ($param{hexewkb} or $param{HEXEWKB}); # PostGIS HEX EWKB
    my $srid;
    if ($hex) {
        # get and remove SRID
        $srid = substr($hex, 10, 8);
        substr($hex, 10, 8) = '';
    } else {
        $hex = ($param{hexwkb} or $param{HEXWKB});
    }
    if ($hex) {
        $wkb = '';
        for (my $i = 0; $i < length($hex); $i+=2) {
            $wkb .= chr(hex(substr($hex,$i,2)));
        }
    }
    $gml = ($param{gml} or $param{GML});
    $json = ($param{geojson} or $param{GeoJSON});
    $points = $param{Points};
    $arc = ($param{arc} or $param{Arc});
    my $self;
    if (defined $wkt) {
        $self = Geo::OGRc::CreateGeometryFromWkt($wkt, $srs);
    } elsif (defined $wkb) {
        $self = Geo::OGRc::CreateGeometryFromWkb($wkb, $srs);
    } elsif (defined $gml) {
        $self = Geo::OGRc::CreateGeometryFromGML($gml);
    } elsif (defined $json) {
        $self = Geo::OGRc::CreateGeometryFromJson($json);
    } elsif (defined $type) {
        confess "Unknown geometry type: '$type'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
        $type = $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
        $self = Geo::OGRc::new_Geometry($type); # flattens the type
        SetCoordinateDimension($self, 3) if Geo::OGR::GT_HasZ($type);
    } elsif (defined $arc) {
        $self = Geo::OGRc::ApproximateArcAngles(@$arc);
    } else {
        confess "Missing a parameter when creating a Geo::OGR::Geometry object.";
    }
    bless $self, $pkg if defined $self;
    $self->Points($points) if $points;
    return $self;
}

*ExportToWkt = *Geo::OGRc::Geometry_ExportToWkt;
*ExportToIsoWkt = *Geo::OGRc::Geometry_ExportToIsoWkt;
*_ExportToWkb = *Geo::OGRc::Geometry__ExportToWkb;
*ExportToIsoWkb = *Geo::OGRc::Geometry_ExportToIsoWkb;
*ExportToGML = *Geo::OGRc::Geometry_ExportToGML;
*ExportToKML = *Geo::OGRc::Geometry_ExportToKML;
*ExportToJson = *Geo::OGRc::Geometry_ExportToJson;
*AddPoint_3D = *Geo::OGRc::Geometry_AddPoint_3D;
*AddPoint_2D = *Geo::OGRc::Geometry_AddPoint_2D;
*AddGeometryDirectly = *Geo::OGRc::Geometry_AddGeometryDirectly;
*AddGeometry = *Geo::OGRc::Geometry_AddGeometry;
*Clone = *Geo::OGRc::Geometry_Clone;
*GetGeometryType = *Geo::OGRc::Geometry_GetGeometryType;
*GetGeometryName = *Geo::OGRc::Geometry_GetGeometryName;
*Length = *Geo::OGRc::Geometry_Length;
*Area = *Geo::OGRc::Geometry_Area;
*GetArea = *Geo::OGRc::Geometry_GetArea;
*GetPointCount = *Geo::OGRc::Geometry_GetPointCount;
*GetX = *Geo::OGRc::Geometry_GetX;
*GetY = *Geo::OGRc::Geometry_GetY;
*GetZ = *Geo::OGRc::Geometry_GetZ;
*GetPoint_3D = *Geo::OGRc::Geometry_GetPoint_3D;
*GetPoint_2D = *Geo::OGRc::Geometry_GetPoint_2D;
*GetGeometryCount = *Geo::OGRc::Geometry_GetGeometryCount;
*SetPoint_3D = *Geo::OGRc::Geometry_SetPoint_3D;
*SetPoint_2D = *Geo::OGRc::Geometry_SetPoint_2D;
*GetGeometryRef = *Geo::OGRc::Geometry_GetGeometryRef;
*Simplify = *Geo::OGRc::Geometry_Simplify;
*SimplifyPreserveTopology = *Geo::OGRc::Geometry_SimplifyPreserveTopology;
*Boundary = *Geo::OGRc::Geometry_Boundary;
*GetBoundary = *Geo::OGRc::Geometry_GetBoundary;
*ConvexHull = *Geo::OGRc::Geometry_ConvexHull;
*Buffer = *Geo::OGRc::Geometry_Buffer;
*Intersection = *Geo::OGRc::Geometry_Intersection;
*Union = *Geo::OGRc::Geometry_Union;
*UnionCascaded = *Geo::OGRc::Geometry_UnionCascaded;
*Difference = *Geo::OGRc::Geometry_Difference;
*SymDifference = *Geo::OGRc::Geometry_SymDifference;
*SymmetricDifference = *Geo::OGRc::Geometry_SymmetricDifference;
*Distance = *Geo::OGRc::Geometry_Distance;
*Empty = *Geo::OGRc::Geometry_Empty;
*IsEmpty = *Geo::OGRc::Geometry_IsEmpty;
*IsValid = *Geo::OGRc::Geometry_IsValid;
*IsSimple = *Geo::OGRc::Geometry_IsSimple;
*IsRing = *Geo::OGRc::Geometry_IsRing;
*Intersects = *Geo::OGRc::Geometry_Intersects;
*Intersect = *Geo::OGRc::Geometry_Intersect;
*Equals = *Geo::OGRc::Geometry_Equals;
*Equal = *Geo::OGRc::Geometry_Equal;
*Disjoint = *Geo::OGRc::Geometry_Disjoint;
*Touches = *Geo::OGRc::Geometry_Touches;
*Crosses = *Geo::OGRc::Geometry_Crosses;
*Within = *Geo::OGRc::Geometry_Within;
*Contains = *Geo::OGRc::Geometry_Contains;
*Overlaps = *Geo::OGRc::Geometry_Overlaps;
*TransformTo = *Geo::OGRc::Geometry_TransformTo;
*Transform = *Geo::OGRc::Geometry_Transform;
*GetSpatialReference = *Geo::OGRc::Geometry_GetSpatialReference;
*AssignSpatialReference = *Geo::OGRc::Geometry_AssignSpatialReference;
*CloseRings = *Geo::OGRc::Geometry_CloseRings;
*FlattenTo2D = *Geo::OGRc::Geometry_FlattenTo2D;
*Segmentize = *Geo::OGRc::Geometry_Segmentize;
*GetEnvelope = *Geo::OGRc::Geometry_GetEnvelope;
*GetEnvelope3D = *Geo::OGRc::Geometry_GetEnvelope3D;
*Centroid = *Geo::OGRc::Geometry_Centroid;
*PointOnSurface = *Geo::OGRc::Geometry_PointOnSurface;
*WkbSize = *Geo::OGRc::Geometry_WkbSize;
*GetCoordinateDimension = *Geo::OGRc::Geometry_GetCoordinateDimension;
*SetCoordinateDimension = *Geo::OGRc::Geometry_SetCoordinateDimension;
*GetDimension = *Geo::OGRc::Geometry_GetDimension;
*HasCurveGeometry = *Geo::OGRc::Geometry_HasCurveGeometry;
*GetLinearGeometry = *Geo::OGRc::Geometry_GetLinearGeometry;
*GetCurveGeometry = *Geo::OGRc::Geometry_GetCurveGeometry;
*Value = *Geo::OGRc::Geometry_Value;
*Move = *Geo::OGRc::Geometry_Move;
sub DISOWN {
    my $self = shift;
    my $ptr = tied(%$self);
    delete $OWNER{$ptr};
}

sub ACQUIRE {
    my $self = shift;
    my $ptr = tied(%$self);
    $OWNER{$ptr} = 1;
}


# ------- VARIABLE STUBS --------

package Geo::OGR;

*wkb25DBit = *Geo::OGRc::wkb25DBit;
*wkb25Bit = *Geo::OGRc::wkb25Bit;
*wkbUnknown = *Geo::OGRc::wkbUnknown;
*wkbPoint = *Geo::OGRc::wkbPoint;
*wkbLineString = *Geo::OGRc::wkbLineString;
*wkbPolygon = *Geo::OGRc::wkbPolygon;
*wkbMultiPoint = *Geo::OGRc::wkbMultiPoint;
*wkbMultiLineString = *Geo::OGRc::wkbMultiLineString;
*wkbMultiPolygon = *Geo::OGRc::wkbMultiPolygon;
*wkbGeometryCollection = *Geo::OGRc::wkbGeometryCollection;
*wkbCircularString = *Geo::OGRc::wkbCircularString;
*wkbCompoundCurve = *Geo::OGRc::wkbCompoundCurve;
*wkbCurvePolygon = *Geo::OGRc::wkbCurvePolygon;
*wkbMultiCurve = *Geo::OGRc::wkbMultiCurve;
*wkbMultiSurface = *Geo::OGRc::wkbMultiSurface;
*wkbNone = *Geo::OGRc::wkbNone;
*wkbLinearRing = *Geo::OGRc::wkbLinearRing;
*wkbCircularStringZ = *Geo::OGRc::wkbCircularStringZ;
*wkbCompoundCurveZ = *Geo::OGRc::wkbCompoundCurveZ;
*wkbCurvePolygonZ = *Geo::OGRc::wkbCurvePolygonZ;
*wkbMultiCurveZ = *Geo::OGRc::wkbMultiCurveZ;
*wkbMultiSurfaceZ = *Geo::OGRc::wkbMultiSurfaceZ;
*wkbPoint25D = *Geo::OGRc::wkbPoint25D;
*wkbLineString25D = *Geo::OGRc::wkbLineString25D;
*wkbPolygon25D = *Geo::OGRc::wkbPolygon25D;
*wkbMultiPoint25D = *Geo::OGRc::wkbMultiPoint25D;
*wkbMultiLineString25D = *Geo::OGRc::wkbMultiLineString25D;
*wkbMultiPolygon25D = *Geo::OGRc::wkbMultiPolygon25D;
*wkbGeometryCollection25D = *Geo::OGRc::wkbGeometryCollection25D;
*OFTInteger = *Geo::OGRc::OFTInteger;
*OFTIntegerList = *Geo::OGRc::OFTIntegerList;
*OFTReal = *Geo::OGRc::OFTReal;
*OFTRealList = *Geo::OGRc::OFTRealList;
*OFTString = *Geo::OGRc::OFTString;
*OFTStringList = *Geo::OGRc::OFTStringList;
*OFTWideString = *Geo::OGRc::OFTWideString;
*OFTWideStringList = *Geo::OGRc::OFTWideStringList;
*OFTBinary = *Geo::OGRc::OFTBinary;
*OFTDate = *Geo::OGRc::OFTDate;
*OFTTime = *Geo::OGRc::OFTTime;
*OFTDateTime = *Geo::OGRc::OFTDateTime;
*OFTInteger64 = *Geo::OGRc::OFTInteger64;
*OFTInteger64List = *Geo::OGRc::OFTInteger64List;
*OFSTNone = *Geo::OGRc::OFSTNone;
*OFSTBoolean = *Geo::OGRc::OFSTBoolean;
*OFSTInt16 = *Geo::OGRc::OFSTInt16;
*OFSTFloat32 = *Geo::OGRc::OFSTFloat32;
*OJUndefined = *Geo::OGRc::OJUndefined;
*OJLeft = *Geo::OGRc::OJLeft;
*OJRight = *Geo::OGRc::OJRight;
*wkbXDR = *Geo::OGRc::wkbXDR;
*wkbNDR = *Geo::OGRc::wkbNDR;
*NullFID = *Geo::OGRc::NullFID;
*ALTER_NAME_FLAG = *Geo::OGRc::ALTER_NAME_FLAG;
*ALTER_TYPE_FLAG = *Geo::OGRc::ALTER_TYPE_FLAG;
*ALTER_WIDTH_PRECISION_FLAG = *Geo::OGRc::ALTER_WIDTH_PRECISION_FLAG;
*ALTER_NULLABLE_FLAG = *Geo::OGRc::ALTER_NULLABLE_FLAG;
*ALTER_DEFAULT_FLAG = *Geo::OGRc::ALTER_DEFAULT_FLAG;
*ALTER_ALL_FLAG = *Geo::OGRc::ALTER_ALL_FLAG;
*F_VAL_NULL = *Geo::OGRc::F_VAL_NULL;
*F_VAL_GEOM_TYPE = *Geo::OGRc::F_VAL_GEOM_TYPE;
*F_VAL_WIDTH = *Geo::OGRc::F_VAL_WIDTH;
*F_VAL_ALLOW_NULL_WHEN_DEFAULT = *Geo::OGRc::F_VAL_ALLOW_NULL_WHEN_DEFAULT;
*F_VAL_ALL = *Geo::OGRc::F_VAL_ALL;
*OLCRandomRead = *Geo::OGRc::OLCRandomRead;
*OLCSequentialWrite = *Geo::OGRc::OLCSequentialWrite;
*OLCRandomWrite = *Geo::OGRc::OLCRandomWrite;
*OLCFastSpatialFilter = *Geo::OGRc::OLCFastSpatialFilter;
*OLCFastFeatureCount = *Geo::OGRc::OLCFastFeatureCount;
*OLCFastGetExtent = *Geo::OGRc::OLCFastGetExtent;
*OLCCreateField = *Geo::OGRc::OLCCreateField;
*OLCDeleteField = *Geo::OGRc::OLCDeleteField;
*OLCReorderFields = *Geo::OGRc::OLCReorderFields;
*OLCAlterFieldDefn = *Geo::OGRc::OLCAlterFieldDefn;
*OLCTransactions = *Geo::OGRc::OLCTransactions;
*OLCDeleteFeature = *Geo::OGRc::OLCDeleteFeature;
*OLCFastSetNextByIndex = *Geo::OGRc::OLCFastSetNextByIndex;
*OLCStringsAsUTF8 = *Geo::OGRc::OLCStringsAsUTF8;
*OLCIgnoreFields = *Geo::OGRc::OLCIgnoreFields;
*OLCCreateGeomField = *Geo::OGRc::OLCCreateGeomField;
*OLCCurveGeometries = *Geo::OGRc::OLCCurveGeometries;
*ODsCCreateLayer = *Geo::OGRc::ODsCCreateLayer;
*ODsCDeleteLayer = *Geo::OGRc::ODsCDeleteLayer;
*ODsCCreateGeomFieldAfterCreateLayer = *Geo::OGRc::ODsCCreateGeomFieldAfterCreateLayer;
*ODsCCurveGeometries = *Geo::OGRc::ODsCCurveGeometries;
*ODsCTransactions = *Geo::OGRc::ODsCTransactions;
*ODsCEmulatedTransactions = *Geo::OGRc::ODsCEmulatedTransactions;
*ODrCCreateDataSource = *Geo::OGRc::ODrCCreateDataSource;
*ODrCDeleteDataSource = *Geo::OGRc::ODrCDeleteDataSource;
*OLMD_FID64 = *Geo::OGRc::OLMD_FID64;
*TermProgress = *Geo::OGRc::TermProgress;


package Geo::OGR;
our $VERSION = '2.0002'; # this needs to be the same as that in gdal_perl.i

package Geo::OGR::Driver;
use strict;
use warnings;
use Carp;
use vars qw /@CAPABILITIES %CAPABILITIES/;
for (keys %Geo::OGR::) {
    push(@CAPABILITIES, $1), next if /^ODrC(\w+)/;
}
for my $s (@CAPABILITIES) {
    my $cap = eval "\$Geo::OGR::ODrC$s";
    $CAPABILITIES{$s} = $cap;
}

sub Capabilities {
    return @CAPABILITIES if @_ == 0;
    my $self = shift;
    my @cap;
    for my $cap (@CAPABILITIES) {
        push @cap, $cap if _TestCapability($self, $CAPABILITIES{$cap});
    }
    return @cap;
}

sub TestCapability {
    my($self, $cap) = @_;
    confess "No such capability defined for class Driver: '$cap'." unless defined $CAPABILITIES{$cap};
    return _TestCapability($self, $CAPABILITIES{$cap});
}

*Create = *CreateDataSource;
*Copy = *CopyDataSource;
*OpenDataSource = *Open;
*Delete = *DeleteDataSource;
*Name = *GetName;




package Geo::OGR::DataSource;
use strict;
use warnings;
use Carp;
use vars qw /@CAPABILITIES %CAPABILITIES %LAYERS %RESULT_SET/;
for (keys %Geo::OGR::) {
    push(@CAPABILITIES, $1), next if /^ODsC(\w+)/;
}
for my $s (@CAPABILITIES) {
    my $cap = eval "\$Geo::OGR::ODsC$s";
    $CAPABILITIES{$s} = $cap;
}

sub Capabilities {
    return @CAPABILITIES if @_ == 0;
    my $self = shift;
    my @cap;
    for my $cap (@CAPABILITIES) {
        push @cap, $cap if _TestCapability($self, $CAPABILITIES{$cap});
    }
    return @cap;
}

sub TestCapability {
    my($self, $cap) = @_;
    confess "No such capability defined for class DataSource: '$cap'." unless defined $CAPABILITIES{$cap};
    return _TestCapability($self, $CAPABILITIES{$cap});
}
*GetDriver = *_GetDriver;

sub new {
    my $pkg = shift;
    return Geo::OGR::Open(@_);
}

sub Open {
    return Geo::OGR::Open(@_);
}

sub OpenShared {
    return Geo::OGR::OpenShared(@_);
}

sub ExecuteSQL {
    my $self = shift;
    my $layer = $self->_ExecuteSQL(@_);
    $LAYERS{tied(%$layer)} = $self;
    $RESULT_SET{tied(%$layer)} = 1;
    return $layer;
}

sub ReleaseResultSet {
    # a no-op, _ReleaseResultSet is called from Layer::DESTROY
}

sub GetLayer {
    my($self, $name) = @_;
    my $layer = defined $name ? GetLayerByName($self, "$name") : GetLayerByIndex($self, 0);
    $name = '' unless defined $name;
    confess "No such layer: '$name'." unless $layer;
    $LAYERS{tied(%$layer)} = $self;
    return $layer;
}
*Layer = *GetLayer;

sub GetLayerNames {
    my $self = shift;
    my @names;
    for my $i (0..$self->GetLayerCount-1) {
        my $layer = GetLayerByIndex($self, $i);
        push @names, $layer->GetName;
    }
    return @names;
}
*Layers = *GetLayerNames;

sub CreateLayer {
    my $self = shift;
    my %defaults = ( Name => 'unnamed',
                     SRS => undef,
                     Options => {},
                     GeometryType => 'Unknown',
                     Schema => undef,
                     Fields => undef,
                     ApproxOK => 1);
    my %params;
    if (@_ == 0) {
    } elsif (ref($_[0]) eq 'HASH') {
        %params = %{$_[0]};
    } elsif (@_ % 2 == 0 and (defined $_[0] and exists $defaults{$_[0]})) {
        %params = @_;
    } else {
        ($params{Name}, $params{SRS}, $params{GeometryType}, $params{Options}, $params{Schema}) = @_;
    }
    for (keys %params) {
        carp "CreateLayer: unknown named parameter '$_'." unless exists $defaults{$_};
    }
    if (exists $params{Schema}) {
        my $s = $params{Schema};
        $params{GeometryType} = $s->{GeometryType} if exists $s->{GeometryType};
        $params{Fields} = $s->{Fields} if exists $s->{Fields};
        $params{Name} = $s->{Name} if exists $s->{Name};
    }
    $defaults{GeometryType} = 'None' if $params{Fields};
    for (keys %defaults) {
        $params{$_} = $defaults{$_} unless defined $params{$_};
    }
    confess "Unknown geometry type: '$params{GeometryType}'."
        unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$params{GeometryType}};
    my $gt = $Geo::OGR::Geometry::TYPE_STRING2INT{$params{GeometryType}};
    my $layer = _CreateLayer($self, $params{Name}, $params{SRS}, $gt, $params{Options});
    $LAYERS{tied(%$layer)} = $self;
    my $f = $params{Fields};
    if ($f) {
        confess "Named parameter 'Fields' must be a reference to an array." unless ref($f) eq 'ARRAY';
        for my $field (@$f) {
            $layer->CreateField($field);
        }
    }
    return $layer;
}

sub DeleteLayer {
    my ($self, $name) = @_;
    my $index;
    for my $i (0..$self->GetLayerCount-1) {
        my $layer = GetLayerByIndex($self, $i);
        $index = $i, last if $layer->GetName eq $name;
    }
    confess "No such layer: '$name'." unless defined $index;
    _DeleteLayer($self, $index);
}




package Geo::OGR::Layer;
use strict;
use warnings;
use Carp;
use Scalar::Util 'blessed';
use vars qw /@CAPABILITIES %CAPABILITIES  %DEFNS/;
for (keys %Geo::OGR::) {
    push(@CAPABILITIES, $1), next if /^OLC(\w+)/;
}
for my $s (@CAPABILITIES) {
    my $cap = eval "\$Geo::OGR::OLC$s";
    $CAPABILITIES{$s} = $cap;
}

sub DESTROY {
    my $self;
    if ($_[0]->isa('SCALAR')) {
        $self = $_[0];
    } else {
        return unless $_[0]->isa('HASH');
        $self = tied(%{$_[0]});
        return unless defined $self;
    }
    if ($Geo::OGR::DataSource::RESULT_SET{$self}) {
        $Geo::OGR::DataSource::LAYERS{$self}->_ReleaseResultSet($self);
        delete $Geo::OGR::DataSource::RESULT_SET{$self}
    }
    delete $ITERATORS{$self};
    if (exists $OWNER{$self}) {
        delete $OWNER{$self};
    }
    $self->RELEASE_PARENTS();
}

sub RELEASE_PARENTS {
    my $self = shift;
    delete $Geo::OGR::DataSource::LAYERS{$self};
}

sub Capabilities {
    return @CAPABILITIES if @_ == 0;
    my $self = shift;
    my @cap;
    for my $cap (@CAPABILITIES) {
        push @cap, $cap if _TestCapability($self, $CAPABILITIES{$cap});
    }
    return @cap;
}

sub TestCapability {
    my($self, $cap) = @_;
    return _TestCapability($self, $CAPABILITIES{$cap});
}

sub GetDataSource {
    my $self = shift;
    return $Geo::OGR::DataSource::LAYERS{$self};
}
*DataSource = *GetDataSource;

sub GetDefn {
    my $self = shift;
    my $defn = $self->GetLayerDefn;
    $DEFNS{$defn} = $self;
    return $defn;
}

sub CreateField {
    my $self = shift;
    my %defaults = ( ApproxOK => 1,
                     Type => '' );
    my %params;
    if (@_ == 0) {
    } elsif (ref($_[0]) eq 'HASH') {
        %params = %{$_[0]};
    } elsif (@_ % 2 == 0) {
        %params = @_;
    } else {
        ($params{Defn}) = @_;
    }
    for (keys %defaults) {
        $params{$_} = $defaults{$_} unless defined $params{$_};
    }
    if (blessed($params{Defn}) and $params{Defn}->isa('Geo::OGR::FieldDefn')) {
        $self->_CreateField($params{Defn}, $params{ApproxOK});
    } elsif (blessed($_[0]) and $params{Defn}->isa('Geo::OGR::GeomFieldDefn')) {
        $self->CreateGeomField($params{Defn}, $params{ApproxOK});
    } else {
        my $a = $params{ApproxOK};
        delete $params{ApproxOK};
        if (exists $params{GeometryType}) {
            $params{Type} = $params{GeometryType};
            delete $params{GeometryType};
        }
        if (exists $Geo::OGR::FieldDefn::TYPE_STRING2INT{$params{Type}}) {
            my $fd = Geo::OGR::FieldDefn->new(%params);
            _CreateField($self, $fd, $a);
        } else {
            my $fd = Geo::OGR::GeomFieldDefn->new(%params);
            CreateGeomField($self, $fd, $a);
        }
    }
}

sub AlterFieldDefn {
    my $self = shift;
    my $field = shift;
    my $index;
    eval {
        $index = $self->GetFieldIndex($field);
    };
    confess "Only non-spatial fields can be altered.\n$@" if $@;
    if (blessed($_[0]) and $_[0]->isa('Geo::OGR::FieldDefn')) {
        _AlterFieldDefn($self, $index, @_);
    } elsif (@_ % 2 == 0) {
        my %params = @_;
        my $definition = Geo::OGR::FieldDefn->new(%params);
        my $flags = 0;
        $flags |= 1 if exists $params{Name};
        $flags |= 2 if exists $params{Type};
        $flags |= 4 if exists $params{Width} or exists $params{Precision};
        $flags |= 8 if exists $params{Nullable};
        $flags |= 16 if exists $params{Default};
        _AlterFieldDefn($self, $index, $definition, $flags);
    } else {
        croak "Usage: AlterFieldDefn(\$Name, \%NamedParameters)";
    }
}

sub DeleteField {
    my($self, $fn) = @_;
    my $d = $self->GetDefn;
    my $index = $d->GetFieldIndex($fn);
    $index = $fn if $index < 0;
    eval {
        _DeleteField($self, $index);
    };
    confess "Field not found: '$fn'. Only non-spatial fields can be deleted." if $@;
}

sub GetSchema {
    my $self = shift;
    carp "Schema of a layer should not be set directly." if @_;
    if (@_ and @_ % 2 == 0) {
        my %schema = @_;
        if ($schema{Fields}) {
            for my $field (@{$schema{Fields}}) {
                $self->CreateField($field);
            }
        }
    }
    return $self->GetDefn->Schema;
}
*Schema = *GetSchema;

sub Row {
    my $self = shift;
    my $update = @_ > 0;
    my %row = @_;
    my $feature = defined $row{FID} ? $self->GetFeature($row{FID}) : $self->GetNextFeature;
    return unless $feature;
    my $ret;
    if (defined wantarray) {
        $ret = $feature->Row(@_);
    } else {
        $feature->Row(@_);
    }
    $self->SetFeature($feature) if $update;
    return unless defined wantarray;
    return $ret;
}

sub Tuple {
    my $self = shift;
    my $FID = shift;
    my $feature = defined $FID ? $self->GetFeature($FID) : $self->GetNextFeature;
    return unless $feature;
    my $set = @_ > 0;
    unshift @_, $feature->GetFID if $set;
    my @ret;
    if (defined wantarray) {
        @ret = $feature->Tuple(@_);
    } else {
        $feature->Tuple(@_);
    }
    $self->SetFeature($feature) if $set;
    return unless defined wantarray;
    return @ret;
}

sub SpatialFilter {
    my $self = shift;
    $self->SetSpatialFilter($_[0]) if @_ == 1;
    $self->SetSpatialFilterRect(@_) if @_ == 4;
    return unless defined wantarray;
    $self->GetSpatialFilter;
}

sub InsertFeature {
    my $self = shift;
    my $feature = shift;
    confess "Usage: \$feature->InsertFeature(reference to a hash or array)." unless ref($feature);
    my $new = Geo::OGR::Feature->new($self->GetDefn);
    if (ref($feature) eq 'HASH') {
        $new->Row(%$feature);
    } elsif (ref($feature) eq 'ARRAY') {
        $new->Tuple(@$feature);
    } elsif (blessed($feature) and $feature->isa('Geo::OGR::Feature')) {
        $new->Row($feature->Row);
    }
    $self->CreateFeature($new);
}

sub ForFeatures {
    my $self = shift;
    my $code = shift;
    my $in_place = shift;
    $self->ResetReading;
    while (my $f = $self->GetNextFeature) {
        $code->($f);
        $self->SetFeature($f) if $in_place;
    };
}

sub ForGeometries {
    my $self = shift;
    my $code = shift;
    my $in_place = shift;
    $self->ResetReading;
    while (my $f = $self->GetNextFeature) {
        my $g = $f->Geometry();
        $code->($g);
        if ($in_place) {
            $f->Geometry($g);
            $self->SetFeature($f);
        }
    }
}

sub GetFieldNames {
    my $self = shift;
    my $d = $self->GetDefn;
    my @ret;
    for (my $i = 0; $i < $d->GetFieldCount; $i++) {
        push @ret, $d->GetFieldDefn($i)->Name();
    }
    for (my $i = 0; $i < $d->GetGeomFieldCount; $i++) {
        push @ret, $d->GetGeomFieldDefn($i)->Name();
    }
    return @ret;
}

sub GetFieldDefn {
    my ($self, $name) = @_;
    my $d = $self->GetDefn;
    for (my $i = 0; $i < $d->GetFieldCount; $i++) {
        my $fd = $d->GetFieldDefn($i);
        return $fd if $fd->Name eq $name;
    }
    for (my $i = 0; $i < $d->GetGeomFieldCount; $i++) {
        my $fd = $d->GetGeomFieldDefn($i);
        return $fd if $fd->Name eq $name;
    }
    confess "No such field: '$name'.";
}

sub GeometryType {
    my $self = shift;
    my $d = $self->GetDefn;
    my $fd = $d->GetGeomFieldDefn(0);
    return $fd->Type if $fd;
}

sub SpatialReference {
    my($self, $field, $sr) = @_;
    my $d = $self->GetDefn;
    my $i;
    if (not defined $field or (blessed($field) and $field->isa('Geo::OSR::SpatialReference'))) {
        $i = 0;
    } else {
        $i = $d->GetGeomFieldIndex($field);
    }
    my $d2 = $d->GetGeomFieldDefn($i);
    $d2->SpatialReference($sr) if defined $sr;
    return $d2->SpatialReference() if defined wantarray;
}




package Geo::OGR::FeatureDefn;
use strict;
use warnings;
use Encode;
use Carp;
use Scalar::Util 'blessed';

sub RELEASE_PARENTS {
    my $self = shift;
    delete $Geo::OGR::Feature::DEFNS{$self};
    delete $Geo::OGR::Layer::DEFNS{$self};
}

*Name = *GetName;

sub GetSchema {
    my $self = shift;
    carp "Schema of a feature definition should not be set directly." if @_;
    if (@_ and @_ % 2 == 0) {
        my %schema = @_;
        if ($schema{Fields}) {
            for my $field (@{$schema{Fields}}) {
                $self->AddField($field);
            }
        }
    }
    my %schema;
    $schema{Name} = $self->Name();
    $schema{StyleIgnored} = $self->StyleIgnored();
    $schema{Fields} = [];
    for my $i (0..$self->GetFieldCount-1) {
        my $s = $self->GetFieldDefn($i)->Schema;
        push @{$schema{Fields}}, $s;
    }
    for my $i (0..$self->GetGeomFieldCount-1) {
        my $s = $self->GetGeomFieldDefn($i)->Schema;
        push @{$schema{Fields}}, $s;
    }
    return wantarray ? %schema : \%schema;
}
*Schema = *GetSchema;

sub AddField {
    my $self = shift;
    confess "Read-only definition." if $Geo::OGR::Feature::DEFNS{$self} or $Geo::OGR::Layer::DEFNS{$self};
    my %params;
    if (@_ == 0) {
    } elsif (ref($_[0]) eq 'HASH') {
        %params = %{$_[0]};
    } elsif (@_ % 2 == 0) {
        %params = @_;
    }
    $params{Type} = '' unless defined $params{Type};
    if (exists $Geo::OGR::FieldDefn::TYPE_STRING2INT{$params{Type}}) {
        my $fd = Geo::OGR::FieldDefn->new(%params);
        $self->AddFieldDefn($fd);
    } else {
        my $fd = Geo::OGR::GeomFieldDefn->new(%params);
        $self->AddGeomFieldDefn($fd);
    }
}

sub DeleteField {
    my ($self, $name) = @_;
    confess "Read-only definition." if $Geo::OGR::Feature::DEFNS{$self} or $Geo::OGR::Layer::DEFNS{$self};
    for my $i (0..$self->GetFieldCount-1) {
        confess "Non-geometry fields cannot be deleted." if $self->GetFieldDefn($i)->Name eq $name;
    }
    for my $i (0..$self->GetGeomFieldCount-1) {
        $self->DeleteGeomFieldDefn($i) if $self->GetGeomFieldDefn($i)->Name eq $name;
    }
    confess "No such field: '$name'.";
}

sub GetFieldNames {
    my $self = shift;
    my @names = ();
    for my $i (0..$self->GetFieldCount-1) {
        push @names, $self->GetFieldDefn($i)->Name;
    }
    for my $i (0..$self->GetGeomFieldCount-1) {
        push @names, $self->GetGeomFieldDefn($i)->Name;
    }
    return @names;
}

sub GetFieldDefn {
    my ($self, $name) = @_;
    for my $i (0..$self->GetFieldCount-1) {
        my $fd = $self->GetFieldDefn($i);
        return $fd if $fd->Name eq $name;
    }
    for my $i (0..$self->GetGeomFieldCount-1) {
        my $fd = $self->GetGeomFieldDefn($i);
        return $fd if $fd->Name eq $name;
    }
    confess "No such field: '$name'.";
}

sub GeomType {
    my ($self, $type) = @_;
    confess "Read-only definition." if $Geo::OGR::Feature::DEFNS{$self} or $Geo::OGR::Layer::DEFNS{$self};
    if (defined $type) {
        confess "Unknown geometry data type: '$type'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
        $type = $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
        SetGeomType($self, $type);
    }
    return $Geo::OGR::Geometry::TYPE_INT2STRING{GetGeomType($self)} if defined wantarray;
}
*GeometryType = *GeomType;

sub GeometryIgnored {
    my $self = shift;
    SetGeometryIgnored($self, $_[0]) if @_;
    IsGeometryIgnored($self) if defined wantarray;
}

sub StyleIgnored {
    my $self = shift;
    SetStyleIgnored($self, $_[0]) if @_;
    IsStyleIgnored($self) if defined wantarray;
}




package Geo::OGR::Feature;
use strict;
use warnings;
use vars qw /%GEOMETRIES %DEFNS/;
use Carp;
use Encode;
use Scalar::Util 'blessed';

sub FETCH {
    my($self, $index) = @_;
    my $i;
    eval {$i = $self->_GetFieldIndex($index)};
    $self->GetField($i) unless $@;
    $i = $self->_GetGeomFieldIndex($index);
    $self->GetGeometry($i);
}

sub STORE {
    my $self = shift;
    my $index = shift;
    my $i;
    eval {$i = $self->_GetFieldIndex($index)};
    $self->SetField($i, @_) unless $@;
    $i = $self->_GetGeomFieldIndex($index);
    $self->SetGeometry($i, @_);
}

sub FID {
    my $self = shift;
    $self->SetFID($_[0]) if @_;
    return unless defined wantarray;
    $self->GetFID;
}

sub StyleString {
    my $self = shift;
    $self->SetStyleString($_[0]) if @_;
    return unless defined wantarray;
    $self->GetStyleString;
}

sub Validate {
    my $self = shift;
    my $flags = 0;
    for my $flag (@_) {
        my $f = eval '$Geo::OGR::'.uc($flag);
        $flags |= $f;
    }
    _Validate($self, $flags);
}

sub GetSchema {
    my $self = shift;
    confess "Schema of a feature cannot be set directly." if @_;
    return $self->GetDefnRef->Schema;
}
*Schema = *GetSchema;

sub Row {
    my $self = shift;
    my $nf = $self->GetFieldCount;
    my $ngf = $self->GetGeomFieldCount;
    if (@_) { # update
        my %row;
        if (@_ == 1 and ref($_[0]) eq 'HASH') {
            %row = %{$_[0]};
        } elsif (@_ and @_ % 2 == 0) {
            %row = @_;
        } else {
            confess 'Usage: $feature->Row(%FeatureData).';
        }
        $self->SetFID($row{FID}) if defined $row{FID};
        #$self->Geometry($schema, $row{Geometry}) if $row{Geometry};
        for my $name (keys %row) {
            next if $name eq 'FID';
            if ($name eq 'Geometry') {
                $self->SetGeometry(0, $row{$name});
                next;
            }
            my $f = 0;
            for my $i (0..$nf-1) {
                if ($self->GetFieldDefnRef($i)->Name eq $name) {
                    $self->SetField($i, $row{$name});
                    $f = 1;
                    last;
                }
            }
            next if $f;
            for my $i (0..$ngf-1) {
                if ($self->GetGeomFieldDefnRef($i)->Name eq $name) {
                    $self->SetGeometry($i, $row{$name});
                    $f = 1;
                    last;
                }
            }
            next if $f;
            carp "Feature->Row: Unknown field: '$name'.";
        }
    }
    return unless defined wantarray;
    my %row = ();
    for my $i (0..$nf-1) {
        my $name = $self->GetFieldDefnRef($i)->Name;
        $row{$name} = $self->GetField($i);
    }
    for my $i (0..$ngf-1) {
        my $name = $self->GetGeomFieldDefnRef($i)->Name;
        $name = 'Geometry' if $name eq '';
        $row{$name} = $self->GetGeometry($i);
    }
    $row{FID} = $self->GetFID;
    #$row{Geometry} = $self->Geometry;
    return \%row;
}

sub Tuple {
    my $self = shift;
    my $nf = $self->GetFieldCount;
    my $ngf = $self->GetGeomFieldCount;
    if (@_) {
        my $FID;
        $FID = shift if @_ == $nf + $ngf + 1;
        $self->SetFID($FID) if defined $FID;
        my $values = \@_;
        if (@$values != $nf + $ngf) {
            my $n = $nf + $ngf;
            confess "Too many or too few attribute values for a feature (need $n).";
        }
        my $index = 0; # index to non-geometry and geometry fields
        for my $i (0..$nf-1) {
            $self->SetField($i, $values->[$i]);
        }
        for my $i (0..$ngf-1) {
            $self->SetGeometry($i, $values->[$nf+$i]);
        }
    }
    return unless defined wantarray;
    my @ret = ($self->GetFID);
    for my $i (0..$nf-1) {
        my $v = $self->GetField($i);
        push @ret, $v;
    }
    for my $i (0..$ngf-1) {
        my $v = $self->GetGeometry($i);
        push @ret, $v;
    }
    return @ret;
}

sub GetDefn {
    my $self = shift;
    my $defn = $self->GetDefnRef;
    $DEFNS{$defn} = $self;
    return $defn;
}

*GetFieldNames = *Geo::OGR::Layer::GetFieldNames;
*GetFieldDefn = *Geo::OGR::Layer::GetFieldDefn;

sub _GetFieldIndex {
    my($self, $field) = @_;
    if ($field =~ /^\d+$/) {
        return $field if $field >= 0 and $field < $self->GetFieldCount;
    } else {
        for my $i (0..$self->GetFieldCount-1) {
            return $i if $self->GetFieldDefnRef($i)->Name eq $field;
        }
    }
    confess "No such field: '$field'.";
}

sub GetField {
    my($self, $field) = @_;
    $field = $self->_GetFieldIndex($field);
    return unless IsFieldSet($self, $field);
    my $type = GetFieldType($self, $field);
    if ($type == $Geo::OGR::OFTInteger) {
        return GetFieldAsInteger($self, $field);
    }
    if ($type == $Geo::OGR::OFTInteger64) {
        return GetFieldAsInteger64($self, $field);
    }
    if ($type == $Geo::OGR::OFTReal) {
        return GetFieldAsDouble($self, $field);
    }
    if ($type == $Geo::OGR::OFTString) {
        return GetFieldAsString($self, $field);
    }
    if ($type == $Geo::OGR::OFTIntegerList) {
        my $ret = GetFieldAsIntegerList($self, $field);
        return wantarray ? @$ret : $ret;
    }
    if ($type == $Geo::OGR::OFTInteger64List) {
        my $ret = GetFieldAsInteger64List($self, $field);
        return wantarray ? @$ret : $ret;
    }
    if ($type == $Geo::OGR::OFTRealList) {
        my $ret = GetFieldAsDoubleList($self, $field);
        return wantarray ? @$ret : $ret;
    }
    if ($type == $Geo::OGR::OFTStringList) {
        my $ret = GetFieldAsStringList($self, $field);
        return wantarray ? @$ret : $ret;
    }
    if ($type == $Geo::OGR::OFTBinary) {
        return GetFieldAsString($self, $field);
    }
    if ($type == $Geo::OGR::OFTDate) {
        my @ret = GetFieldAsDateTime($self, $field);
        # year, month, day, hour, minute, second, timezone
        return wantarray ? @ret[0..2] : [@ret[0..2]];
    }
    if ($type == $Geo::OGR::OFTTime) {
        my @ret = GetFieldAsDateTime($self, $field);
        return wantarray ? @ret[3..6] : [@ret[3..6]];
    }
    if ($type == $Geo::OGR::OFTDateTime) {
        return GetFieldAsDateTime($self, $field);
    }
    confess "Perl bindings do not support field type '$Geo::OGR::FieldDefn::TYPE_INT2STRING{$type}'.";
}

sub UnsetField {
    my($self, $field) = @_;
    $field = $self->_GetFieldIndex($field);
    _UnsetField($self, $field);
}

sub SetField {
    my $self = shift;
    my $field = shift;
    $field = $self->_GetFieldIndex($field);
    if (@_ == 0 or !defined($_[0])) {
        _UnsetField($self, $field);
        return;
    }
    my $list = ref($_[0]) ? $_[0] : [@_];
    my $type = GetFieldType($self, $field);
    if ($type == $Geo::OGR::OFTInteger or
        $type == $Geo::OGR::OFTInteger64 or
        $type == $Geo::OGR::OFTReal or
        $type == $Geo::OGR::OFTString or
        $type == $Geo::OGR::OFTBinary)
    {
        _SetField($self, $field, $_[0]);
    }
    elsif ($type == $Geo::OGR::OFTIntegerList) {
        SetFieldIntegerList($self, $field, $list);
    }
    elsif ($type == $Geo::OGR::OFTInteger64List) {
        SetFieldInteger64List($self, $field, $list);
    }
    elsif ($type == $Geo::OGR::OFTRealList) {
        SetFieldDoubleList($self, $field, $list);
    }
    elsif ($type == $Geo::OGR::OFTStringList) {
        SetFieldStringList($self, $field, $list);
    }
    elsif ($type == $Geo::OGR::OFTDate) {
        # year, month, day, hour, minute, second, timezone
        for my $i (0..6) {
            $list->[$i] = 0 unless defined $list->[$i];
        }
        _SetField($self, $field, @$list[0..6]);
    }
    elsif ($type == $Geo::OGR::OFTTime) {
        $list->[3] = 0 unless defined $list->[3];
        _SetField($self, $field, 0, 0, 0, @$list[0..3]);
    }
    elsif ($type == $Geo::OGR::OFTDateTime) {
        $list->[6] = 0 unless defined $list->[6];
        _SetField($self, $field, @$list[0..6]);
    }
    else {
        confess "Perl bindings do not support field type '$Geo::OGR::FieldDefn::TYPE_INT2STRING{$type}'.";
    }
}

sub Field {
    my $self = shift;
    my $field = shift;
    $self->SetField($field, @_) if @_;
    $self->GetField($field);
}

sub _GetGeomFieldIndex {
    my($self, $field) = @_;
    if (not defined $field) {
        return 0 if $self->GetGeomFieldCount > 0;
    } if ($field =~ /^\d+$/) {
        return $field if $field >= 0 and $field < $self->GetGeomFieldCount;
    } else {
        for my $i (0..$self->GetGeomFieldCount-1) {
            return $i if $self->GetGeomFieldDefn($i)->Name eq $field;
        }
        return 0 if $self->GetGeomFieldCount > 0 and $field eq 'Geometry';
    }
    confess "No such field: '$field'.";
}

sub Geometry {
    my $self = shift;
    my $field = (ref($_[0]) eq '' or (@_ > 2 and @_ % 2 == 1)) ? shift : undef;
    $field = $self->_GetGeomFieldIndex($field);
    if (@_) {
        my $type = $self->GetDefn->GetGeomFieldDefn($field)->Type;
        my $geometry;
        if (@_ == 1) {
            $geometry = $_[0];
        } elsif (@_ and @_ % 2 == 0) {
            %$geometry = @_;
        }
        if (blessed($geometry) and $geometry->isa('Geo::OGR::Geometry')) {
            confess "The type of the inserted geometry ('".$geometry->GeometryType."') is not the field type ('$type')."
                if $type ne 'Unknown' and $type ne $geometry->GeometryType;
            eval {
                $self->SetGeomFieldDirectly($field, $geometry);
            };
            confess "$@" if $@;
            $GEOMETRIES{tied(%{$geometry})} = $self;
        } elsif (ref($geometry) eq 'HASH') {
            $geometry->{GeometryType} = $type unless exists $geometry->{GeometryType};
            eval {
                $geometry = Geo::OGR::Geometry->new($geometry);
            };
            confess "The type of the inserted geometry ('".$geometry->GeometryType."') is not the field type ('$type')."
                if $type ne 'Unknown' and $type ne $geometry->GeometryType;
            eval {
                $self->SetGeomFieldDirectly($field, $geometry);
            };
            confess "$@" if $@;
        } else {
            confess "'@_' does not define a geometry.";
        }
    }
    return unless defined wantarray;
    my $geometry = $self->GetGeomFieldRef($field);
    return unless $geometry;
    $GEOMETRIES{tied(%{$geometry})} = $self;
    return $geometry;
}
*GetGeometry = *Geometry;
*SetGeometry = *Geometry;

sub SetFrom {
    my($self, $other) = @_;
    _SetFrom($self, $other), return if @_ <= 2;
    my $forgiving = $_[2];
    _SetFrom($self, $other, $forgiving), return if @_ <= 3;
    my $map = $_[3];
    my @list;
    for my $i (1..GetFieldCount($self)) {
        push @list, ($map->{$i} || -1);
    }
    SetFromWithMap($self, $other, 1, \@list);
}




package Geo::OGR::FieldDefn;
use strict;
use warnings;
use vars qw /
    %SCHEMA_KEYS
    @TYPES @SUB_TYPES @JUSTIFY_VALUES
    %TYPE_STRING2INT %TYPE_INT2STRING
    %SUB_TYPE_STRING2INT %SUB_TYPE_INT2STRING
    %JUSTIFY_STRING2INT %JUSTIFY_INT2STRING
    /;
use Carp;
use Encode;
%SCHEMA_KEYS = map {$_ => 1} qw/Name Type SubType Justify Width Precision Nullable Default Ignored/;
for (keys %Geo::OGR::) {
    push(@TYPES, $1), next if /^OFT(\w+)/;
    push(@SUB_TYPES, $1), next if /^OFST(\w+)/;
    push(@JUSTIFY_VALUES, $1), next if /^OJ(\w+)/;
}
for my $string (@TYPES) {
    my $int = eval "\$Geo::OGR::OFT$string";
    $TYPE_STRING2INT{$string} = $int;
    $TYPE_INT2STRING{$int} = $string;
}
for my $string (@SUB_TYPES) {
    my $int = eval "\$Geo::OGR::OFST$string";
    $SUB_TYPE_STRING2INT{$string} = $int;
    $SUB_TYPE_INT2STRING{$int} = $string;
}
for my $string (@JUSTIFY_VALUES) {
    my $int = eval "\$Geo::OGR::OJ$string";
    $JUSTIFY_STRING2INT{$string} = $int;
    $JUSTIFY_INT2STRING{$int} = $string;
}

sub Types {
    return @TYPES;
}

sub SubTypes {
    return @SUB_TYPES;
}

sub JustifyValues {
    return @JUSTIFY_VALUES;
}

sub Schema {
    my $self = shift;
    if (@_) {
        my %args = @_;
        for my $key (keys %SCHEMA_KEYS) {
            eval '$self->'.$key.'($args{'.$key.'}) if exists $args{'.$key.'}';
            croak $@ if $@;
        }
    }
    return unless defined wantarray;
    my %schema = ();
    for my $key (keys %SCHEMA_KEYS) {
        $schema{$key} = eval '$self->'.$key;
    }
    return wantarray ? %schema : \%schema;
}
*GetSchema = *Schema;
*SetSchema = *Schema;

sub Name {
    my $self = shift;
    SetName($self, $_[0]) if @_;
    GetName($self) if defined wantarray;
}

sub Type {
    my($self, $type) = @_;
    if (defined $type) {
        confess "Unknown field type: '$type'." unless exists $TYPE_STRING2INT{$type};
        $type = $TYPE_STRING2INT{$type};
        SetType($self, $type);
    }
    return $TYPE_INT2STRING{GetType($self)} if defined wantarray;
}

sub SubType {
    my($self, $sub_type) = @_;
    if (defined $sub_type) {
        confess "Unknown field sub type: '$sub_type'." unless exists $SUB_TYPE_STRING2INT{$sub_type};
        $sub_type = $SUB_TYPE_STRING2INT{$sub_type};
        SetSubType($self, $sub_type);
    }
    return $SUB_TYPE_INT2STRING{GetSubType($self)} if defined wantarray;
}

sub Justify {
    my($self, $justify) = @_;
    if (defined $justify) {
        confess "Unknown justify value: '$justify'." unless exists $JUSTIFY_STRING2INT{$justify};
        $justify = $JUSTIFY_STRING2INT{$justify} if exists $JUSTIFY_STRING2INT{$justify};
        SetJustify($self, $justify);
    }
    return $JUSTIFY_INT2STRING{GetJustify($self)} if defined wantarray;
}

sub Width {
    my $self = shift;
    SetWidth($self, $_[0]) if @_;
    GetWidth($self) if defined wantarray;
}

sub Precision {
    my $self = shift;
    SetPrecision($self, $_[0]) if @_;
    GetPrecision($self) if defined wantarray;
}

sub Nullable {
    my $self = shift;
    SetNullable($self, $_[0]) if @_;
    IsNullable($self) if defined wantarray;
}

sub Default {
    my $self = shift;
    SetDefault($self, $_[0]) if @_;
    GetDefault($self) if defined wantarray;
}

sub Ignored {
    my $self = shift;
    SetIgnored($self, $_[0]) if @_;
    IsIgnored($self) if defined wantarray;
}




package Geo::OGR::GeomFieldDefn;
use strict;
use warnings;
use vars qw / %SCHEMA_KEYS /;
use Carp;
use Scalar::Util 'blessed';
%SCHEMA_KEYS = map {$_ => 1} qw/Name Type SpatialReference Nullable Ignored/;

sub Schema {
    my $self = shift;
    if (@_) {
        my %args = @_;
        for my $key (keys %SCHEMA_KEYS) {
            eval '$self->'.$key.'($args{'.$key.'}) if exists $args{'.$key.'}';
            croak $@ if $@;
        }
    }
    return unless defined wantarray;
    my %schema = ();
    for my $key (keys %SCHEMA_KEYS) {
        $schema{$key} = eval '$self->'.$key;
    }
    return wantarray ? %schema : \%schema;
}
*GetSchema = *Schema;
*SetSchema = *Schema;

sub Name {
    my $self = shift;
    SetName($self, $_[0]) if @_;
    GetName($self) if defined wantarray;
}

sub Type {
    my($self, $type) = @_;
    if (defined $type) {
        confess "Unknown geometry type: '$type'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
        $type = $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
        SetType($self, $type);
    }
    $Geo::OGR::Geometry::TYPE_INT2STRING{GetType($self)} if defined wantarray;
}

sub Types {
  return @Geo::OGR::Geometry::GEOMETRY_TYPES;
}

sub SpatialReference {
    my $self = shift;
    SetSpatialRef($self, $_[0]) if @_;
    GetSpatialRef($self) if defined wantarray;
}

sub Nullable {
    my $self = shift;
    SetNullable($self, $_[0]) if @_;
    IsNullable($self) if defined wantarray;
}

sub Ignored {
    my $self = shift;
    SetIgnored($self, $_[0]) if @_;
    IsIgnored($self) if defined wantarray;
}




package Geo::OGR::Geometry;
use strict;
use warnings;
use Carp;
use vars qw /
    @BYTE_ORDER_TYPES @GEOMETRY_TYPES
    %BYTE_ORDER_STRING2INT %BYTE_ORDER_INT2STRING
    %TYPE_STRING2INT %TYPE_INT2STRING
    /;
@BYTE_ORDER_TYPES = qw/XDR NDR/;
for my $string (@BYTE_ORDER_TYPES) {
    my $int = eval "\$Geo::OGR::wkb$string";
    $BYTE_ORDER_STRING2INT{$string} = $int;
    $BYTE_ORDER_INT2STRING{$int} = $string;
}
for (keys %Geo::OGR::) {
    next if /^wkb25/;
    next if /^wkb.DR/;
    push(@GEOMETRY_TYPES, $1), next if /^wkb(\w+)/;
}
for my $string (@GEOMETRY_TYPES) {
    my $int = eval "\$Geo::OGR::wkb$string";
    $TYPE_STRING2INT{$string} = $int;
    $TYPE_INT2STRING{$int} = $string;
}

sub ByteOrders {
    return @BYTE_ORDER_TYPES;
}

sub GeometryTypes {
    return @GEOMETRY_TYPES;
}

sub RELEASE_PARENTS {
    my $self = shift;
    delete $Geo::OGR::Feature::GEOMETRIES{$self};
}

sub ApproximateArcAngles {
    my %p = @_;
    my %default = ( Center => [0,0,0],
                    PrimaryRadius => 1,
                    SecondaryAxis => 1,
                    Rotation => 0,
                    StartAngle => 0,
                    EndAngle => 360,
                    MaxAngleStepSizeDegrees => 4 
        );
    for my $p (keys %p) {
        if (exists $default{$p}) {
            $p{$p} = $default{$p} unless defined $p{$p};
        } else {
            carp "Unknown named parameter: '$p'.";
        }
    }
    for my $p (keys %default) {
        $p{$p} = $default{$p} unless exists $p{$p};
    }
    confess "Usage: Center => [x,y,z]." unless ref($p{Center}) eq 'ARRAY';
    for my $i (0..2) {
        $p{Center}->[$i] = 0 unless defined $p{Center}->[$i];
    }
    return Geo::OGR::ApproximateArcAngles($p{Center}->[0], $p{Center}->[1], $p{Center}->[2], $p{PrimaryRadius}, $p{SecondaryAxis}, $p{Rotation}, $p{StartAngle}, $p{EndAngle}, $p{MaxAngleStepSizeDegrees});
}

sub As {
    my $self = shift;
    my %param;
    if (@_ == 1 and ref($_[0]) eq 'HASH') {
        %param = %{$_[0]};
    } elsif (@_ % 2 == 0) {
        %param = @_;
    } else {
        ($param{Format}, $param{x}) = @_;
    }
    $param{ByteOrder} = 'XDR' unless $param{ByteOrder};
    my $f = $param{Format};
    my $x = $param{x};
    $x = $param{ByteOrder} unless defined $x;
    if ($f =~ /text/i) {
        return $self->AsText;
    } elsif ($f =~ /wkt/i) {
        if ($f =~ /iso/i) {
            return $self->ExportToIsoWkt;
        } else {
            return $self->AsText;
        }
    } elsif ($f =~ /binary/i) {
        return $self->AsBinary($x);        
    } elsif ($f =~ /wkb/i) {
        if ($f =~ /iso/i) {
            return $self->ExportToIsoWkb;
        } elsif ($f =~ /hexe/i) {
            $param{srid} = 'XDR' unless $param{srid};
            $x = $param{srid} unless defined $x;
            return $self->AsHEXEWKB($x);
        } elsif ($f =~ /hex/i) {
            return $self->AsHEXWKB;
        } else {
            return $self->AsBinary($x);
        }
    } elsif ($f =~ /gml/i) {
        return $self->AsGML;
    } elsif ($f =~ /kml/i) {
        return $self->AsKML;
    } elsif ($f =~ /json/i) {
        return $self->AsJSON;
    } else {
        confess "Unsupported format: $f.";
    }
}

sub AsHEXWKB {
    my ($self) = @_;
    my $wkb = _ExportToWkb($self, 1);
    my $hex = '';
    for (my $i = 0; $i < length($wkb); $i++) {
        my $x = sprintf("%x", ord(substr($wkb,$i,1)));
        $x = '0' . $x if length($x) == 1;
        $hex .= uc($x);
    }
    return $hex;
}

sub AsHEXEWKB {
    my ($self, $srid) = @_;
    my $hex = AsHEXWKB($self);
    if ($srid) {
        my $s = sprintf("%x", $srid);
        $srid = '';
        do {
            if (length($s) > 2) {
                $srid .= substr($s,-2,2);
                substr($s,-2,2) = '';
            } elsif (length($s) > 1) {
                $srid .= $s;
                $s = '';
            } else {
                $srid .= '0'.$s;
                $s = '';
            }
        } until $s eq '';
    } else {
        $srid = '00000000';
    }
    while (length($srid) < 8) {
        $srid .= '00';
    }
    substr($hex, 10, 0) = uc($srid);
    return $hex;
}

sub GeometryType {
    my $self = shift;
    return $TYPE_INT2STRING{$self->GetGeometryType};
}

sub CoordinateDimension {
    my $self = shift;
    SetCoordinateDimension($self, $_[0]) if @_;
    GetCoordinateDimension($self) if defined wantarray;
}

sub AddPoint {
    @_ == 4 ? AddPoint_3D(@_) : AddPoint_2D(@_);
}

sub SetPoint {
    @_ == 5 ? SetPoint_3D(@_) : SetPoint_2D(@_);
}

sub GetPoint {
    my($self, $i) = @_;
    $i = 0 unless defined $i;
    my $point = ($self->GetGeometryType & 0x80000000) == 0 ? GetPoint_2D($self, $i) : GetPoint_3D($self, $i);
    return @$point;
}

sub Point {
    my $self = shift;
    my $i;
    if (@_) {
        my $t = $self->GetGeometryType;
        if ($t == $Geo::OGR::wkbPoint) {
            shift if @_ > 2;
            $i = 0;
        } elsif ($t == $Geo::OGR::wkbPoint25D) {
            shift if @_ > 3;
            $i = 0;
        } else {
            my $i = shift;
        }
        SetPoint($self, $i, @_);
    }
    return GetPoint($self, $i) if defined wantarray;
}

sub Points {
    my $self = shift;
    my $t = $self->GetGeometryType;
    my $flat = not Geo::OGR::GT_HasZ($t);
    $t = Geo::OGR::GT_Flatten($t);
    $t = $TYPE_INT2STRING{$t};
    my $points = shift;
    if ($points) {
        Empty($self);
        if ($t eq 'Unknown' or $t eq 'None' or $t eq 'GeometryCollection') {
            confess "Can't set points of a geometry of type '$t'.";
        } elsif ($t eq 'Point') {
            # support both "Point" as a list of one point and one point
            if (ref($points->[0])) {
                $flat ?
                    AddPoint_2D($self, @{$points->[0]}[0..1]) :
                    AddPoint_3D($self, @{$points->[0]}[0..2]);
            } else {
                $flat ?
                    AddPoint_2D($self, @$points[0..1]) :
                    AddPoint_3D($self, @$points[0..2]);
            }
        } elsif ($t eq 'LineString' or $t eq 'LinearRing') {
            if ($flat) {
                for my $p (@$points) {
                    AddPoint_2D($self, @$p[0..1]);
                }
            } else{
                for my $p (@$points) {
                    AddPoint_3D($self, @$p[0..2]);
                }
            }
        } elsif ($t eq 'Polygon') {
            for my $r (@$points) {
                my $ring = Geo::OGR::Geometry->new('LinearRing');
                $ring->SetCoordinateDimension(3) unless $flat;
                $ring->Points($r);
                $self->AddGeometryDirectly($ring);
            }
        } elsif ($t eq 'MultiPoint') {
            for my $p (@$points) {
                my $point = Geo::OGR::Geometry->new($flat ? 'Point' : 'Point25D');
                $point->Points($p);
                $self->AddGeometryDirectly($point);
            }
        } elsif ($t eq 'MultiLineString') {
            for my $l (@$points) {
                my $linestring = Geo::OGR::Geometry->new($flat ? 'LineString' : 'LineString25D');
                $linestring->Points($l);
                $self->AddGeometryDirectly($linestring);
            }
        } elsif ($t eq 'MultiPolygon') {
            for my $p (@$points) {
                my $polygon = Geo::OGR::Geometry->new($flat ? 'Polygon' : 'Polygon25D');
                $polygon->Points($p);
                $self->AddGeometryDirectly($polygon);
            }
        }
    }
    return unless defined wantarray;
    $self->_GetPoints($flat);
}

sub _GetPoints {
    my($self, $flat) = @_;
    my @points;
    my $n = $self->GetGeometryCount;
    if ($n) {
        for my $i (0..$n-1) {
            push @points, $self->GetGeometryRef($i)->_GetPoints($flat);
        }
    } else {
        $n = $self->GetPointCount;
        if ($n == 1) {
            push @points, $flat ? GetPoint_2D($self) : GetPoint_3D($self);
        } else {
            my $i;
            if ($flat) {
                for my $i (0..$n-1) {
                    push @points, scalar GetPoint_2D($self, $i);
                }
            } else {
                for my $i (0..$n-1) {
                    push @points, scalar GetPoint_3D($self, $i);
                }
            }
        }
    }
    return \@points;
}

sub ExportToWkb {
    my($self, $bo) = @_;
    if (defined $bo) {
        confess "Unknown byte order: '$bo'." unless exists $BYTE_ORDER_STRING2INT{$bo};
        $bo = $BYTE_ORDER_STRING2INT{$bo};
    }
    return _ExportToWkb($self, $bo);
}

sub ForceTo {
    my $self = shift;
    my $type = shift;
    confess "Unknown geometry type: '$type'." unless exists $TYPE_STRING2INT{$type};
    $type = $TYPE_STRING2INT{$type};
    eval {
        $self = Geo::OGR::ForceTo($self, $type, @_);
    };
    confess $@ if $@;
    return $self;
}

sub ForceToLineString {
    my $self = shift;
    $self = Geo::OGR::ForceToLineString($self);
    return $self;
}

sub ForceToMultiPoint {
    my $self = shift;
    $self = Geo::OGR::ForceToMultiPoint($self);
    for my $g (@_) {
        $self->AddGeometry($g);
    }
    return $self;
}

sub ForceToMultiLineString {
    my $self = shift;
    $self = Geo::OGR::ForceToMultiLineString($self);
    for my $g (@_) {
        $self->AddGeometry($g);
    }
    return $self;
}

sub ForceToMultiPolygon {
    my $self = shift;
    $self = Geo::OGR::ForceToMultiPolygon($self);
    for my $g (@_) {
        $self->AddGeometry($g);
    }
    return $self;
}

sub ForceToCollection {
    my $self = Geo::OGR::Geometry->new(GeometryType => 'GeometryCollection');
    for my $g (@_) {
        $self->AddGeometry($g);
    }
    return $self;
}
*Collect = *ForceToCollection;

sub Dissolve {
    my $self = shift;
    my @c;
    my $n = $self->GetGeometryCount;
    if ($n > 0) {
        for my $i (0..$n-1) {
            push @c, $self->GetGeometryRef($i)->Clone;
        }
    } else {
        push @c, $self;
    }
    return @c;
}
*AsText = *ExportToWkt;
*AsBinary = *ExportToWkb;
*AsGML = *ExportToGML;
*AsKML = *ExportToKML;
*AsJSON = *ExportToJson;
*BuildPolygonFromEdges = *Geo::OGR::BuildPolygonFromEdges;
*ForceToPolygon = *Geo::OGR::ForceToPolygon;


package Geo::OGR;
use strict;
use warnings;
use Carp;

sub GeometryType {
    my($type_or_name) = @_;
    if (defined $type_or_name) {
        return $Geo::OGR::Geometry::TYPE_STRING2INT{$type_or_name} if
            exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type_or_name};
        return $Geo::OGR::Geometry::TYPE_INT2STRING{$type_or_name} if
            exists $Geo::OGR::Geometry::TYPE_INT2STRING{$type_or_name};
        confess "Unknown geometry type: '$type_or_name'.";
    } else {
        return @Geo::OGR::Geometry::GEOMETRY_TYPES;
    }
}

sub GeometryTypeModify {
    my($type, $modifier) = @_;
    confess "Unknown geometry type: '$type'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
    $type = $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
    return $Geo::OGR::Geometry::TYPE_INT2STRING{GT_Flatten($type)} if $modifier =~ /flat/i;
    return $Geo::OGR::Geometry::TYPE_INT2STRING{GT_SetZ($type)} if $modifier =~ /z/i;
    return $Geo::OGR::Geometry::TYPE_INT2STRING{GT_GetCollection($type)} if $modifier =~ /collection/i;
    return $Geo::OGR::Geometry::TYPE_INT2STRING{GT_GetCurve($type)} if $modifier =~ /curve/i;
    return $Geo::OGR::Geometry::TYPE_INT2STRING{GT_GetLinear($type)} if $modifier =~ /linear/i;
    confess "Unknown geometry type modifier: '$modifier'.";
}

sub GeometryTypeTest {
    my($type, $test, $type2) = @_;
    confess "Unknown geometry type: '$type'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
    $type = $Geo::OGR::Geometry::TYPE_STRING2INT{$type};
    if (defined $type2) {
        confess "Unknown geometry type: '$type2'." unless exists $Geo::OGR::Geometry::TYPE_STRING2INT{$type2};
        $type2 = $Geo::OGR::Geometry::TYPE_STRING2INT{$type2};
    } else {
        confess "Usage: GeometryTypeTest(type1, 'is_subclass_of', type2)." if $test =~ /subclass/i;
    }
    return GT_HasZ($type) if $test =~ /z/i;
    return GT_IsSubClassOf($type, $type2) if $test =~ /subclass/i;
    return GT_IsCurve($type) if $test =~ /curve/i;
    return GT_IsSurface($type) if $test =~ /surface/i;
    return GT_IsNonLinear($type) if $test =~ /linear/i;
    confess "Unknown geometry type test: '$test'.";
}

sub RELEASE_PARENTS {
}

*ByteOrders = *Geo::OGR::Geometry::ByteOrders;
*GeometryTypes = *Geo::OGR::Geometry::GeometryTypes;

sub GetDriverNames {
    my @names;
    for my $i (0..GetDriverCount()-1) {
        push @names, _GetDriver($i)->Name;
    }
    return @names;
}

sub Drivers {
    my @drivers;
    for my $i (0..GetDriverCount()-1) {
        push @drivers, _GetDriver($i);
    }
    return @drivers;
}

sub GetDriver {
    my($name) = @_;
    $name = 0 unless defined $name;
    my $driver;
    $driver = _GetDriver($name) if $name =~ /^\d+$/; # is the name an index to driver list?
    $driver = GetDriverByName("$name") unless $driver;
    return $driver if $driver;
    confess "Driver not found: '$name'. Maybe support for it was not built in?";
}
*Driver = *GetDriver;
1;
