NetTopologySuite.IO.ShapeFile icon indicating copy to clipboard operation
NetTopologySuite.IO.ShapeFile copied to clipboard

Write a shape file in Xamarin.forms bug

Open benabdelkrim opened this issue 3 years ago • 18 comments

exception

benabdelkrim avatar Nov 05 '20 09:11 benabdelkrim

please post the complete code you're testing, from this I don't see why the line highlighted can throw a "ArgumentNullException" actually. No similar "throws" in the source code

I suspect the error is in attributes dictionary...

DGuidi avatar Nov 05 '20 09:11 DGuidi

capturebug

benabdelkrim avatar Nov 05 '20 09:11 benabdelkrim

Posting images of your VisualStudio instance is not useful.
Please set up a minimum working sample project for us to test your code.

FObermaier avatar Nov 05 '20 10:11 FObermaier

string path;
string C_postal = "C_postal"; string Type_bat = "Type_bat"; string X_coord = "X_coord"; string Y_coord = "Y_coord"; //create geometry factory GeometryFactory geomFactory = NtsGeometryServices.Instance.CreateGeometryFactory(); //create the default table with fields - alternately use DBaseField classes AttributesTable attributes1 = new AttributesTable(); attributes1.Add(C_postal, "40050"); attributes1.Add(Type_bat, "Immeuble"); attributes1.Add(X_coord, "300000"); attributes1.Add(Y_coord, "5000000"); AttributesTable attributes2 = new AttributesTable(); attributes2.Add(C_postal, "40050"); attributes2.Add(Type_bat, "Using"); attributes2.Add(X_coord, "300200"); attributes2.Add(Y_coord, "5000300"); //create geometries and features Geometry geometry1 = geomFactory.CreatePoint(new Coordinate(300000, 5000000)); Geometry geometry2 = geomFactory.CreatePoint(new Coordinate(300200, 5000300)); Feature feat1 = new Feature(geometry1, attributes1); Feature feat2 = new Feature(geometry2, attributes2); //create attribute list IList<Feature> features = new List<Feature>() { feat1, feat2 }; path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "shape"); ShapefileDataWriter writer = new ShapefileDataWriter(path); writer.Header = ShapefileDataWriter.GetHeader(features[0], features.Count); System.Collections.IList featList = (System.Collections.IList)features; writer.Write(features);

benabdelkrim avatar Nov 05 '20 10:11 benabdelkrim

this code is working in application desktop c# but in Xamarin.forms not working, i don't know why ?

benabdelkrim avatar Nov 05 '20 10:11 benabdelkrim

I'm still missing a project file, the code you posted is just a function. Now, to get to where you are at, we do have to put in effort to set up a project that uses Xamairn.Forms, that eventually invokes your code. Please provide a minimum working sample project for us to test your code.

FObermaier avatar Nov 05 '20 10:11 FObermaier

thank you for find the code in this link https://www.swisstransfer.com/d/c1fda1a0-e909-4d7f-a992-e86e0970d66f

benabdelkrim avatar Nov 05 '20 11:11 benabdelkrim

this code works in .net, I made small changes

string path;
            string C_postal = "C_postal";
            string Type_bat = "Type_bat";
            string X_coord = "X_coord";
            string Y_coord = "Y_coord";
            //create geometry factory
            var geomFactory = NtsGeometryServices.Instance.CreateGeometryFactory();
            //create the default table with fields - alternately use DBaseField classes
            var attributes1 = new AttributesTable();
            attributes1.Add(C_postal, "40050");
            attributes1.Add(Type_bat, "Immeuble");
            attributes1.Add(X_coord, "300000");
            attributes1.Add(Y_coord, "5000000");
            var attributes2 = new AttributesTable();
            attributes2.Add(C_postal, "40050");
            attributes2.Add(Type_bat, "Using");
            attributes2.Add(X_coord, "300200");
            attributes2.Add(Y_coord, "5000300");
            //create geometries and features
            Geometry geometry1 = geomFactory.CreatePoint(new Coordinate(300000, 5000000));
            Geometry geometry2 = geomFactory.CreatePoint(new Coordinate(300200, 5000300));
            var feat1 = new Feature(geometry1, attributes1);
            var feat2 = new Feature(geometry2, attributes2);
            //create attribute list
            var features = new List<IFeature>() { feat1, feat2 };
            path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "shape");
            var writer = new ShapefileDataWriter(path);
            writer.Header = ShapefileDataWriter.GetHeader(features[0], features.Count);
            writer.Write(features);

DGuidi avatar Nov 05 '20 11:11 DGuidi

it still the problem ! even i separate the Header ! var writer = new ShapefileDataWriter(path); writer.Header = ShapefileDataWriter.GetHeader(features[0], features.Count); Other thing this code is working in .NET but in xamarin.forms i have this problem

benabdelkrim avatar Nov 05 '20 11:11 benabdelkrim

can you post the stack trace? can be helpful to investigate what is the method actually throwing the exception

DGuidi avatar Nov 05 '20 12:11 DGuidi

The cause for this to fail is probably the failure of the initialization of NetTopologySuite.IO.DbaseEncodingUtility. Maybe some of the initial codepages are not available for Android?

FObermaier avatar Nov 05 '20 15:11 FObermaier

Please I need to know if there's a solution to read/write a shapefile in Xamarin.forms. I really appreciate your help.

Thanks in advance.

benabdelkrim avatar Nov 05 '20 16:11 benabdelkrim

oh, understood. Maybe you can try to initialize the writer using an explicit Encoding as the third parameter and see if it works

new ShapefileDataWriter(path, geomFactory, Encoding.UTF8) // or ASCII

BTW I'm trying to install Xamarin to do some tests... but it takes quite a time! Abd it's incredibly slow without Hyper-V acceleration (unavailable in my system...)

DGuidi avatar Nov 05 '20 18:11 DGuidi

@FObermaier GetEncodingForCodePageIdentifier(437) - French OEM - returns null this code shall work but actually the write of the features fail...

ShapefileDataWriter writer = new ShapefileDataWriter(path, geomFactory, Encoding.Default);
writer.Header = ShapefileDataWriter.GetHeader(features[0], features.Count, Encoding.Default);
writer.Write(features);

because RegisterEncodings stops recording all the other encodings after the first that fails, I think... handling all the exceptions, and not only NotSupportedException, fix the problem

try
{
  var enc = GetEncodingForCodePageIdentifier(codePage);
  AddLdidEncodingPair(ldid, enc);
}
catch //(NotSupportedException)
{
  string message = string.Format("Failed to get codepage for language driver {0}", ldid);
  Debug.WriteLine(message);
}

those are the missing encodings

Loaded assembly: System.Text.Encoding.CodePages.dll [External]
[0:] Failed to get codepage for language driver 1
[0:] Failed to get codepage for language driver 2
[0:] Failed to get codepage for language driver 8
[0:] Failed to get codepage for language driver 9
[0:] Failed to get codepage for language driver 10
[0:] Failed to get codepage for language driver 11
[0:] Failed to get codepage for language driver 13
[0:] Failed to get codepage for language driver 14
[0:] Failed to get codepage for language driver 15
[0:] Failed to get codepage for language driver 16
[0:] Failed to get codepage for language driver 17
[0:] Failed to get codepage for language driver 18
[0:] Failed to get codepage for language driver 19
[0:] Failed to get codepage for language driver 20
[0:] Failed to get codepage for language driver 21
[0:] Failed to get codepage for language driver 22
[0:] Failed to get codepage for language driver 23
[0:] Failed to get codepage for language driver 24
[0:] Failed to get codepage for language driver 25
[0:] Failed to get codepage for language driver 26
[0:] Failed to get codepage for language driver 27
[0:] Failed to get codepage for language driver 28
[0:] Failed to get codepage for language driver 29
[0:] Failed to get codepage for language driver 31
[0:] Failed to get codepage for language driver 34
[0:] Failed to get codepage for language driver 35
[0:] Failed to get codepage for language driver 36
[0:] Failed to get codepage for language driver 37
[0:] Failed to get codepage for language driver 38
[0:] Failed to get codepage for language driver 55
[0:] Failed to get codepage for language driver 64
[0:] Failed to get codepage for language driver 77
[0:] Failed to get codepage for language driver 78
[0:] Failed to get codepage for language driver 79
[0:] Failed to get codepage for language driver 80
[0:] Failed to get codepage for language driver 88
[0:] Failed to get codepage for language driver 89
[0:] Failed to get codepage for language driver 100
[0:] Failed to get codepage for language driver 101
[0:] Failed to get codepage for language driver 102
[0:] Failed to get codepage for language driver 103
[0:] Failed to get codepage for language driver 106
[0:] Failed to get codepage for language driver 107
[0:] Failed to get codepage for language driver 108
[0:] Failed to get codepage for language driver 120
[0:] Failed to get codepage for language driver 121
[0:] Failed to get codepage for language driver 122
[0:] Failed to get codepage for language driver 123
[0:] Failed to get codepage for language driver 124
[0:] Failed to get codepage for language driver 134
[0:] Failed to get codepage for language driver 135
[0:] Failed to get codepage for language driver 136
[0:] Failed to get codepage for language driver 200
[0:] Failed to get codepage for language driver 201
[0:] Failed to get codepage for language driver 202
[0:] Failed to get codepage for language driver 203
[0:] Failed to get codepage for language driver 204

@FObermaier if you agree I can push the fix direclty from my side, let me know

DGuidi avatar Nov 06 '20 07:11 DGuidi

@benabdelkrim you can do this to continue working

  1. checkout nettopologysuite.io.shapefile code
  2. add the nts.io.shapefile project to your solution
  3. remove the nuget reference to nts.io.shapefile and add the project reference
  4. patch the DBaseEncodingUtility.RegisterEncodings in nts.io.shapefile as:
try
{
  var enc = GetEncodingForCodePageIdentifier(codePage);
  AddLdidEncodingPair(ldid, enc);
}
catch //(NotSupportedException)
{
  string message = string.Format("Failed to get codepage for language driver {0}", ldid);
  Debug.WriteLine(message);
}
  1. use this code
ShapefileDataWriter writer = new ShapefileDataWriter(path, geomFactory, Encoding.Default);
writer.Header = ShapefileDataWriter.GetHeader(features[0], features.Count, Encoding.Default);
writer.Write(features);

DGuidi avatar Nov 06 '20 07:11 DGuidi

@DGuidi current implementation of GetEncodingForCodePageIdentifier(int) does not work well on Android. Changing the implementation to

/// <summary>
/// Utility function to get an encoding for the provided codepage identifier
/// </summary>
/// <param name="codePage">A code page identifier</param>
/// <returns> An <c>Encoding</c> or <c>null</c></returns>
public static Encoding GetEncodingForCodePageIdentifier(int codePage)
{
    try { return CodePagesEncodingProvider.Instance.GetEncoding(codePage) ?? Encoding.GetEncoding(codePage); }
    catch { return null; }
}

gives much better results. No Encoding that can't be found. I changed the implementation of RegisterEncodings to

private static void RegisterEncodings(object[][] ldidCodePagePairs)
{
    var validCodePages = new HashSet<int>(Encoding.GetEncodings().Select(t => t.CodePage));
    foreach (object[] ldidCodePagePair in ldidCodePagePairs)
    {
        byte ldid = Convert.ToByte(ldidCodePagePair[0], CultureInfo.InvariantCulture);
        int codePage = (int)ldidCodePagePair[1];
        if (validCodePages.Contains(codePage))
            AddLdidEncodingPair(ldid, Encoding.GetEncoding(codePage));
#if DEBUG
        else
        {
            Debug.WriteLine("Failed to get Encoding for ldid={0} (Codepage {1})", ldid, codePage);
        }
#endif
    }
}

Additionally I made the the unit tests pass, there were a lot failing... If you don't mind, I'd rather commit my changes.

FObermaier avatar Nov 06 '20 09:11 FObermaier

Additionally I made the the unit tests pass, there were a lot failing...

do you mean, they fails in android?

If you don't mind, I'd rather commit my changes.

of course, thanks

DGuidi avatar Nov 06 '20 09:11 DGuidi

Hi all,

On Android I have the same issue as the original issue describes. I tried out the solution recommended by @FObermaier, and it solves the issue (adding the "Encoding.GetEncoding(codePage);" as fallback)

public static Encoding GetEncodingForCodePageIdentifier(int codePage) => CodePagesEncodingProvider.Instance.GetEncoding(codePage) ?? Encoding.GetEncoding(codePage);

Just wondering if there is any reason why it is not merged to the implementation? Without this, I'm not able to read the ShapeFile on Android, unless I use the source code of the NetTopologySuite.IO.ShapeFile, and add that fallback manually, but in that case I won't be able to get updates later.

Thanks, S.

ssrodnas avatar Nov 11 '21 14:11 ssrodnas