NetTopologySuite / NetTopologySuite.IO.Esri

BSD 3-Clause "New" or "Revised" License
31 stars 16 forks source link

Add stream support #36

Closed jengstro2 closed 4 months ago

jengstro2 commented 9 months ago

Stream input alternative for reading shp and dbf.

CLAassistant commented 9 months ago

CLA assistant check
All committers have signed the CLA.

FrankHileman commented 8 months ago

I needed stream write support, so I was working on similar changes for writing. I think the method OpenStreamRead should just be called OpenRead; since the parameters are different, it can be an overload. Also, should I create a separate fork and pull request for the stream writing, or could they be added to this?

FrankHileman commented 8 months ago
        /// <summary>
        /// Opens shapefile writer.
        /// </summary>
        /// <param name="shpStream">SHP file stream.</param>
        /// <param name="shxStream">SHX file stream.</param>
        /// <param name="dbfStream">DBF file stream.</param>
        /// <param name="options">Writer options.</param>
        /// <returns>Shapefile writer.</returns>
        public static ShapefileWriter OpenWrite(Stream shpStream, Stream shxStream, Stream dbfStream, ShapefileWriterOptions options)
        {
            options = options ?? throw new ArgumentNullException(nameof(options));
            if (options.ShapeType.IsPoint())
            {
                return new ShapefilePointWriter(shpStream, shxStream, dbfStream, options);
            }
            else if (options.ShapeType.IsMultiPoint())
            {
                return new ShapefileMultiPointWriter(shpStream, shxStream, dbfStream, options);
            }
            else if (options.ShapeType.IsPolyLine())
            {
                return new ShapefilePolyLineWriter(shpStream, shxStream, dbfStream, options);
            }
            else if (options.ShapeType.IsPolygon())
            {
                return new ShapefilePolygonWriter(shpStream, shxStream, dbfStream, options);
            }
            else
            {
                throw new ShapefileException("Unsupported shapefile type: " + options.ShapeType, "No File");
            }
        }

        /// <summary>
        /// Writes features to the shapefile.
        /// </summary>
        /// <param name="features">Features to be written.</param>
        /// <param name="shpStream">SHP file stream.</param>
        /// <param name="shxStream">SHX file stream.</param>
        /// <param name="dbfStream">DBF file stream.</param>
        /// <param name="projection">Projection metadata for the shapefile (content of the PRJ file).</param>
        /// <param name="encoding">DBF file encoding (if not set UTF8 is used).</param>
        public static void WriteAllFeatures(IEnumerable<IFeature> features, Stream shpStream, Stream shxStream, Stream dbfStream,
            string projection = null, Encoding encoding = null)
        {
            if (features == null)
                throw new ArgumentNullException(nameof(features));

            var firstFeature = features.FirstOrDefault();
            if (firstFeature == null)
                throw new ArgumentException(nameof(ShapefileWriter) + " requires at least one feature to be written.");

            var fields = firstFeature.Attributes.GetDbfFields();
            var shapeType = features.FindNonEmptyGeometry().GetShapeType();
            var options = new ShapefileWriterOptions(shapeType, fields)
            {
                Projection = projection,
                Encoding = encoding
            };

            using (var shpWriter = OpenWrite(shpStream, shxStream, dbfStream, options))
            {
                shpWriter.Write(features);
            }
        }
jengstro2 commented 8 months ago

OpenRead(...) overloaded with Stream option -> fine by me. Yeah, Stream write should be there also 👍

KubaSzostak commented 5 months ago

Thank you for your valuable input @jengstro2. I changed the method names to OpenRead() and ReadAllFeatures(). I added also tests for new methods. Please check if it's fine for you.

@FrankHileman, stream write support is also added. Can you review it?

KubaSzostak commented 5 months ago

41

FrankHileman commented 5 months ago

There is one issue, unrelated to Streams specifically. There isn't any place to put the projection file using Streams. I haven't found a better solution, except to have yet another stream.

KubaSzostak commented 5 months ago

Good point, @FrankHileman. I've added projection stream parameter. Does it work for you?

KubaSzostak commented 4 months ago

Fix #41

FrankHileman commented 1 month ago

Hello, in testing this I found a problem. Unlike the other streams, which are left open, the projection stream is closed. We should leave all streams open after the write operation. The problem is the use of a using statement, and a StreamWriter constructor. The StreamWriter constructor has a parameter called "leaveOpen", when set to true, will leave the stream open when the StreamWriter is disposed. We should set this to true.


       internal ShapefileWriter(Stream shpStream, Stream shxStream, Stream dbfStream, Stream prjStream, ShapefileWriterOptions options)
           : base(new DbfWriter(dbfStream, options?.Fields, options?.Encoding))
       {
           try
           {
               options = options ?? throw new ArgumentNullException(nameof(options));
               ShapeType = options.ShapeType;
               ShpWriter = CreateShpWriter(shpStream, shxStream);

               if (!string.IsNullOrWhiteSpace(options.Projection) && prjStream != null)
               {
                   using (var writer = new StreamWriter(prjStream))
                   {
                       writer.Write(options.Projection);
                   }
               }
           }
           catch
           {
               DisposeManagedResources();
               throw;
           }
       }
FrankHileman commented 1 month ago

I wasn't sure if I should start a new issue...

FrankHileman commented 1 month ago

using (var writer = new StreamWriter(prjStream, Encoding.UTF8, 1024, true)) { writer.Write(options.Projection); }