NetTopologySuite / NetTopologySuite.IO.ShapeFile

The ShapeFile IO module for NTS.
33 stars 25 forks source link

Write shape data to MemoryStreams -> MemoryStream not expandable #71

Closed erikthysell closed 3 years ago

erikthysell commented 3 years ago

First - thanks for a fantastic library! I am looking for a way to have a all three needed shapefiles (shp, shx and dbf) written to a MemoryStream(or three separate MemoryStream's). I have tried the following:

using System.IO;
using System.Text;
using NetTopologySuite.IO;
using NetTopologySuite.Geometries;
using NetTopologySuite.Features;
using NetTopologySuite.IO.Streams;
using System;

namespace TestConsole
{

    public static class ShapeMemoryStreamTester
    {
        public static void TestShapeFiles2MemoryStreams()
        {
            var pt = GeometryFactory.Default.CreatePoint(new NetTopologySuite.Geometries.Coordinate(2, 3));
            var attributes = new AttributesTable { { "Foo", "Bar" } };
            Feature[] features = { new Feature(pt, attributes) };

            // Exception is thrown weather or not initCapacity is used
            var initCapacity = 0;
            var shpS = new MemoryStream(initCapacity);
            var shxS = new MemoryStream(initCapacity);
            var dbfS = new MemoryStream(initCapacity);

            var reg = new ShapefileStreamProviderRegistry(
                shapeStream: new ByteStreamProvider(StreamTypes.Shape, shpS),
                dataStream: new ByteStreamProvider(StreamTypes.Data, dbfS),
                indexStream: new ByteStreamProvider(StreamTypes.Index, shxS),
                validateShapeProvider: true,
                validateDataProvider: true,
                validateIndexProvider: true);

            var wr = new ShapefileDataWriter(reg, GeometryFactory.Default, CodePagesEncodingProvider.Instance.GetEncoding(1252));
            wr.Header = ShapefileDataWriter.GetHeader(features[0], features.Length);

            //Exception throws here
            wr.Write(features);

            var fnbase = @"c:\temp\StreamShapeTest";
            var shpBR = new BinaryReader(shpS);
            var shxBR = new BinaryReader(shpS);
            var dbfBR = new BinaryReader(shpS);

            var shpBytes = new byte[shpS.Length];
            var shxBytes = new byte[shxS.Length];
            var dbfBytes = new byte[dbfS.Length];

            var nSHPBytes = shpBR.Read(shpBytes);
            var nSHXBytes = shxBR.Read(shpBytes);
            var nDBFBytes = dbfBR.Read(shpBytes);

            File.WriteAllBytes(fnbase + ".shp", shpBytes);
            File.WriteAllBytes(fnbase + ".shx", shxBytes);
            File.WriteAllBytes(fnbase + ".dbf", dbfBytes);
        }
    }
}

But this just result in an exception being thrown:

"Memory stream is not expandable."

at System.IO.MemoryStream.set_Capacity(Int32 value) at System.IO.MemoryStream.EnsureCapacity(Int32 value) at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) at System.IO.BinaryWriter.Write(Int32 value) at NetTopologySuite.IO.ShapefileHeader.Write(BigEndianBinaryWriter file) at NetTopologySuite.IO.ShapefileWriter.WriteShpHeader(BigEndianBinaryWriter shpBinaryWriter, Int32 shpLength, Envelope bounds) at NetTopologySuite.IO.ShapefileWriter..ctor(GeometryFactory geometryFactory, IStreamProviderRegistry streamProviderRegistry, ShapeGeometryType geomType) at NetTopologySuite.IO.ShapefileDataWriter.Write(IEnumerable`1 featureCollection) at TestConsole.ShapeMemoryStreamTester.TestShapeFiles2MemoryStreams() in C:\ \ShapeMemoryStreamTester.cs:line 35

It also happens if MemoryStream is instantiated with initial capacity initCapacity= 0 , without capacity argument (as stated here should result in an expandable stream), and/or if I set initCapacity = 2^20 (which I think would be large enough).

Is there another way of accomplishing this or can it be fixed? (or am I doing something wrong??)

DGuidi commented 3 years ago

maybe related to #55 ?

erikthysell commented 3 years ago

I was using the nuget package (lastest release) which dont have the ExternallyManagedStreamProvider (yet). Switching ByteStreamProvider to ExternallyManagedStreamProvider solved the problem. ByteStreamProvider seems to only be for reading purpose.