Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null value #31

Closed
TheR7angelo opened this issue Jun 6, 2023 · 9 comments
Closed

Null value #31

TheR7angelo opened this issue Jun 6, 2023 · 9 comments
Assignees

Comments

@TheR7angelo
Copy link

when I want to create a shapefile and insert a null value in it, I get a system object not supported error.
Fine, but the value is an int?

@DGuidi
Copy link

DGuidi commented Jun 6, 2023

please post some code and/or sample data to explain the procedure you use

@TheR7angelo
Copy link
Author

This is the example given with a type modifier
` var features = new List();
for (var i = 1; i < 5; i++)
{
var lineCoords = new List
{
new(i, i + 1, i),
new(i, i, i),
new(i + 1, i, i)
};
var line = new LineString(lineCoords.ToArray());
var mline = new MultiLineString(new LineString[] { line });

        int? test = null;

        var attributes = new AttributesTable
        {
            { "date", new DateTime() },
            { "float", i * 0.1 },
            { "int", test },
            { "logical", i % 2 == 0 },
            { "text", i.ToString("0.00") }
        };

        var feature = new Feature(mline, attributes);
        features.Add(feature);
    }

    Shapefile.WriteAllFeatures(features, "t");`

and i got that
System.NotSupportedException: Unsupported dBASE field type: RuntimeType (System.Object).
System.NotSupportedException
Unsupported dBASE field type: RuntimeType (System.Object)
at NetTopologySuite.IO.Esri.Dbf.Fields.DbfField.Create(String name, Type type)
at NetTopologySuite.IO.Esri.FeatureExtensions.GetDbfFields(IAttributesTable attributes)
at NetTopologySuite.IO.Esri.Shapefile.WriteAllFeatures(IEnumerable`1 features, String shpPath, String projection, Encoding encoding)

When I look in debug mode I see that this is the field I modified to make it nullable

@dax-leo
Copy link

dax-leo commented Jun 6, 2023

I experienced similar problem.
For example it's not possible to write nullable integer. I tried using null and DBNull.Value, but nothing worked.

@TheR7angelo
Copy link
Author

TheR7angelo commented Jun 6, 2023

same with all data type
We can read null value but we can't write null value

@TheR7angelo
Copy link
Author

New problem when a date is null its value is '00000000' or when reading it causes this

The DateTime represented by the string '00000000' is not supported in calendar

@KubaSzostak
Copy link
Member

KubaSzostak commented Jun 18, 2023

Thanks for pointing that out! We're working on a fix for this issue. For now, if there is a nullable field, you have to set all field definitions manually:

var features = new List<Feature>();
for (var i = 1; i < 5; i++)
{
    var lineCoords = new List<Coordinate>
    {
        new(i, i + 1),
        new(i, i),
        new(i + 1, i)
    };
    var line = new LineString(lineCoords.ToArray());
    var mline = new MultiLineString(new LineString[] { line });

    int? test = null;

    var attributes = new AttributesTable
    {
        { "date", new DateTime() },
        { "float", i * 0.1 },
        { "int", test },
        { "logical", i % 2 == 0 },
        { "text", i.ToString("0.00") }
    };  

    var feature = new Feature(mline, attributes);
    features.Add(feature);
}

var fields = new List<DbfField>();
fields.AddDateField("date");
fields.AddFloatField("float");
fields.AddNumericInt32Field("int");
fields.AddLogicalField("logical");
fields.AddCharacterField("text");

var options = new ShapefileWriterOptions(ShapeType.PolyLine, fields.ToArray());
var shpPath = TestShapefiles.GetTempShpPath();
using (var shpWriter = Shapefile.OpenWrite(shpPath, options))
{
    shpWriter.Write(features);
}
TestShapefiles.DeleteShp(shpPath);

@KubaSzostak KubaSzostak self-assigned this Jun 18, 2023
@KubaSzostak
Copy link
Member

Another solution is to use pure SHP Writer without the AttributesTable bridge:

var fields = new List<DbfField>();
var dateField = fields.AddDateField("date");
var floatField = fields.AddFloatField("float");
var intField = fields.AddNumericInt32Field("int");
var logicalField = fields.AddLogicalField("logical");
var textField = fields.AddCharacterField("text");

var options = new ShapefileWriterOptions(ShapeType.PolyLine, fields.ToArray());
var shpPath = TestShapefiles.GetTempShpPath();
using (var shpWriter = Shapefile.OpenWrite(shpPath, options))
{
    for (var i = 1; i < 5; i++)
    {
        var lineCoords = new List<Coordinate>
        {
            new(i, i + 1),
            new(i, i),
            new(i + 1, i)
        };
        var line = new LineString(lineCoords.ToArray());
        var mline = new MultiLineString(new LineString[] { line });

        int? test = null;

        shpWriter.Geometry = mline;
        dateField.DateValue = DateTime.Now;
        floatField.NumericValue = i * 0.1;
        intField.NumericValue = test;
        logicalField.LogicalValue = i % 2 == 0;
        textField.StringValue = i.ToString("0.00");
        shpWriter.Write();
    }
}
TestShapefiles.DeleteShp(shpPath);

This will be more performant as in this case there is no boxing/unboxing.

@nRoger-Env
Copy link

I had a similar problem with datetime attributes. The problem was not that I couldn't write null values, it was caused by having all values of a particular attribute set as null. If the first value was not null it worked.

Anyway, I just wanted to say to @KubaSzostak that the latest exemple you posted is working really good. You should place it in the main github page. This also solved a problem I had with writing a shapefile containing only points where the coordinates of the points were not registering properly (ArcGIS said the shape file had no coordonate system assigned)

@KubaSzostak
Copy link
Member

When all features have a null value, the field type cannot be detected correctly. To solve this, the AttributesTable needs to be extended to store attribute types along with attribute values, so that AttributesTable.GetType() returns property attribute type instead of default typeof(object).

Below is a sample code that demonstrates how to receive attribute type from CLR type.

private static void TestClrTypes()
{
    int? i0 = null;
    int? i1 = 1;
    int i2 = 2;

    string s0 = null;
    string s1 = "s1";

    WriteType("int? i0 = null  ", i0);
    WriteType("int? i1 = 1     ", i1);
    WriteType("int i2 = 2      ", i2);

    WriteType("string s0 = null", s0);
    WriteType("string s1 = \"s1\"", s1);
}

private static void WriteType<T>(string name, T value)
{
    var valueText = value?.ToString() ?? "<null>";
    var valueType = typeof(T);
    var underlyingType = Nullable.GetUnderlyingType(valueType) ?? valueType;

    Console.WriteLine($"{name}");
    Console.WriteLine($"- Value:          {valueText}");
    Console.WriteLine($"- ValueType:      {valueType}");
    Console.WriteLine($"- UnderlyingType: {underlyingType}");
    Console.WriteLine();
}
int? i0 = null  
- Value:          <null>
- ValueType:      System.Nullable`1[System.Int32]
- UnderlyingType: System.Int32

int? i1 = 1     
- Value:          1
- ValueType:      System.Nullable`1[System.Int32]
- UnderlyingType: System.Int32

int i2 = 2      
- Value:          2
- ValueType:      System.Int32
- UnderlyingType: System.Int32

string s0 = null
- Value:          <null>
- ValueType:      System.String
- UnderlyingType: System.String

string s1 = "s1"
- Value:          s1
- ValueType:      System.String
- UnderlyingType: System.String

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants