Skip to content

MultiPoints are being written incorrectly (at least to support OGC rules). #22

@mxcolin

Description

@mxcolin

The format for a MultiPoint is being written using the compressed format when it's saved to the database. Take this code for example:

var c = new OracleConnection("my_cs");
c.Open();

var s = "INSERT INTO MY_TABLE(GEO_DATA) VALUES (:p_name)";

// Multi point with 2 POINTS
var mp = new MultiPoint([new Point(1, 1), new Point(2, 2)]);

var ogw = new OracleGeometryWriter();
SdoGeometry sdoGeometry = ogw.Write(mp);
sdoGeometry.Sdo_Srid = 1;

// Parameter
var param = new OracleParameter();
param.ParameterName = "p_name";
param.DbType = DbType.Object;
param.Direction = ParameterDirection.Input;
param.UdtTypeName = "MDSYS.SDO_GEOMETRY";
param.Value = sdoGeometry;

var com = new OracleCommand(s, c);
com.Parameters.Add(param);
com.ExecuteNonQuery();

This code DOES create a MultiPoint in the compressed format. If you were to run the following SQL on the resulting value:

SELECT  
(SDO_UTIL.GETNUMELEM(GEO_DATA)) NUM_GEOMETRIES, 
FROM MY_TABLE  

It will return 1, no matter how many points are added into the MultiPoint, in this case it should be 2 (at least if you are following OGC). You can see this by looking at how the ElemArray and OrdinatesArray come back from the call to Write:

| ElemArray | {double[3]} | double[]
  | [0] | 1 | double
  | [1] | 1 | double
  | [2] | 2 | double

| OrdinatesArray | {double[4]} | double[]
  | [0] | 1 | double
  | [1] | 1 | double
  | [2] | 2 | double
  | [3] | 2 | double

Here's how they should look

| ElemArray | {double[6]} | double[]
  | [0] | 1 | double
  | [1] | 1 | double
  | [2] | 1 | double
  | [3] | 3 | double
  | [4] | 1 | double
  | [5] | 1 | double

| OrdinatesArray | {double[4]} | double[]
  | [0] | 1 | double
  | [1] | 1 | double
  | [2] | 2 | double
  | [3] | 2 | double

When formatted like this (expanded format) the above call to SDO_UTIL.GETNUMELEM will return the correct number.

Rewriting ProcessMultiPoint in OracleGeometryWriter to

private int ProcessMultiPoint(MultiPoint multiPoint, List<double> elemInfoList, List<double> ordinateList, int pos)
{
    int cnt = multiPoint.NumGeometries;

    for (int i = 0; i < cnt; i++)
    {
        var point = (Point)multiPoint.GetGeometryN(i);

        elemInfoList.Add(pos);
        elemInfoList.Add((int)SdoEType.Coordinate);
        elemInfoList.Add(1);

        ordinateList.Add(point.X);
        ordinateList.Add(point.Y);

        pos += 2;
    }

    return pos;
}

Switches it to a more compatible format. Oracle does support both a compressed and an expanded format for MultiPoints and the code as written currently uses the compressed format. However, a quirk of Oracle is that the SDO_UTIL.GETNUMELEM function returns different values depending on the format used. As such it would be better to support the standard format that returns the standard results one would expect from ST_NumGeometries or at least make it configurable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions