int determineWkbCapacity()

in src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java [1895:2107]


    int determineWkbCapacity() {
        int totalSize = 0;

        totalSize += BYTE_ORDER_SIZE; // byte order
        totalSize += INTERNAL_TYPE_SIZE; // internal type size

        switch (internalType) {
            case POINT:
                // Handle special case where POINT EMPTY
                if (numberOfPoints == 0) {
                    totalSize += NUMBER_OF_SHAPES_SIZE; // number of points
                }
                totalSize += numberOfPoints * WKB_POINT_SIZE;
                break;
            case LINESTRING:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of points
                totalSize += numberOfPoints * WKB_POINT_SIZE;
                break;
            case POLYGON:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of rings
                totalSize += figures.length * 4 + (numberOfPoints * WKB_POINT_SIZE);
                break;
            case MULTIPOINT:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of points
                totalSize += numberOfFigures * WKB_POINT_HEADER_SIZE;
                totalSize += numberOfPoints * WKB_POINT_SIZE;
                break;
            case MULTILINESTRING:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of LineStrings
                totalSize += numberOfFigures * WKB_HEADER_SIZE;
                totalSize += numberOfPoints * WKB_POINT_SIZE;
                break;
            case MULTIPOLYGON:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of polygons
                totalSize += (numberOfShapes - 1) * WKB_HEADER_SIZE;
                for (int i = 1; i < shapes.length; i++) {
                    if (i == shapes.length - 1) { // last element
                        totalSize += LINEAR_RING_HEADER_SIZE * (figures.length - shapes[i].getFigureOffset());
                    } else {
                        int nextFigureOffset = shapes[i + 1].getFigureOffset();
                        totalSize += LINEAR_RING_HEADER_SIZE * (nextFigureOffset - shapes[i].getFigureOffset());
                    }
                }
                totalSize += numberOfPoints * WKB_POINT_SIZE;
                break;
            case GEOMETRYCOLLECTION:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of shapes
                int actualNumberOfPoints = numberOfPoints;
                for (Segment s : segments) {
                    if (s.getSegmentType() == SEGMENT_FIRST_ARC || s.getSegmentType() == SEGMENT_FIRST_LINE) {
                        totalSize += WKB_HEADER_SIZE;
                        actualNumberOfPoints++;
                    }
                }
                int numberOfCompositeCurves = 0;
                for (Figure f : figures) {
                    if (f.getFiguresAttribute() == FA_COMPOSITE_CURVE) {
                        numberOfCompositeCurves++;
                    }
                }
                if (numberOfCompositeCurves > 1) {
                    actualNumberOfPoints = actualNumberOfPoints - (numberOfCompositeCurves - 1);
                }
                if (numberOfSegments > 0) {
                    actualNumberOfPoints--;
                }
                // start from 1
                for (int i = 1; i < shapes.length; i++) {
                    if (shapes[i].getOpenGISType() == InternalSpatialDatatype.POINT.getTypeCode()) {
                        /*
                         * empty points are actually considered an empty multipoint in WKB. In this case, the figure
                         * offset will be -1, so adjust accordingly.
                         */
                        if (shapes[i].getFigureOffset() == -1) {
                            totalSize += WKB_HEADER_SIZE;
                        } else {
                            totalSize += WKB_POINT_HEADER_SIZE;
                        }
                    } else if (shapes[i].getOpenGISType() == InternalSpatialDatatype.POLYGON.getTypeCode()) {
                        if (shapes[i].getFigureOffset() != -1) {
                            if (i == shapes.length - 1) { // last element
                                totalSize += LINEAR_RING_HEADER_SIZE * (figures.length - shapes[i].getFigureOffset());
                            } else {

                                /*
                                 * the logic below handles cases where we have something like:
                                 * GEOMETRYCOLLECTION(POLYGON(0 1, 0 2, 0 3), POLYGON EMPTY, POLYGON EMPTY) In this
                                 * case, in order to find out how many figures there are in the first polygon, we need
                                 * to look ahead to the next shape (POLYGON EMPTY) to find out how many figures are in
                                 * this polygon. However, if the shape is empty, the figure index is going to be -1. If
                                 * that happens, we need to keep looking ahead until we find a shape that isn't empty.
                                 * If the last shape is also empty, then we can calculate the number of figures in the
                                 * current polygon by doing (number of all the figures) - (current figure index).
                                 */

                                int figureIndexEnd = -1;
                                int localCurrentShapeIndex = i;
                                while (figureIndexEnd == -1 && localCurrentShapeIndex < shapes.length - 1) {
                                    figureIndexEnd = shapes[localCurrentShapeIndex + 1].getFigureOffset();
                                    localCurrentShapeIndex++;
                                }

                                if (figureIndexEnd == -1) {
                                    figureIndexEnd = numberOfFigures;
                                }

                                totalSize += LINEAR_RING_HEADER_SIZE * (figureIndexEnd - shapes[i].getFigureOffset());
                            }
                        }
                        totalSize += WKB_HEADER_SIZE;
                    } else if (shapes[i].getOpenGISType() == InternalSpatialDatatype.CURVEPOLYGON.getTypeCode()) {
                        if (shapes[i].getFigureOffset() != -1) {
                            if (i == shapes.length - 1) { // last element
                                totalSize += WKB_HEADER_SIZE * (figures.length - shapes[i].getFigureOffset());
                            } else {
                                int figureIndexEnd = -1;
                                int localCurrentShapeIndex = i;
                                while (figureIndexEnd == -1 && localCurrentShapeIndex < shapes.length - 1) {
                                    figureIndexEnd = shapes[localCurrentShapeIndex + 1].getFigureOffset();
                                    localCurrentShapeIndex++;
                                }

                                if (figureIndexEnd == -1) {
                                    figureIndexEnd = numberOfFigures;
                                }
                                totalSize += WKB_HEADER_SIZE * (figureIndexEnd - shapes[i].getFigureOffset());
                            }
                        }
                        totalSize += WKB_HEADER_SIZE;
                    } else {
                        totalSize += WKB_HEADER_SIZE;
                    }
                }
                totalSize += actualNumberOfPoints * WKB_POINT_SIZE;
                break;
            case CIRCULARSTRING:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of points
                totalSize += numberOfPoints * WKB_POINT_SIZE;
                break;
            case COMPOUNDCURVE:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of curves
                actualNumberOfPoints = numberOfPoints;

                /*
                 * Segments are exclusively used by COMPOUNDCURVE. However, by extension GEOMETRYCOLLECTION or
                 * CURVEPOLYGON can have them beacuse they can contain COMPOUNDCURVE inside. If a geometric shape
                 * contains any number of COMPOUNDCURVEs, we need to calculate the actual number of points involved in
                 * the geometric shape. This is because some points in the compound curve shapes are being used across
                 * two segments. For example, let's say we have a COMPOUNDCURVE(CIRCULARSTRING(1 0, 0 1, 9 6, 8 7, -1
                 * 0), CIRCULARSTRING(-1 0, 7 9, -10 2)). This compoundcurve contains a CIRCULARSTRING that spans across
                 * 5 points, and another CIRCULARSTRING that spans across 3 points. In WKB format this would be
                 * considered as 8 points. However, the existing Geometry/Geography object will have their CLR sent from
                 * SQL Server as only having 7 points. This is because compoundcurves have a rule that all the segments
                 * have to be connected to each other (if the user tried to create a compoundcurve that doesn't link
                 * from one segment to the next, it will throw an error). Therefore, in order to convert from CLR to
                 * WKB, we need to find out the "true" number of points that exist in this geometric object, and the
                 * below logic handles the calculation.
                 */

                for (Segment s : segments) {
                    if (s.getSegmentType() == SEGMENT_FIRST_ARC || s.getSegmentType() == SEGMENT_FIRST_LINE) {
                        totalSize += WKB_HEADER_SIZE;
                        actualNumberOfPoints++;
                    }
                }
                if (numberOfSegments > 0) {
                    actualNumberOfPoints--;
                }
                totalSize += actualNumberOfPoints * WKB_POINT_SIZE;
                break;
            case CURVEPOLYGON:
                totalSize += NUMBER_OF_SHAPES_SIZE; // number of shapes
                actualNumberOfPoints = numberOfPoints;
                for (Segment s : segments) {
                    if (s.getSegmentType() == SEGMENT_FIRST_ARC || s.getSegmentType() == SEGMENT_FIRST_LINE) {
                        totalSize += WKB_HEADER_SIZE;
                        actualNumberOfPoints++;
                    }
                }
                numberOfCompositeCurves = 0;

                /*
                 * Since CURVEPOLYGONs can have COMPOUNDCURVEs inside, we need to account for them as well. However,
                 * COMPOUNDCURVEs inside CURVEPOLYGONs are treated differently than when they are on their own, because
                 * each compound curve (represented by f.getFiguresAttribute() == FA_COMPOSITE_CURVE) needs to wrap
                 * around to itself, which means that for every composite curve, the actual number of pointsdecreases by
                 * one. The below logic accounts for this.
                 */

                for (Figure f : figures) {
                    totalSize += WKB_HEADER_SIZE;
                    if (f.getFiguresAttribute() == FA_COMPOSITE_CURVE) {
                        numberOfCompositeCurves++;
                    }
                }

                if (numberOfCompositeCurves > 1) {
                    actualNumberOfPoints = actualNumberOfPoints - (numberOfCompositeCurves - 1);
                }
                if (numberOfSegments > 0) {
                    actualNumberOfPoints--;
                }
                totalSize += actualNumberOfPoints * WKB_POINT_SIZE;
                break;
            case FULLGLOBE:
                totalSize = 5; // create a variable for this later?
                break;
            default:
                break;
        }

        return totalSize;
    }