Open EdoardoTona opened 2 years ago
The issue arises because WKTReader
has IsOldNtsCoordinateSyntaxAllowed
set to true
by default.
To prevent that use a WKTWriter
instantiated like this:
var reader new WKTReader() { IsOldNtsCoordinateSyntaxAllowed = false };
see also NTS Issue #567
@FObermaier do you think Npgsql needs to be changed in any way here?
@roji , a hint to the documentation on how to use the WKTReader
in scenarios involving the PostGisReader
should be sufficient.
We should consider making IsOldNtsCoordinateSyntaxAllowed = false
the default on NTS' side.
Given a Geometry
type is there a workaround for this?
The workaround is to prevent creating the Geometry
with a dimension of 3 when there are only 2 in the first place.
If the creation of the geometry involves WKTReader
you should instantiate that using the following code:
var wktRdr = new NetTopologySuite.IO.WKTReader(/* possible NetTopologySuite.NtsGeometryServices instance */) {
IsOldNtsCoordinateSyntaxAllowed = false
};
@FObermaier that works on the reader. But subsequently manipulating the geometry, if using any of the NTS geometry calculations, Buffer
, Reduce
will set the HasZ
on the CoordinateSequence back to true. Should I configure anything on NTS side?
@Swoorup can you add a testcase (I mean, a complete example) that reproduces the error?
@DGuidi I use other operations as well but even a buffer(0.0) causes an issue.
let rec hasZ (p: Geometry) =
match p with
| :? Point as pp -> pp.CoordinateSequence.HasZ
| :? Polygon as pp -> pp.ExteriorRing.CoordinateSequence.HasZ
| :? LineString as ls -> ls.CoordinateSequence.HasZ
| :? GeometryCollection as gc -> hasZ (gc.GetGeometryN(0))
| _ -> failwith "Unexpected"
let geom = WKTReader(IsOldNtsCoordinateSyntaxAllowed = false).Read("POINT (0 0)")
let fixed = geom.Buffer(0.0) // removing this line makes `hasZ` false
printfn $"%A{hasZ fixed}"
HasZ is true
for empty geometries, otherwise it works as expected
var reader = new WKTReader()
{
IsOldNtsCoordinateSyntaxAllowed = false
};
var geom = (Point)reader.Read("POINT (0 0)");
Assert.IsFalse(geom.CoordinateSequence.HasZ);
var empty = (Polygon)geom.Buffer(0.0);
Assert.IsTrue(empty.IsEmpty);
Assert.IsTrue(empty.ExteriorRing.CoordinateSequence.HasZ);
var poly = (Polygon)geom.Buffer(10.0);
Assert.IsFalse(poly.IsEmpty);
Assert.IsFalse(poly.ExteriorRing.CoordinateSequence.HasZ);
@DGuidi It fails for this wkt:
Another operation that causes it to have Z dimension (just NaNs):
let fixed = GeometrySnapper.SnapToSelf(geometry, 0.0002, true)
nope, it works. at least with latest main.
var reader = new WKTReader()
{
IsOldNtsCoordinateSyntaxAllowed = false
};
const string wkt = "MULTIPOLYGON(((138.4012 -35.638,138.3996 -35.6404,138.3802 -35.6389,138.3732 -35.6397,138.3678 -35.6423,138.3547 -35.6398,138.2847 -35.6389,138.2651 -35.6413,138.258 -35.6482,138.254 -35.6503,138.2529 -35.6493,138.2474 -35.6539,138.2448 -35.6532,138.2434 -35.6554,138.2403 -35.6545,138.232 -35.6597,138.227 -35.6607,138.2264 -35.6594,138.2208 -35.6624,138.2159 -35.661,138.2066 -35.664,138.2004 -35.6631,138.1948 -35.6653,138.1928 -35.6642,138.185 -35.6649,138.1793 -35.6625,138.1767 -35.6638,138.1714 -35.6607,138.1626 -35.6608,138.1562 -35.6582,138.1549 -35.6544,138.149 -35.655,138.1439 -35.6526,138.1424 -35.6494,138.1356 -35.6472,138.1358 -35.6453,138.1309 -35.6463,138.1274 -35.6445,138.1251 -35.6409,138.1179 -35.6377,138.1147 -35.6328,138.1065 -35.633,138.1 -35.6296,138.0949 -35.6238,138.0957 -35.6162,138.0946 -35.6155,138.0958 -35.6138,138.0935 -35.6024,138.0952 -35.5997,138.1055 -35.5949,138.1115 -35.5872,138.1147 -35.5816,138.1141 -35.5783,138.1199 -35.5693,138.1196 -35.5667,138.1212 -35.5659,138.1226 -35.5611,138.1271 -35.5569,138.1263 -35.5558,138.1505 -35.5364,138.151 -35.5331,138.1578 -35.5302,138.1639 -35.5234,138.165 -35.5197,138.1686 -35.5183,138.1769 -35.5196,138.1884 -35.524,138.2121 -35.5134,138.2149 -35.5111,138.2143 -35.5099,138.218 -35.5113,138.2241 -35.5098,138.2378 -35.5021,138.2442 -35.5013,138.2484 -35.4963,138.2604 -35.4904,138.2766 -35.4782,138.2754 -35.4817,138.2806 -35.4739,138.2919 -35.47,138.29 -35.4695,138.2941 -35.4674,138.295 -35.4641,138.2975 -35.4625,138.3034 -35.4503,138.3185 -35.4287,138.3229 -35.4194,138.3257 -35.4173,138.3279 -35.4092,138.3414 -35.3928,138.3591 -35.3827,138.3624 -35.3781,138.3658 -35.378,138.3809 -35.371,138.3861 -35.3723,138.3932 -35.3677,138.4103 -35.3661,138.4305 -35.3533,138.4398 -35.3513,138.4424 -35.3452,138.4409 -35.3435,138.4458 -35.3368,138.447 -35.3193,138.4437 -35.2903,138.4416 -35.2849,138.4409 -35.2858,138.4391 -35.284,138.4401 -35.2827,138.4419 -35.2834,138.4423 -35.2812,138.4393 -35.2764,138.4401 -35.2691,138.4428 -35.2677,138.4521 -35.2678,138.4603 -35.2602,138.4609 -35.2478,138.4817 -35.2463,138.4609 -35.2471,138.4611 -35.245,138.4672 -35.2449,138.4695 -35.2406,138.4692 -35.2283,138.4672 -35.2215,138.4797 -35.2203,138.4677 -35.2211,138.4668 -35.2185,138.4705 -35.2118,138.4688 -35.194,138.4903 -35.1907,138.5158 -35.1894,138.5147 -35.1748,138.5204 -35.1732,138.52 -35.1688,138.5301 -35.1683,138.5296 -35.1654,138.5345 -35.1636,138.5356 -35.1612,138.5477 -35.1641,138.5522 -35.1588,138.5571 -35.1615,138.5651 -35.1615,138.5661 -35.1585,138.5727 -35.1574,138.5794 -35.1606,138.5797 -35.1559,138.5824 -35.1559,138.5834 -35.152,138.5868 -35.1524,138.5886 -35.1498,138.586 -35.1479,138.5866 -35.1466,138.5957 -35.1451,138.6026 -35.15,138.6056 -35.148,138.6115 -35.1501,138.6182 -35.1577,138.6234 -35.1585,138.6264 -35.1619,138.6319 -35.1638,138.6295 -35.1649,138.6264 -35.1703,138.6175 -35.1763,138.6177 -35.1794,138.6243 -35.1837,138.6342 -35.1832,138.6379 -35.1758,138.6379 -35.1664,138.7188 -35.1664,138.7188 -35.2414,138.7218 -35.2414,138.7206 -35.2427,138.7272 -35.2465,138.7249 -35.2529,138.7221 -35.2542,138.7486 -35.2564,138.7491 -35.253,138.7438 -35.2516,138.7451 -35.2511,138.7459 -35.2453,138.7462 -35.2298,138.7516 -35.2224,138.7567 -35.2203,138.7536 -35.2136,138.7581 -35.2107,138.7659 -35.2114,138.7662 -35.209,138.7754 -35.2099,138.774 -35.2197,138.7723 -35.2222,138.7738 -35.2214,138.7772 -35.2263,138.7851 -35.2286,138.7814 -35.2356,138.7733 -35.2442,138.7728 -35.2471,138.7817 -35.248,138.7836 -35.2504,138.7891 -35.2512,138.7917 -35.2494,138.7936 -35.2526,138.7989 -35.253,138.7972 -35.2505,138.8069 -35.246,138.7997 -35.2454,138.8008 -35.2366,138.8071 -35.239,138.8079 -35.2326,138.812 -35.2297,138.8213 -35.2278,138.8301 -35.2286,138.84 -35.2176,138.8426 -35.2178,138.8401 -35.2146,138.8411 -35.2068,138.8471 -35.2069,138.8481 -35.2042,138.8568 -35.2052,138.8607 -35.2023,138.8596 -35.2016,138.8652 -35.1953,138.8677 -35.1967,138.8692 -35.192,138.872 -35.1899,138.8757 -35.1918,138.8774 -35.1969,138.8842 -35.1938,138.8872 -35.1983,138.8917 -35.1963,138.892 -35.2023,138.8981 -35.1966,138.9035 -35.1942,138.9104 -35.1936,138.9127 -35.2049,138.9201 -35.2086,138.9374 -35.2354,138.9385 -35.2348,138.9433 -35.2411,138.9505 -35.2352,138.9552 -35.2403,138.9892 -35.2262,138.9891 -35.228,138.9919 -35.2304,138.9911 -35.2314,138.9972 -35.2292,139.0035 -35.2412,139.0153 -35.237,139.0232 -35.2523,139.0368 -35.2474,139.0422 -35.2576,139.0947 -35.2575,139.0962 -35.237,139.1115 -35.2341,139.1583 -35.2535,139.1552 -35.3029,139.1521 -35.3039,139.1551 -35.3045,139.1522 -35.353,139.161 -35.3559,139.1583 -35.3578,139.1564 -35.3561,139.1579 -35.3605,139.1551 -35.3635,139.1565 -35.3637,139.1521 -35.366,139.1508 -35.3736,139.1567 -35.3729,139.1436 -35.3763,139.1354 -35.3753,139.1315 -35.3766,139.1349 -35.3765,139.1323 -35.3774,139.1297 -35.3767,139.1098 -35.3821,139.1069 -35.3815,139.1081 -35.382,139.1007 -35.3831,139.1022 -35.3837,139.1012 -35.3849,139.0925 -35.3887,139.0954 -35.387,139.0913 -35.3859,139.089 -35.3884,139.0842 -35.3877,139.0839 -35.3891,139.0765 -35.391,139.0613 -35.3901,139.0356 -35.3917,139.0345 -35.3912,139.0396 -35.3897,139.0285 -35.3906,139.0197 -35.3924,139.0184 -35.3954,139.0132 -35.397,139.0091 -35.3974,139.0081 -35.3952,139.006 -35.3954,139.0007 -35.3987,138.9986 -35.3976,138.993 -35.3985,138.9929 -35.3996,138.9904 -35.3993,138.9914 -35.4004,138.9845 -35.4014,138.9768 -35.4054,138.9773 -35.4067,138.972 -35.4114,138.9732 -35.4143,138.9701 -35.4174,138.9707 -35.4201,138.9662 -35.426,138.9663 -35.4308,138.9655 -35.4285,138.9604 -35.4358,138.9596 -35.4413,138.9575 -35.4423,138.9587 -35.4432,138.9561 -35.444,138.9514 -35.4501,138.9518 -35.4529,138.9547 -35.4556,138.9647 -35.4541,138.9577 -35.4567,138.9549 -35.4606,138.9552 -35.4655,138.9584 -35.4677,138.9578 -35.4735,138.9734 -35.4813,138.9748 -35.4836,138.9809 -35.4819,138.9824 -35.4856,138.9881 -35.4901,138.9991 -35.4885,139.0058 -35.4852,139.0117 -35.4874,139.0121 -35.4898,139.015 -35.492,139.0208 -35.4928,139.0258 -35.4955,139.0275 -35.4944,139.0315 -35.4962,139.0389 -35.4961,139.0476 -35.5018,139.0319 -35.5138,139.0229 -35.5131,139.0163 -35.5155,139.0032 -35.5137,138.9913 -35.5166,138.9892 -35.52,138.981 -35.5182,138.966 -35.5288,138.9663 -35.5413,138.963 -35.5454,138.943 -35.5558,138.9351 -35.5558,138.9315 -35.5624,138.9261 -35.5589,138.9177 -35.5612,138.9077 -35.5564,138.9 -35.5552,138.8989 -35.5507,138.8941 -35.5494,138.8883 -35.5522,138.8881 -35.5554,138.8751 -35.5578,138.8246 -35.5395,138.7731 -35.5242,138.7461 -35.5182,138.7099 -35.5141,138.7005 -35.5182,138.6931 -35.5235,138.6926 -35.5265,138.6957 -35.5272,138.6954 -35.5289,138.6904 -35.5339,138.6883 -35.5327,138.6851 -35.5337,138.6843 -35.5383,138.6789 -35.5367,138.6635 -35.5366,138.6269 -35.5443,138.6323 -35.5443,138.6256 -35.5508,138.6263 -35.5583,138.6192 -35.5579,138.6132 -35.5621,138.6133 -35.5656,138.6097 -35.5702,138.5983 -35.581,138.5993 -35.5826,138.598 -35.5855,138.5992 -35.5845,138.6027 -35.5888,138.6056 -35.5889,138.6057 -35.5927,138.6026 -35.5948,138.5998 -35.5925,138.5844 -35.5993,138.582 -35.6015,138.5822 -35.6055,138.5747 -35.606,138.5651 -35.609,138.5593 -35.6137,138.5556 -35.6193,138.5222 -35.6431,138.5078 -35.637,138.5061 -35.6379,138.5046 -35.6361,138.4945 -35.634,138.4926 -35.6349,138.4924 -35.6334,138.4868 -35.6342,138.4874 -35.6328,138.4834 -35.6334,138.483 -35.6322,138.4792 -35.6348,138.4628 -35.6302,138.4566 -35.6326,138.4491 -35.63,138.4399 -35.631,138.4312 -35.6292,138.421 -35.6335,138.4125 -35.6399,138.4037 -35.6403,138.4012 -35.638)),((138.4903 -35.1907,138.4688 -35.194,138.4665 -35.188,138.4676 -35.1875,138.4649 -35.1846,138.4665 -35.1844,138.467 -35.1821,138.4659 -35.1777,138.4764 -35.1773,138.4766 -35.1809,138.4895 -35.1804,138.4903 -35.1907)))";
var geom = (Polygon)reader.Read(wkt).GetGeometryN(0);
Assert.IsFalse(geom.ExteriorRing.CoordinateSequence.HasZ);
var buff0 = (Polygon)geom.Buffer(0.0);
Assert.IsFalse(buff0.IsEmpty);
Assert.IsFalse(buff0.ExteriorRing.CoordinateSequence.HasZ);
var buff10 = (Polygon)geom.Buffer(10.0);
Assert.IsFalse(buff10.IsEmpty);
Assert.IsFalse(buff10.ExteriorRing.CoordinateSequence.HasZ);
Ahh, I forgot about that....
As a workaround (namespace imports are missing):
use sth. like (namespace imports are missing)
public class ChangeDimensionUtility
{
private static CoordinateSequence ChangeToXY(CoordinateSequence sequence, Geometry geometry)
{
if ((sequence?.Dimension ?? 2) == 2)
return sequence;
var res = geometry.Factory.CoordinateSequenceFactory.Create(sequence.Count, 2, 0);
CoordinateSequences.Copy(sequence, 0, res, 0, sequence.Count);
return res;
}
private static CoordinateSequence ChangeToXYZ(CoordinateSequence sequence, Geometry geometry)
{
if ((sequence?.Dimension ?? 3) == 3 && (sequence?.Measures ?? 0) == 0)
return sequence;
var res = geometry.Factory.CoordinateSequenceFactory.Create(sequence.Count, 3, 0);
CoordinateSequences.Copy(sequence, 0, res, 0, sequence.Count);
return res;
}
private static CoordinateSequence ChangeToXYM(CoordinateSequence sequence, Geometry geometry)
{
if ((sequence?.Dimension ?? 3) == 3 && (sequence?.Measures ?? 1) == 1)
return sequence;
var res = geometry.Factory.CoordinateSequenceFactory.Create(sequence.Count, 3, 1);
CoordinateSequences.Copy(sequence, 0, res, 0, sequence.Count);
return res;
}
public static CoordinateSequence ChangeToXYZM(CoordinateSequence sequence, Geometry geometry)
{
if ((sequence?.Dimension ?? 4) == 4 && (sequence?.Measures ?? 1) == 1)
return sequence;
var res = geometry.Factory.CoordinateSequenceFactory.Create(sequence.Count, 4, 1);
CoordinateSequences.Copy(sequence, 0, res, 0, sequence.Count);
return res;
}
public static T ChangeDimension<T>(T geometry, Ordinates ordinates) where T: Geometry
{
if (geometry == null)
return null;
Func<CoordinateSequence, Geometry, CoordinateSequence> fn = null;
switch (ordinates)
{
case Ordinates.XY:
fn = ChangeToXY;
break;
case Ordinates.XYZ:
fn = ChangeToXYZ;
break;
case Ordinates.XYM:
fn = ChangeToXYM;
break;
case Ordinates.XYZM:
fn = ChangeToXYZM;
break;
default:
throw new ArgumentOutOfRangeException(nameof(ordinates));
}
var ge = new GeometryEditor(geometry.Factory) { CopyUserData = true };
var op = new GeometryEditor.CoordinateSequenceOperation(fn);
return (T)ge.Edit(geometry, op);
}
}
Ahh, I forgot about that....
As a workaround (namespace imports are missing):
Wonder if the easiest fix in the future would be to revert those lines, i.e also check for NaN in the PostGisWriter?
I believe that I'm seeing the same behavior, Creating a polygon like this:
var coords = (new Coordinate[]
{
new Coordinate(0, 0),
new Coordinate(80.283745, 0),
new Coordinate(80.283745, 80.283745),
new Coordinate(50.5678, 80.283745),
new Coordinate(0, 0)
});
var poly = gf.CreatePolygon(coords);
Console.WriteLine(poly.ToString());
Outputs the following:
POLYGON((0 0,80.283745 0,80.283745 80.283745,50.5678 80.283745,0 0))
But subsequently INSERTING it into a database creates a column with the following:
POLYGON M ((0 0 NaN,80.283745 0 NaN,80.283745 80.283745 NaN,50.5678 80.283745 NaN,0 0 NaN))
I've worked around it by doing the following:
var coords = gf.CoordinateSequenceFactory.Create(5, 2, 0);
coords.SetX(0, 0);
coords.SetY(0, 0);
coords.SetX(1, 80.283745);
coords.SetY(1, 0);
coords.SetX(2, 80.283745);
coords.SetY(2, 80.283745);
coords.SetX(3, 50.5678);
coords.SetY(3, 80.283745);
coords.SetX(4, 0);
coords.SetY(4, 0);
var poly = gf.CreatePolygon(coords);
but that seems very awkward.
This is with version 7.0.6. Any chance on this being addressed?
As workaround I forced the usage of NetTopologySuite.IO.PostGis 2.0.0
<PackageReference Include="NetTopologySuite.IO.PostGis" Version="2.0.0">
<NoWarn>NU1605</NoWarn>
</PackageReference>
@FObermaier I'm one of the Npgsql maintainers. We're using PostGisReader and PostGisWriter to implement our serialization. How would we prevent these NaNs from ending up in the database?
@NinoFloris are you able to set PostGisWriter
's HandleOrdinates
property according to the geometry type in that table?
I'm not able to reproduce any of the alledgedly not working examples in this thread. Before I dig any further into this I'd like to see a PR with a unit test that fails.
At most we know the datatypename that a user wants to send a db parameter as.
We do expose some config options for the NTS plugin https://github.com/npgsql/npgsql/blob/e8f20a0f74c26dd59b2e02e2bf1889afc231246d/src/Npgsql.NetTopologySuite/NpgsqlNetTopologySuiteExtensions.cs#L23. It seems like that might be too coarse if users have multiple columns with different needs though.
Hi, the version 2.1.0 introduced this issue: all XY geometries are stored in postgis as XYZ geometries, with NaN value in Z.
Example: I store
POINT(0 0)
using Npgsql and NTS. Using NetTopologySuite.IO.PostGis v2.0.0, postgis'st_ndims
returns:2
andst_astext
returns:POINT (0 0)
Using NetTopologySuite.IO.PostGis v2.1.0, postgis'st_ndims
returns:3
andst_astext
returns:POINT Z (0 0 -1.#IND)
-1.#IND
is aNaN
representation in Windows https://stackoverflow.com/a/347940.I'm using:
Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite
5.0.7 (and later)PostgreSQL 11.11, compiled by Visual C++ build 1800, 64-bit
managed in Azure as Single ServerPOSTGIS="2.5.1 r17027" [EXTENSION] PGSQL="110" GEOS="3.7.0-CAPI-1.11.0 3.7.1" PROJ="Rel. 4.9.3, 15 August 2016" GDAL="GDAL 2.2.4, released 2018/03/19 GDAL_DATA not found" LIBXML="2.7.8" LIBJSON="0.12" LIBPROTOBUF="1.2.1" RASTER
How to reproduce the issue:
The above code works in net6.0 with
Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite
5.0.5 (usingNetTopologySuite.IO.PostGis
2.0.0). Using the next version 5.0.7 (usingNetTopologySuite.IO.PostGis
2.1.0) it throws Npgsql.PostgresException with the message23514: new row for relation "test" violates check constraint "enforce_dims"
.The PostGisReader is smart enougth to see the Z coordinate is NaN, returning a 2 dimensional coordinate.
I think the issue has been introduced with the following commit:
https://github.com/NetTopologySuite/NetTopologySuite.IO.PostGis/commit/ddd394c13ee4765bb6e8d10482fdf251d5cc459f#diff-b1018343089a37cb228d0c3357039b782d592ad2591204f0d4c6d41089d1ecf8L585
Version 2.1.0 is:
if (sequence.HasZ)
version 2.0.0 was:if (sequence.HasZ && !double.IsNaN(sequence.GetZ(0)))