/// <summary> /// Reads a stream and converts the shapefile record to an equilivant geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="factory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, int totalRecordLength, IGeometryFactory factory) { int totalRead = 0; int shapeTypeNum = ReadInt32(file, totalRecordLength, ref totalRead); var type = (ShapeGeometryType)EnumUtility.Parse(typeof(ShapeGeometryType), shapeTypeNum.ToString()); if (type == ShapeGeometryType.NullShape) { return(factory.CreateMultiPoint(new IPoint[] { })); } if (type != ShapeType) { throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); } // Read and for now ignore bounds. int bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) { double d = ReadDouble(file, totalRecordLength, ref totalRead); boundingBox[boundingBoxIndex] = d; } // Read points var numPoints = ReadInt32(file, totalRecordLength, ref totalRead); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var points = new IPoint[numPoints]; var pm = factory.PrecisionModel; for (var i = 0; i < numPoints; i++) { var x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); var y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); buffer.AddCoordinate(x, y); buffer.AddMarker(); } // Trond Benum: We have now read all the points, let's read optional Z and M values GetZMValues(file, totalRecordLength, ref totalRead, buffer); var sequences = buffer.ToSequences(factory.CoordinateSequenceFactory); for (var i = 0; i < numPoints; i++) { points[i] = factory.CreatePoint(sequences[i]); } geom = factory.CreateMultiPoint(points); return(geom); }
/// <summary> /// Function to read a <see cref="ILineString"/> or <see cref="IMultiLineString"/> from a ShapeFile stream using the specified <paramref name="reader"/>. /// </summary> /// <param name="reader">The reader to use</param> /// <param name="ordinates">The ordinates to read</param> /// <returns>The read lineal geometry</returns> protected IGeometry ReadLineString(BinaryReader reader, Ordinates ordinates) { /*var bbox = */ ReadBoundingBox(reader); // Jump boundingbox var numParts = ReadNumParts(reader); var numPoints = ReadNumPoints(reader); var indexParts = ReadIndexParts(reader, numParts, numPoints); var buffer = new CoordinateBuffer(numPoints, ShapeFileConstants.NoDataBorder, true); ReadCoordinates(reader, numPoints, indexParts, ordinates, buffer); if (numParts == 1) return _factory.CreateLineString(buffer.ToSequence()); return CreateMultiLineString(buffer.ToSequences()); }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivant geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="geometryFactory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, int totalRecordLength, IGeometryFactory geometryFactory) { int totalRead = 0; int shapeTypeNum = ReadInt32(file, totalRecordLength, ref totalRead); var type = (ShapeGeometryType) EnumUtility.Parse(typeof(ShapeGeometryType), shapeTypeNum.ToString()); if (type == ShapeGeometryType.NullShape) return geometryFactory.CreateMultiPoint(new IPoint[] { }); if (type != ShapeType) throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); // Read and for now ignore bounds. int bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) { double d = ReadDouble(file, totalRecordLength, ref totalRead); boundingBox[boundingBoxIndex] = d; } // Read points var numPoints = ReadInt32(file, totalRecordLength, ref totalRead); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var points = new IPoint[numPoints]; var pm = geometryFactory.PrecisionModel; for (var i = 0; i < numPoints; i++) { var x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); var y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); buffer.AddCoordinate(x, y); buffer.AddMarker(); } // Trond Benum: We have now read all the points, let's read optional Z and M values GetZMValues(file, totalRecordLength, ref totalRead, buffer); var sequences = buffer.ToSequences(geometryFactory.CoordinateSequenceFactory); for (var i = 0; i < numPoints; i++) points[i] = geometryFactory.CreatePoint(sequences[i]); geom = geometryFactory.CreateMultiPoint(points); return geom; }
public void TestAddMarkers() { var cb = new CoordinateBuffer(10); for (var i = 0; i < 10; i++) { if (i > 0 && i % 5 == 0) { cb.AddMarker(); } cb.AddCoordinate(i, i); } //cb.AddMarker(); var seqs = cb.ToSequences(); Assert.AreEqual(2, seqs.Length); Assert.AreEqual(5, seqs[0].Count); Assert.AreEqual(5, seqs[1].Count); }
/// <summary> /// Creates a single Polygon with holes. /// </summary> /// <param name="buffer"></param> /// <returns></returns> private IGeometry CreateSingleOrMultiPolygon(CoordinateBuffer buffer) { // Support vars var cses = buffer.ToSequences(); var shellRings = new List <ILinearRing>(); var holeRings = new List <ILinearRing>(); var numHoleRings = new Queue <int>(); //Sort for shells and holes foreach (var cs in cses) { var ring = _factory.CreateLinearRing(cs); if (!ring.IsCCW) { shellRings.Add(ring); numHoleRings.Enqueue(holeRings.Count); } else { holeRings.Add(ring); } } numHoleRings.Enqueue(holeRings.Count); if (shellRings.Count == 1) { return(_factory.CreatePolygon(shellRings[0], holeRings.ToArray())); } var polygons = new IPolygon[shellRings.Count]; var offset = numHoleRings.Dequeue(); for (int i = 0; i < shellRings.Count; i++) { var shellRing = shellRings[i]; var numHoles = numHoleRings.Dequeue(); var holes = holeRings.GetRange(offset, numHoles - offset).ToArray(); polygons[i] = _factory.CreatePolygon(shellRing, holes); } return(_factory.CreateMultiPolygon(polygons)); }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivent geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="factory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, int totalRecordLength, IGeometryFactory factory) { int totalRead = 0; var type = (ShapeGeometryType)ReadInt32(file, totalRecordLength, ref totalRead); if (type == ShapeGeometryType.NullShape) return factory.CreatePolygon(null, null); if (type != ShapeType) throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); // Read and for now ignore bounds. var bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) boundingBox[boundingBoxIndex] = ReadDouble(file, totalRecordLength, ref totalRead); var numParts = ReadInt32(file, totalRecordLength, ref totalRead); var numPoints = ReadInt32(file, totalRecordLength, ref totalRead); var partOffsets = new int[numParts]; for (var i = 0; i < numParts; i++) partOffsets[i] = ReadInt32(file, totalRecordLength, ref totalRead); var skippedList = new HashSet<int>(); //var allPoints = new List<Coordinate>(); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var pm = factory.PrecisionModel; for (var part = 0; part < numParts; part++) { var start = partOffsets[part]; var finish = (part == numParts - 1) ? numPoints : partOffsets[part + 1]; var length = finish - start; for (var i = 0; i < length; i++) { var x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); var y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); // Thanks to Abhay Menon! if (!(Coordinate.NullOrdinate.Equals(x) || Coordinate.NullOrdinate.Equals(y))) buffer.AddCoordinate(x, y); else skippedList.Add(start + i); } //Add a marker that we have finished one part of the geometry buffer.AddMarker(); } // Trond Benum: We have now read all the parts, let's read optional Z and M values // and populate Z in the coordinate before we start manipulating the segments // We have to track corresponding optional M values and set them up in the // Geometries via ICoordinateSequence further down. GetZMValues(file, totalRecordLength, ref totalRead, buffer, skippedList); // Get the resulting sequences var sequences = buffer.ToSequences(factory.CoordinateSequenceFactory); var shells = new List<ILinearRing>(); var holes = new List<ILinearRing>(); for (var i = 0; i < sequences.Length; i++) { //Skip garbage input data with 0 points if (sequences[i].Count < 1) continue; var tmp = EnsureClosedSequence(sequences[i], factory.CoordinateSequenceFactory); var ring = factory.CreateLinearRing(tmp); if (ring.IsCCW) holes.Add(ring); else shells.Add(ring); } // Ensure the ring is encoded right if (shells.Count == 0 && holes.Count == 1) { shells.Add(factory.CreateLinearRing(holes[0].CoordinateSequence.Reversed())); holes.Clear(); } // Now we have lists of all shells and all holes var holesForShells = new List<List<ILinearRing>>(shells.Count); for (var i = 0; i < shells.Count; i++) holesForShells.Add(new List<ILinearRing>()); //Thanks to Bruno.Labrecque //Sort shells by area, rings should only be added to the smallest shell, that contains the ring shells.Sort(ProbeLinearRing); // Find holes foreach (var testHole in holes) { var testEnv = testHole.EnvelopeInternal; var testPt = testHole.GetCoordinateN(0); //We have the shells sorted for (var j = 0; j < shells.Count; j++) { var tryShell = shells[j]; var tryEnv = tryShell.EnvelopeInternal; var isContained = tryEnv.Contains(testEnv) && CGAlgorithms.IsPointInRing(testPt, tryShell.Coordinates); // Check if this new containing ring is smaller than the current minimum ring if (isContained) { // Suggested by Brian Macomber and added 3/28/2006: // holes were being found but never added to the holesForShells array // so when converted to geometry by the factory, the inner rings were never created. var holesForThisShell = holesForShells[j]; holesForThisShell.Add(testHole); //Suggested by Bruno.Labrecque //A LinearRing should only be added to one outer shell break; } } } var polygons = new IPolygon[shells.Count]; for (var i = 0; i < shells.Count; i++) polygons[i] = (factory.CreatePolygon(shells[i], holesForShells[i].ToArray())); if (polygons.Length == 1) geom = polygons[0]; else geom = factory.CreateMultiPolygon(polygons); return geom; }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivent geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="factory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override Geometry Read(BigEndianBinaryReader file, int totalRecordLength, GeometryFactory factory) { int totalRead = 0; var type = (ShapeGeometryType)ReadInt32(file, totalRecordLength, ref totalRead); if (type == ShapeGeometryType.NullShape) { return(factory.CreateMultiLineString(null)); } if (type != ShapeType) { throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); } // Read and for now ignore bounds. int bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) { double d = ReadDouble(file, totalRecordLength, ref totalRead); boundingBox[boundingBoxIndex] = d; } int numParts = ReadInt32(file, totalRecordLength, ref totalRead); int numPoints = ReadInt32(file, totalRecordLength, ref totalRead); int[] partOffsets = new int[numParts]; for (int i = 0; i < numParts; i++) { partOffsets[i] = ReadInt32(file, totalRecordLength, ref totalRead); } var lines = new List <LineString>(numParts); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var pm = factory.PrecisionModel; for (int part = 0; part < numParts; part++) { int start = partOffsets[part]; int finish = part == numParts - 1 ? numPoints : partOffsets[part + 1]; int length = finish - start; for (int i = 0; i < length; i++) { double x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); double y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); buffer.AddCoordinate(x, y); } buffer.AddMarker(); } // Trond Benum: We have now read all the parts, let's read optional Z and M values // and populate Z in the coordinate before we start manipulating the segments // We have to track corresponding optional M values and set them up in the // Geometries via CoordinateSequence further down. GetZMValues(file, totalRecordLength, ref totalRead, buffer); var sequences = new List <CoordinateSequence>(buffer.ToSequences(factory.CoordinateSequenceFactory)); for (int s = 0; s < sequences.Count; s++) { var points = sequences[s]; //Skip garbage input data with 0 points if (points.Count < 1) { continue; } bool createLineString = true; if (points.Count == 1) { switch (GeometryInstantiationErrorHandling) { case GeometryInstantiationErrorHandlingOption.ThrowException: break; case GeometryInstantiationErrorHandlingOption.Empty: sequences[s] = factory.CoordinateSequenceFactory.Create(0, points.Ordinates); break; case GeometryInstantiationErrorHandlingOption.TryFix: sequences[s] = AddCoordinateToSequence(points, factory.CoordinateSequenceFactory, points.GetX(0), points.GetY(0), points.GetZ(0), points.GetM(0)); break; case GeometryInstantiationErrorHandlingOption.Null: createLineString = false; break; } } if (createLineString) { // Grabs m values if we have them var line = factory.CreateLineString(points); lines.Add(line); } } geom = (lines.Count != 1) ? (Geometry)factory.CreateMultiLineString(lines.ToArray()) : lines[0]; return(geom); }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivent geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="geometryFactory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, int totalRecordLength, IGeometryFactory geometryFactory) { int totalRead = 0; var type = (ShapeGeometryType)ReadInt32(file, totalRecordLength, ref totalRead); if (type == ShapeGeometryType.NullShape) { return(geometryFactory.CreatePolygon(null, null)); } if (type != ShapeType) { throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); } // Read and for now ignore bounds. var bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) { boundingBox[boundingBoxIndex] = ReadDouble(file, totalRecordLength, ref totalRead); } var numParts = ReadInt32(file, totalRecordLength, ref totalRead); var numPoints = ReadInt32(file, totalRecordLength, ref totalRead); var partOffsets = new int[numParts]; for (var i = 0; i < numParts; i++) { partOffsets[i] = ReadInt32(file, totalRecordLength, ref totalRead); } var skippedList = new HS(); //var allPoints = new List<Coordinate>(); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var pm = geometryFactory.PrecisionModel; for (var part = 0; part < numParts; part++) { var start = partOffsets[part]; var finish = (part == numParts - 1) ? numPoints : partOffsets[part + 1]; var length = finish - start; for (var i = 0; i < length; i++) { var x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); var y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); // Thanks to Abhay Menon! if (!(Coordinate.NullOrdinate.Equals(x) || Coordinate.NullOrdinate.Equals(y))) { buffer.AddCoordinate(x, y); } else { skippedList.Add(start + i); } } //Add a marker that we have finished one part of the geometry buffer.AddMarker(); } // Trond Benum: We have now read all the parts, let's read optional Z and M values // and populate Z in the coordinate before we start manipulating the segments // We have to track corresponding optional M values and set them up in the // Geometries via ICoordinateSequence further down. GetZMValues(file, totalRecordLength, ref totalRead, buffer, skippedList); // Get the resulting sequences var sequences = buffer.ToSequences(geometryFactory.CoordinateSequenceFactory); var shells = new List <ILinearRing>(); var holes = new List <ILinearRing>(); for (var i = 0; i < sequences.Length; i++) { var tmp = EnsureClosedSequence(sequences[i], geometryFactory.CoordinateSequenceFactory); var ring = geometryFactory.CreateLinearRing(tmp); if (ring.IsCCW) { holes.Add(ring); } else { shells.Add(ring); } } // Ensure the ring is encoded right if (shells.Count == 0 && holes.Count == 1) { shells.Add(geometryFactory.CreateLinearRing(holes[0].CoordinateSequence.Reversed())); holes.Clear(); } // Now we have lists of all shells and all holes var holesForShells = new List <List <ILinearRing> >(shells.Count); for (var i = 0; i < shells.Count; i++) { holesForShells.Add(new List <ILinearRing>()); } //Thanks to Bruno.Labrecque //Sort shells by area, rings should only be added to the smallest shell, that contains the ring shells.Sort(ProbeLinearRing); // Find holes foreach (var testHole in holes) { var testEnv = testHole.EnvelopeInternal; var testPt = testHole.GetCoordinateN(0); //We have the shells sorted for (var j = 0; j < shells.Count; j++) { var tryShell = shells[j]; var tryEnv = tryShell.EnvelopeInternal; var isContained = tryEnv.Contains(testEnv) && CGAlgorithms.IsPointInRing(testPt, tryShell.Coordinates); // Check if this new containing ring is smaller than the current minimum ring if (isContained) { // Suggested by Brian Macomber and added 3/28/2006: // holes were being found but never added to the holesForShells array // so when converted to geometry by the factory, the inner rings were never created. var holesForThisShell = holesForShells[j]; holesForThisShell.Add(testHole); //Suggested by Bruno.Labrecque //A LinearRing should only be added to one outer shell break; } } } var polygons = new IPolygon[shells.Count]; for (var i = 0; i < shells.Count; i++) { polygons[i] = (geometryFactory.CreatePolygon(shells[i], holesForShells[i].ToArray())); } if (polygons.Length == 1) { geom = polygons[0]; } else { geom = geometryFactory.CreateMultiPolygon(polygons); } return(geom); }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivent geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="geometryFactory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, int totalRecordLength, IGeometryFactory geometryFactory) { int totalRead = 0; var type = (ShapeGeometryType)ReadInt32(file, totalRecordLength, ref totalRead); if (type == ShapeGeometryType.NullShape) return geometryFactory.CreateMultiLineString(null); if (type != ShapeType) throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); // Read and for now ignore bounds. int bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) { double d = ReadDouble(file, totalRecordLength, ref totalRead); boundingBox[boundingBoxIndex] = d; } int numParts = ReadInt32(file, totalRecordLength, ref totalRead); int numPoints = ReadInt32(file, totalRecordLength, ref totalRead); int[] partOffsets = new int[numParts]; for (int i = 0; i < numParts; i++) partOffsets[i] = ReadInt32(file, totalRecordLength, ref totalRead); var lines = new List<ILineString>(numParts); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var pm = geometryFactory.PrecisionModel; for (var part = 0; part < numParts; part++) { var start = partOffsets[part]; var finish = part == numParts - 1 ? numPoints : partOffsets[part + 1]; var length = finish - start; for (var i = 0; i < length; i++) { var x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); var y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); buffer.AddCoordinate(x, y); } buffer.AddMarker(); } // Trond Benum: We have now read all the parts, let's read optional Z and M values // and populate Z in the coordinate before we start manipulating the segments // We have to track corresponding optional M values and set them up in the // Geometries via ICoordinateSequence further down. GetZMValues(file, totalRecordLength, ref totalRead, buffer); var sequences = new List<ICoordinateSequence>(buffer.ToSequences(geometryFactory.CoordinateSequenceFactory)); for (var s = 0; s < sequences.Count; s++) { var points = sequences[s]; //Skip garbage input data with 0 points if (points.Count < 1) continue; var createLineString = true; if (points.Count == 1) { switch (GeometryInstantiationErrorHandling) { case GeometryInstantiationErrorHandlingOption.ThrowException: break; case GeometryInstantiationErrorHandlingOption.Empty: sequences[s] = geometryFactory.CoordinateSequenceFactory.Create(0, points.Ordinates); break; case GeometryInstantiationErrorHandlingOption.TryFix: sequences[s] = AddCoordinateToSequence(points, geometryFactory.CoordinateSequenceFactory, points.GetOrdinate(0, Ordinate.X), points.GetOrdinate(0, Ordinate.Y), points.GetOrdinate(0, Ordinate.Z), points.GetOrdinate(0, Ordinate.M)); break; case GeometryInstantiationErrorHandlingOption.Null: createLineString = false; break; } } if (createLineString) { // Grabs m values if we have them var line = geometryFactory.CreateLineString(points); lines.Add(line); } } geom = (lines.Count != 1) ? (IGeometry)geometryFactory.CreateMultiLineString(lines.ToArray()) : lines[0]; return geom; }
public void TestAddMarkers() { var cb = new CoordinateBuffer(10); for (var i = 0; i < 10; i++) { if (i > 0 && i % 5 == 0) cb.AddMarker(); cb.AddCoordinate(i, i); } //cb.AddMarker(); var seqs = cb.ToSequences(); Assert.AreEqual(2, seqs.Length); Assert.AreEqual(5, seqs[0].Count); Assert.AreEqual(5, seqs[1].Count); }
/// <summary> /// Creates a single Polygon with holes. /// </summary> /// <param name="buffer"></param> /// <returns></returns> private IGeometry CreateSingleOrMultiPolygon(CoordinateBuffer buffer) { // Support vars var cses = buffer.ToSequences(); var shellRings = new List<ILinearRing>(); var holeRings = new List<ILinearRing>(); var numHoleRings = new Queue<int>(); //Sort for shells and holes foreach (var cs in cses) { var ring = _factory.CreateLinearRing(cs); if (!ring.IsCCW) { shellRings.Add(ring); numHoleRings.Enqueue(holeRings.Count); } else holeRings.Add(ring); } numHoleRings.Enqueue(holeRings.Count); if (shellRings.Count == 1) return _factory.CreatePolygon(shellRings[0], holeRings.ToArray()); var polygons = new IPolygon[shellRings.Count]; var offset = numHoleRings.Dequeue(); for (int i = 0; i < shellRings.Count; i++) { var shellRing = shellRings[i]; var numHoles = numHoleRings.Dequeue(); var holes = holeRings.GetRange(offset, numHoles - offset).ToArray(); polygons[i] = _factory.CreatePolygon(shellRing, holes); } return _factory.CreateMultiPolygon(polygons); }